diff --git a/CMakeLists.txt b/CMakeLists.txt index b5f9c023778e..0108f448dfb4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -480,22 +480,23 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/compiler_rt/unordtf2.zig" "${CMAKE_SOURCE_DIR}/lib/std/start.zig" "${CMAKE_SOURCE_DIR}/lib/std/std.zig" - "${CMAKE_SOURCE_DIR}/lib/std/target.zig" - "${CMAKE_SOURCE_DIR}/lib/std/target/aarch64.zig" - "${CMAKE_SOURCE_DIR}/lib/std/target/amdgpu.zig" - "${CMAKE_SOURCE_DIR}/lib/std/target/arm.zig" - "${CMAKE_SOURCE_DIR}/lib/std/target/avr.zig" - "${CMAKE_SOURCE_DIR}/lib/std/target/bpf.zig" - "${CMAKE_SOURCE_DIR}/lib/std/target/hexagon.zig" - "${CMAKE_SOURCE_DIR}/lib/std/target/mips.zig" - "${CMAKE_SOURCE_DIR}/lib/std/target/msp430.zig" - "${CMAKE_SOURCE_DIR}/lib/std/target/nvptx.zig" - "${CMAKE_SOURCE_DIR}/lib/std/target/powerpc.zig" - "${CMAKE_SOURCE_DIR}/lib/std/target/riscv.zig" - "${CMAKE_SOURCE_DIR}/lib/std/target/sparc.zig" - "${CMAKE_SOURCE_DIR}/lib/std/target/s390x.zig" - "${CMAKE_SOURCE_DIR}/lib/std/target/wasm.zig" - "${CMAKE_SOURCE_DIR}/lib/std/target/x86.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target/Query.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target/aarch64.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target/amdgpu.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target/arm.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target/avr.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target/bpf.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target/hexagon.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target/mips.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target/msp430.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target/nvptx.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target/powerpc.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target/riscv.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target/sparc.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target/s390x.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target/wasm.zig" + "${CMAKE_SOURCE_DIR}/lib/std/Target/x86.zig" "${CMAKE_SOURCE_DIR}/lib/std/Thread.zig" "${CMAKE_SOURCE_DIR}/lib/std/Thread/Futex.zig" "${CMAKE_SOURCE_DIR}/lib/std/Thread/Mutex.zig" @@ -508,7 +509,6 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/zig.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig/Ast.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig/AstRlAnnotate.zig" - "${CMAKE_SOURCE_DIR}/lib/std/zig/CrossTarget.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig" @@ -516,12 +516,12 @@ set(ZIG_STAGE2_SOURCES "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig" - "${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativeTargetInfo.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig/system/x86.zig" "${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig" "${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" @@ -810,18 +810,26 @@ endif() set(BUILD_ZIG2_ARGS "${CMAKE_SOURCE_DIR}/lib" - build-exe src/main.zig -ofmt=c -lc - -OReleaseSmall - --name zig2 -femit-bin="${ZIG2_C_SOURCE}" - --mod "build_options::${ZIG_CONFIG_ZIG_OUT}" - --mod "aro_options::src/stubs/aro_options.zig" - --mod "Builtins/Builtin.def::src/stubs/aro_builtins.zig" - --mod "Attribute/names.def::src/stubs/aro_names.zig" - --mod "Diagnostics/messages.def::src/stubs/aro_messages.zig" - --mod "aro_backend:build_options=aro_options:deps/aro/backend.zig" - --mod "aro:Builtins/Builtin.def,Attribute/names.def,Diagnostics/messages.def,build_options=aro_options,backend=aro_backend:deps/aro/aro.zig" - --deps build_options,aro + build-exe -ofmt=c -lc -OReleaseSmall + --name zig2 + -femit-bin="${ZIG2_C_SOURCE}" -target "${ZIG_HOST_TARGET_TRIPLE}" + --dep "build_options" + --dep "aro" + --mod "root" "src/main.zig" + --mod "build_options" "${ZIG_CONFIG_ZIG_OUT}" + --mod "aro_options" "src/stubs/aro_options.zig" + --mod "Builtins/Builtin.def" "src/stubs/aro_builtins.zig" + --mod "Attribute/names.def" "src/stubs/aro_names.zig" + --mod "Diagnostics/messages.def" "src/stubs/aro_messages.zig" + --dep "build_options=aro_options" + --mod "aro_backend" "deps/aro/backend.zig" + --dep "Builtins/Builtin.def" + --dep "Attribute/names.def" + --dep "Diagnostics/messages.def" + --dep "build_options=aro_options" + --dep "backend=aro_backend" + --mod "aro" "deps/aro/aro.zig" ) add_custom_command( @@ -834,12 +842,13 @@ add_custom_command( set(BUILD_COMPILER_RT_ARGS "${CMAKE_SOURCE_DIR}/lib" - build-obj lib/compiler_rt.zig -ofmt=c - -OReleaseSmall - --name compiler_rt -femit-bin="${ZIG_COMPILER_RT_C_SOURCE}" - --mod "build_options::${ZIG_CONFIG_ZIG_OUT}" - --deps build_options + build-obj -ofmt=c -OReleaseSmall + --name compiler_rt + -femit-bin="${ZIG_COMPILER_RT_C_SOURCE}" -target "${ZIG_HOST_TARGET_TRIPLE}" + --dep "build_options" + --mod "root" "lib/compiler_rt.zig" + --mod "build_options" "${ZIG_CONFIG_ZIG_OUT}" ) add_custom_command( diff --git a/bootstrap.c b/bootstrap.c index 08fff00b2d06..c6236b3d2772 100644 --- a/bootstrap.c +++ b/bootstrap.c @@ -138,18 +138,29 @@ int main(int argc, char **argv) { { const char *child_argv[] = { - "./zig1", "lib", "build-exe", "src/main.zig", + "./zig1", "lib", "build-exe", "-ofmt=c", "-lc", "-OReleaseSmall", "--name", "zig2", "-femit-bin=zig2.c", - "--mod", "build_options::config.zig", - "--mod", "aro_options::src/stubs/aro_options.zig", - "--mod", "Builtins/Builtin.def::src/stubs/aro_builtins.zig", - "--mod", "Attribute/names.def::src/stubs/aro_names.zig", - "--mod", "Diagnostics/messages.def::src/stubs/aro_messages.zig", - "--mod", "aro_backend:build_options=aro_options:deps/aro/backend.zig", - "--mod", "aro:Builtins/Builtin.def,Attribute/names.def,Diagnostics/messages.def,build_options=aro_options,backend=aro_backend:deps/aro/aro.zig", - "--deps", "build_options,aro", "-target", host_triple, + "--dep", "build_options", + "--dep", "aro", + "--mod", "root", "src/main.zig", + + "--mod", "build_options", "config.zig", + "--mod", "aro_options", "src/stubs/aro_options.zig", + "--mod", "Builtins/Builtin.def", "src/stubs/aro_builtins.zig", + "--mod", "Attribute/names.def", "src/stubs/aro_names.zig", + "--mod", "Diagnostics/messages.def", "src/stubs/aro_messages.zig", + + "--dep", "build_options=aro_options", + "--mod", "aro_backend", "deps/aro/backend.zig", + + "--dep", "Builtins/Builtin.def", + "--dep", "Attribute/names.def", + "--dep", "Diagnostics/messages.def", + "--dep", "build_options=aro_options", + "--dep", "backend=aro_backend", + "--mod", "aro", "deps/aro/aro.zig", NULL, }; print_and_run(child_argv); @@ -157,12 +168,13 @@ int main(int argc, char **argv) { { const char *child_argv[] = { - "./zig1", "lib", "build-obj", "lib/compiler_rt.zig", + "./zig1", "lib", "build-obj", "-ofmt=c", "-OReleaseSmall", "--name", "compiler_rt", "-femit-bin=compiler_rt.c", - "--mod", "build_options::config.zig", - "--deps", "build_options", "-target", host_triple, + "--dep", "build_options", + "--mod", "root", "lib/compiler_rt.zig", + "--mod", "build_options", "config.zig", NULL, }; print_and_run(child_argv); diff --git a/build.zig b/build.zig index 11b3006039b7..d24b51741aca 100644 --- a/build.zig +++ b/build.zig @@ -39,10 +39,10 @@ pub fn build(b: *std.Build) !void { const docgen_exe = b.addExecutable(.{ .name = "docgen", .root_source_file = .{ .path = "tools/docgen.zig" }, - .target = .{}, + .target = b.host, .optimize = .Debug, + .single_threaded = single_threaded, }); - docgen_exe.single_threaded = single_threaded; const docgen_cmd = b.addRunArtifact(docgen_exe); docgen_cmd.addArgs(&.{ "--zig", b.zig_exe }); @@ -89,11 +89,11 @@ pub fn build(b: *std.Build) !void { const check_case_exe = b.addExecutable(.{ .name = "check-case", .root_source_file = .{ .path = "test/src/Cases.zig" }, + .target = b.host, .optimize = optimize, - .main_mod_path = .{ .path = "." }, + .single_threaded = single_threaded, }); check_case_exe.stack_size = stack_size; - check_case_exe.single_threaded = single_threaded; const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false; const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false; @@ -194,14 +194,18 @@ pub fn build(b: *std.Build) !void { break :blk 4; }; - const exe = addCompilerStep(b, optimize, target); - exe.strip = strip; + const exe = addCompilerStep(b, .{ + .optimize = optimize, + .target = target, + .strip = strip, + .sanitize_thread = sanitize_thread, + .single_threaded = single_threaded, + }); exe.pie = pie; - exe.sanitize_thread = sanitize_thread; exe.entitlements = entitlements; exe.build_id = b.option( - std.Build.Step.Compile.BuildId, + std.zig.BuildId, "build-id", "Request creation of '.note.gnu.build-id' section", ); @@ -217,9 +221,7 @@ pub fn build(b: *std.Build) !void { test_step.dependOn(&exe.step); - exe.single_threaded = single_threaded; - - if (target.isWindows() and target.getAbi() == .gnu) { + if (target.result.os.tag == .windows and target.result.abi == .gnu) { // LTO is currently broken on mingw, this can be removed when it's fixed. exe.want_lto = false; check_case_exe.want_lto = false; @@ -230,7 +232,7 @@ pub fn build(b: *std.Build) !void { exe.use_lld = use_llvm; const exe_options = b.addOptions(); - exe.addOptions("build_options", exe_options); + exe.root_module.addOptions("build_options", exe_options); exe_options.addOption(u32, "mem_leak_frames", mem_leak_frames); exe_options.addOption(bool, "skip_non_native", skip_non_native); @@ -345,7 +347,7 @@ pub fn build(b: *std.Build) !void { try addStaticLlvmOptionsToExe(exe); try addStaticLlvmOptionsToExe(check_case_exe); } - if (target.isWindows()) { + if (target.result.os.tag == .windows) { inline for (.{ exe, check_case_exe }) |artifact| { artifact.linkSystemLibrary("version"); artifact.linkSystemLibrary("uuid"); @@ -369,7 +371,7 @@ pub fn build(b: *std.Build) !void { ); // On mingw, we need to opt into windows 7+ to get some features required by tracy. - const tracy_c_flags: []const []const u8 = if (target.isWindows() and target.getAbi() == .gnu) + const tracy_c_flags: []const []const u8 = if (target.result.os.tag == .windows and target.result.abi == .gnu) &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined", "-D_WIN32_WINNT=0x601" } else &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" }; @@ -377,11 +379,11 @@ pub fn build(b: *std.Build) !void { exe.addIncludePath(.{ .cwd_relative = tracy_path }); exe.addCSourceFile(.{ .file = .{ .cwd_relative = client_cpp }, .flags = tracy_c_flags }); if (!enable_llvm) { - exe.linkSystemLibraryName("c++"); + exe.root_module.linkSystemLibrary("c++", .{ .use_pkg_config = .no }); } exe.linkLibC(); - if (target.isWindows()) { + if (target.result.os.tag == .windows) { exe.linkSystemLibrary("dbghelp"); exe.linkSystemLibrary("ws2_32"); } @@ -390,7 +392,7 @@ pub fn build(b: *std.Build) !void { const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); const test_cases_options = b.addOptions(); - check_case_exe.addOptions("build_options", test_cases_options); + check_case_exe.root_module.addOptions("build_options", test_cases_options); test_cases_options.addOption(bool, "enable_tracy", false); test_cases_options.addOption(bool, "enable_logging", enable_logging); @@ -540,16 +542,19 @@ pub fn build(b: *std.Build) !void { fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { const semver = try std.SemanticVersion.parse(version); - var target: std.zig.CrossTarget = .{ + var target_query: std.zig.CrossTarget = .{ .cpu_arch = .wasm32, .os_tag = .wasi, }; - target.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.bulk_memory)); + target_query.cpu_features_add.addFeature(@intFromEnum(std.Target.wasm.Feature.bulk_memory)); - const exe = addCompilerStep(b, .ReleaseSmall, target); + const exe = addCompilerStep(b, .{ + .optimize = .ReleaseSmall, + .target = b.resolveTargetQuery(target_query), + }); const exe_options = b.addOptions(); - exe.addOptions("build_options", exe_options); + exe.root_module.addOptions("build_options", exe_options); exe_options.addOption(u32, "mem_leak_frames", 0); exe_options.addOption(bool, "have_llvm", false); @@ -584,17 +589,24 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { update_zig1_step.dependOn(©_zig_h.step); } -fn addCompilerStep( - b: *std.Build, +const AddCompilerStepOptions = struct { optimize: std.builtin.OptimizeMode, - target: std.zig.CrossTarget, -) *std.Build.Step.Compile { + target: std.Build.ResolvedTarget, + strip: ?bool = null, + sanitize_thread: ?bool = null, + single_threaded: ?bool = null, +}; + +fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.Step.Compile { const exe = b.addExecutable(.{ .name = "zig", .root_source_file = .{ .path = "src/main.zig" }, - .target = target, - .optimize = optimize, + .target = options.target, + .optimize = options.optimize, .max_rss = 7_000_000_000, + .strip = options.strip, + .sanitize_thread = options.sanitize_thread, + .single_threaded = options.single_threaded, }); exe.stack_size = stack_size; @@ -602,15 +614,15 @@ fn addCompilerStep( aro_options.addOption([]const u8, "version_str", "aro-zig"); const aro_options_module = aro_options.createModule(); const aro_backend = b.createModule(.{ - .source_file = .{ .path = "deps/aro/backend.zig" }, - .dependencies = &.{.{ + .root_source_file = .{ .path = "deps/aro/backend.zig" }, + .imports = &.{.{ .name = "build_options", .module = aro_options_module, }}, }); const aro_module = b.createModule(.{ - .source_file = .{ .path = "deps/aro/aro.zig" }, - .dependencies = &.{ + .root_source_file = .{ .path = "deps/aro/aro.zig" }, + .imports = &.{ .{ .name = "build_options", .module = aro_options_module, @@ -625,7 +637,7 @@ fn addCompilerStep( }, }); - exe.addModule("aro", aro_module); + exe.root_module.addImport("aro", aro_module); return exe; } @@ -649,7 +661,7 @@ fn addCmakeCfgOptionsToExe( exe: *std.Build.Step.Compile, use_zig_libcxx: bool, ) !void { - if (exe.target.isDarwin()) { + if (exe.rootModuleTarget().isDarwin()) { // useful for package maintainers exe.headerpad_max_install_names = true; } @@ -677,8 +689,8 @@ fn addCmakeCfgOptionsToExe( // against system-provided LLVM, Clang, LLD. const need_cpp_includes = true; const static = cfg.llvm_linkage == .static; - const lib_suffix = if (static) exe.target.staticLibSuffix()[1..] else exe.target.dynamicLibSuffix()[1..]; - switch (exe.target.getOsTag()) { + const lib_suffix = if (static) exe.rootModuleTarget().staticLibSuffix()[1..] else exe.rootModuleTarget().dynamicLibSuffix()[1..]; + switch (exe.rootModuleTarget().os.tag) { .linux => { // First we try to link against the detected libcxx name. If that doesn't work, we fall // back to -lc++ and cross our fingers. @@ -694,7 +706,7 @@ fn addCmakeCfgOptionsToExe( exe.linkLibCpp(); }, .windows => { - if (exe.target.getAbi() != .msvc) exe.linkLibCpp(); + if (exe.rootModuleTarget().abi != .msvc) exe.linkLibCpp(); }, .freebsd => { if (static) { @@ -756,12 +768,12 @@ fn addStaticLlvmOptionsToExe(exe: *std.Build.Step.Compile) !void { exe.linkSystemLibrary("z"); exe.linkSystemLibrary("zstd"); - if (exe.target.getOs().tag != .windows or exe.target.getAbi() != .msvc) { + if (exe.rootModuleTarget().os.tag != .windows or exe.rootModuleTarget().abi != .msvc) { // This means we rely on clang-or-zig-built LLVM, Clang, LLD libraries. exe.linkSystemLibrary("c++"); } - if (exe.target.getOs().tag == .windows) { + if (exe.rootModuleTarget().os.tag == .windows) { exe.linkSystemLibrary("version"); exe.linkSystemLibrary("uuid"); exe.linkSystemLibrary("ole32"); @@ -810,7 +822,9 @@ fn addCMakeLibraryList(exe: *std.Build.Step.Compile, list: []const u8) void { while (it.next()) |lib| { if (mem.startsWith(u8, lib, "-l")) { exe.linkSystemLibrary(lib["-l".len..]); - } else if (exe.target.isWindows() and mem.endsWith(u8, lib, ".lib") and !fs.path.isAbsolute(lib)) { + } else if (exe.rootModuleTarget().os.tag == .windows and + mem.endsWith(u8, lib, ".lib") and !fs.path.isAbsolute(lib)) + { exe.linkSystemLibrary(lib[0 .. lib.len - ".lib".len]); } else { exe.addObjectFile(.{ .cwd_relative = lib }); diff --git a/ci/x86_64-windows-debug.ps1 b/ci/x86_64-windows-debug.ps1 index c1ae92494e98..bb6ff3ebee74 100644 --- a/ci/x86_64-windows-debug.ps1 +++ b/ci/x86_64-windows-debug.ps1 @@ -76,15 +76,15 @@ Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..." CheckLastExitCode & "stage3-debug\bin\zig.exe" build-obj ` - ..\lib\compiler_rt.zig ` --zig-lib-dir "$ZIG_LIB_DIR" ` -ofmt=c ` -OReleaseSmall ` --name compiler_rt ` -femit-bin="compiler_rt-x86_64-windows-msvc.c" ` - --mod build_options::config.zig ` - --deps build_options ` - -target x86_64-windows-msvc + --dep build_options ` + -target x86_64-windows-msvc ` + --mod root ..\lib\compiler_rt.zig ` + --mod build_options config.zig CheckLastExitCode Import-Module "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\Microsoft.VisualStudio.DevShell.dll" diff --git a/ci/x86_64-windows-release.ps1 b/ci/x86_64-windows-release.ps1 index 988402606b5c..7467e3deaa08 100644 --- a/ci/x86_64-windows-release.ps1 +++ b/ci/x86_64-windows-release.ps1 @@ -75,15 +75,15 @@ Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..." CheckLastExitCode & "stage3-release\bin\zig.exe" build-obj ` - ..\lib\compiler_rt.zig ` --zig-lib-dir "$ZIG_LIB_DIR" ` -ofmt=c ` -OReleaseSmall ` --name compiler_rt ` -femit-bin="compiler_rt-x86_64-windows-msvc.c" ` - --mod build_options::config.zig ` - --deps build_options ` - -target x86_64-windows-msvc + --dep build_options ` + -target x86_64-windows-msvc ` + --mod root ..\lib\compiler_rt.zig ` + --mod build_options config.zig CheckLastExitCode Import-Module "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\Microsoft.VisualStudio.DevShell.dll" diff --git a/deps/aro/aro/Driver.zig b/deps/aro/aro/Driver.zig index 18a24b86a7ed..1738b14093cb 100644 --- a/deps/aro/aro/Driver.zig +++ b/deps/aro/aro/Driver.zig @@ -366,12 +366,15 @@ pub fn parseArgs( } else if (mem.eql(u8, arg, "-S") or mem.eql(u8, arg, "--assemble")) { d.only_preprocess_and_compile = true; } else if (option(arg, "--target=")) |triple| { - const cross = std.zig.CrossTarget.parse(.{ .arch_os_abi = triple }) catch { + const query = std.Target.Query.parse(.{ .arch_os_abi = triple }) catch { try d.comp.addDiagnostic(.{ .tag = .cli_invalid_target, .extra = .{ .str = arg } }, &.{}); continue; }; - d.comp.target = cross.toTarget(); // TODO deprecated - d.comp.langopts.setEmulatedCompiler(target_util.systemCompiler(d.comp.target)); + const target = std.zig.system.resolveTargetQuery(query) catch |e| { + return d.fatal("unable to resolve target: {s}", .{errorDescription(e)}); + }; + d.comp.target = target; + d.comp.langopts.setEmulatedCompiler(target_util.systemCompiler(target)); d.raw_target_triple = triple; } else if (mem.eql(u8, arg, "--verbose-ast")) { d.verbose_ast = true; diff --git a/deps/aro/build/GenerateDef.zig b/deps/aro/build/GenerateDef.zig index 129d65ebdf2a..5c0f8d4b18bd 100644 --- a/deps/aro/build/GenerateDef.zig +++ b/deps/aro/build/GenerateDef.zig @@ -21,7 +21,7 @@ pub const Options = struct { pub const Kind = enum { dafsa, named }; }; -pub fn create(owner: *std.Build, options: Options) std.Build.ModuleDependency { +pub fn create(owner: *std.Build, options: Options) std.Build.Module.Import { const self = owner.allocator.create(GenerateDef) catch @panic("OOM"); const path = owner.pathJoin(&.{ options.src_prefix, options.name }); @@ -39,7 +39,7 @@ pub fn create(owner: *std.Build, options: Options) std.Build.ModuleDependency { .generated_file = .{ .step = &self.step }, }; const module = self.step.owner.createModule(.{ - .source_file = .{ .generated = &self.generated_file }, + .root_source_file = .{ .generated = &self.generated_file }, }); return .{ .module = module, diff --git a/lib/build_runner.zig b/lib/build_runner.zig index a5e537c43c4c..979cc93f1140 100644 --- a/lib/build_runner.zig +++ b/lib/build_runner.zig @@ -46,7 +46,10 @@ pub fn main() !void { return error.InvalidArgs; }; - const host = try std.zig.system.NativeTargetInfo.detect(.{}); + const host: std.Build.ResolvedTarget = .{ + .query = .{}, + .result = try std.zig.system.resolveTargetQuery(.{}), + }; const build_root_directory: std.Build.Cache.Directory = .{ .path = build_root, @@ -96,6 +99,7 @@ pub fn main() !void { var skip_oom_steps: bool = false; var color: Color = .auto; var seed: u32 = 0; + var prominent_compile_errors: bool = false; const stderr_stream = io.getStdErr().writer(); const stdout_stream = io.getStdOut().writer(); @@ -239,6 +243,8 @@ pub fn main() !void { builder.verbose_cc = true; } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) { builder.verbose_llvm_cpu_features = true; + } else if (mem.eql(u8, arg, "--prominent-compile-errors")) { + prominent_compile_errors = true; } else if (mem.eql(u8, arg, "-fwine")) { builder.enable_wine = true; } else if (mem.eql(u8, arg, "-fno-wine")) { @@ -322,6 +328,7 @@ pub fn main() !void { .max_rss_mutex = .{}, .skip_oom_steps = skip_oom_steps, .memory_blocked_steps = std.ArrayList(*Step).init(arena), + .prominent_compile_errors = prominent_compile_errors, .claimed_rss = 0, .summary = summary, @@ -354,11 +361,12 @@ const Run = struct { max_rss_mutex: std.Thread.Mutex, skip_oom_steps: bool, memory_blocked_steps: std.ArrayList(*Step), + prominent_compile_errors: bool, claimed_rss: usize, summary: ?Summary, ttyconf: std.io.tty.Config, - stderr: std.fs.File, + stderr: File, }; fn runStepNames( @@ -558,7 +566,7 @@ fn runStepNames( // Finally, render compile errors at the bottom of the terminal. // We use a separate compile_error_steps array list because step_stack is destructively // mutated in printTreeStep above. - if (total_compile_errors > 0) { + if (run.prominent_compile_errors and total_compile_errors > 0) { for (compile_error_steps.items) |s| { if (s.result_error_bundle.errorMessageCount() > 0) { s.result_error_bundle.renderToStdErr(renderOptions(ttyconf)); @@ -579,7 +587,7 @@ const PrintNode = struct { last: bool = false, }; -fn printPrefix(node: *PrintNode, stderr: std.fs.File, ttyconf: std.io.tty.Config) !void { +fn printPrefix(node: *PrintNode, stderr: File, ttyconf: std.io.tty.Config) !void { const parent = node.parent orelse return; if (parent.parent == null) return; try printPrefix(parent, stderr, ttyconf); @@ -593,11 +601,145 @@ fn printPrefix(node: *PrintNode, stderr: std.fs.File, ttyconf: std.io.tty.Config } } +fn printChildNodePrefix(stderr: File, ttyconf: std.io.tty.Config) !void { + try stderr.writeAll(switch (ttyconf) { + .no_color, .windows_api => "+- ", + .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─ + }); +} + +fn printStepStatus( + s: *Step, + stderr: File, + ttyconf: std.io.tty.Config, + run: *const Run, +) !void { + switch (s.state) { + .precheck_unstarted => unreachable, + .precheck_started => unreachable, + .precheck_done => unreachable, + .running => unreachable, + + .dependency_failure => { + try ttyconf.setColor(stderr, .dim); + try stderr.writeAll(" transitive failure\n"); + try ttyconf.setColor(stderr, .reset); + }, + + .success => { + try ttyconf.setColor(stderr, .green); + if (s.result_cached) { + try stderr.writeAll(" cached"); + } else if (s.test_results.test_count > 0) { + const pass_count = s.test_results.passCount(); + try stderr.writer().print(" {d} passed", .{pass_count}); + if (s.test_results.skip_count > 0) { + try ttyconf.setColor(stderr, .yellow); + try stderr.writer().print(" {d} skipped", .{s.test_results.skip_count}); + } + } else { + try stderr.writeAll(" success"); + } + try ttyconf.setColor(stderr, .reset); + if (s.result_duration_ns) |ns| { + try ttyconf.setColor(stderr, .dim); + if (ns >= std.time.ns_per_min) { + try stderr.writer().print(" {d}m", .{ns / std.time.ns_per_min}); + } else if (ns >= std.time.ns_per_s) { + try stderr.writer().print(" {d}s", .{ns / std.time.ns_per_s}); + } else if (ns >= std.time.ns_per_ms) { + try stderr.writer().print(" {d}ms", .{ns / std.time.ns_per_ms}); + } else if (ns >= std.time.ns_per_us) { + try stderr.writer().print(" {d}us", .{ns / std.time.ns_per_us}); + } else { + try stderr.writer().print(" {d}ns", .{ns}); + } + try ttyconf.setColor(stderr, .reset); + } + if (s.result_peak_rss != 0) { + const rss = s.result_peak_rss; + try ttyconf.setColor(stderr, .dim); + if (rss >= 1000_000_000) { + try stderr.writer().print(" MaxRSS:{d}G", .{rss / 1000_000_000}); + } else if (rss >= 1000_000) { + try stderr.writer().print(" MaxRSS:{d}M", .{rss / 1000_000}); + } else if (rss >= 1000) { + try stderr.writer().print(" MaxRSS:{d}K", .{rss / 1000}); + } else { + try stderr.writer().print(" MaxRSS:{d}B", .{rss}); + } + try ttyconf.setColor(stderr, .reset); + } + try stderr.writeAll("\n"); + }, + .skipped, .skipped_oom => |skip| { + try ttyconf.setColor(stderr, .yellow); + try stderr.writeAll(" skipped"); + if (skip == .skipped_oom) { + try stderr.writeAll(" (not enough memory)"); + try ttyconf.setColor(stderr, .dim); + try stderr.writer().print(" upper bound of {d} exceeded runner limit ({d})", .{ s.max_rss, run.max_rss }); + try ttyconf.setColor(stderr, .yellow); + } + try stderr.writeAll("\n"); + try ttyconf.setColor(stderr, .reset); + }, + .failure => try printStepFailure(s, stderr, ttyconf), + } +} + +fn printStepFailure( + s: *Step, + stderr: File, + ttyconf: std.io.tty.Config, +) !void { + if (s.result_error_bundle.errorMessageCount() > 0) { + try ttyconf.setColor(stderr, .red); + try stderr.writer().print(" {d} errors\n", .{ + s.result_error_bundle.errorMessageCount(), + }); + try ttyconf.setColor(stderr, .reset); + } else if (!s.test_results.isSuccess()) { + try stderr.writer().print(" {d}/{d} passed", .{ + s.test_results.passCount(), s.test_results.test_count, + }); + if (s.test_results.fail_count > 0) { + try stderr.writeAll(", "); + try ttyconf.setColor(stderr, .red); + try stderr.writer().print("{d} failed", .{ + s.test_results.fail_count, + }); + try ttyconf.setColor(stderr, .reset); + } + if (s.test_results.skip_count > 0) { + try stderr.writeAll(", "); + try ttyconf.setColor(stderr, .yellow); + try stderr.writer().print("{d} skipped", .{ + s.test_results.skip_count, + }); + try ttyconf.setColor(stderr, .reset); + } + if (s.test_results.leak_count > 0) { + try stderr.writeAll(", "); + try ttyconf.setColor(stderr, .red); + try stderr.writer().print("{d} leaked", .{ + s.test_results.leak_count, + }); + try ttyconf.setColor(stderr, .reset); + } + try stderr.writeAll("\n"); + } else { + try ttyconf.setColor(stderr, .red); + try stderr.writeAll(" failure\n"); + try ttyconf.setColor(stderr, .reset); + } +} + fn printTreeStep( b: *std.Build, s: *Step, run: *const Run, - stderr: std.fs.File, + stderr: File, ttyconf: std.io.tty.Config, parent_node: *PrintNode, step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void), @@ -610,10 +752,7 @@ fn printTreeStep( if (!first) try ttyconf.setColor(stderr, .dim); if (parent_node.parent != null) { if (parent_node.last) { - try stderr.writeAll(switch (ttyconf) { - .no_color, .windows_api => "+- ", - .escape_codes => "\x1B\x28\x30\x6d\x71\x1B\x28\x42 ", // └─ - }); + try printChildNodePrefix(stderr, ttyconf); } else { try stderr.writeAll(switch (ttyconf) { .no_color, .windows_api => "+- ", @@ -626,119 +765,7 @@ fn printTreeStep( try stderr.writeAll(s.name); if (first) { - switch (s.state) { - .precheck_unstarted => unreachable, - .precheck_started => unreachable, - .precheck_done => unreachable, - .running => unreachable, - - .dependency_failure => { - try ttyconf.setColor(stderr, .dim); - try stderr.writeAll(" transitive failure\n"); - try ttyconf.setColor(stderr, .reset); - }, - - .success => { - try ttyconf.setColor(stderr, .green); - if (s.result_cached) { - try stderr.writeAll(" cached"); - } else if (s.test_results.test_count > 0) { - const pass_count = s.test_results.passCount(); - try stderr.writer().print(" {d} passed", .{pass_count}); - if (s.test_results.skip_count > 0) { - try ttyconf.setColor(stderr, .yellow); - try stderr.writer().print(" {d} skipped", .{s.test_results.skip_count}); - } - } else { - try stderr.writeAll(" success"); - } - try ttyconf.setColor(stderr, .reset); - if (s.result_duration_ns) |ns| { - try ttyconf.setColor(stderr, .dim); - if (ns >= std.time.ns_per_min) { - try stderr.writer().print(" {d}m", .{ns / std.time.ns_per_min}); - } else if (ns >= std.time.ns_per_s) { - try stderr.writer().print(" {d}s", .{ns / std.time.ns_per_s}); - } else if (ns >= std.time.ns_per_ms) { - try stderr.writer().print(" {d}ms", .{ns / std.time.ns_per_ms}); - } else if (ns >= std.time.ns_per_us) { - try stderr.writer().print(" {d}us", .{ns / std.time.ns_per_us}); - } else { - try stderr.writer().print(" {d}ns", .{ns}); - } - try ttyconf.setColor(stderr, .reset); - } - if (s.result_peak_rss != 0) { - const rss = s.result_peak_rss; - try ttyconf.setColor(stderr, .dim); - if (rss >= 1000_000_000) { - try stderr.writer().print(" MaxRSS:{d}G", .{rss / 1000_000_000}); - } else if (rss >= 1000_000) { - try stderr.writer().print(" MaxRSS:{d}M", .{rss / 1000_000}); - } else if (rss >= 1000) { - try stderr.writer().print(" MaxRSS:{d}K", .{rss / 1000}); - } else { - try stderr.writer().print(" MaxRSS:{d}B", .{rss}); - } - try ttyconf.setColor(stderr, .reset); - } - try stderr.writeAll("\n"); - }, - .skipped, .skipped_oom => |skip| { - try ttyconf.setColor(stderr, .yellow); - try stderr.writeAll(" skipped"); - if (skip == .skipped_oom) { - try stderr.writeAll(" (not enough memory)"); - try ttyconf.setColor(stderr, .dim); - try stderr.writer().print(" upper bound of {d} exceeded runner limit ({d})", .{ s.max_rss, run.max_rss }); - try ttyconf.setColor(stderr, .yellow); - } - try stderr.writeAll("\n"); - try ttyconf.setColor(stderr, .reset); - }, - .failure => { - if (s.result_error_bundle.errorMessageCount() > 0) { - try ttyconf.setColor(stderr, .red); - try stderr.writer().print(" {d} errors\n", .{ - s.result_error_bundle.errorMessageCount(), - }); - try ttyconf.setColor(stderr, .reset); - } else if (!s.test_results.isSuccess()) { - try stderr.writer().print(" {d}/{d} passed", .{ - s.test_results.passCount(), s.test_results.test_count, - }); - if (s.test_results.fail_count > 0) { - try stderr.writeAll(", "); - try ttyconf.setColor(stderr, .red); - try stderr.writer().print("{d} failed", .{ - s.test_results.fail_count, - }); - try ttyconf.setColor(stderr, .reset); - } - if (s.test_results.skip_count > 0) { - try stderr.writeAll(", "); - try ttyconf.setColor(stderr, .yellow); - try stderr.writer().print("{d} skipped", .{ - s.test_results.skip_count, - }); - try ttyconf.setColor(stderr, .reset); - } - if (s.test_results.leak_count > 0) { - try stderr.writeAll(", "); - try ttyconf.setColor(stderr, .red); - try stderr.writer().print("{d} leaked", .{ - s.test_results.leak_count, - }); - try ttyconf.setColor(stderr, .reset); - } - try stderr.writeAll("\n"); - } else { - try ttyconf.setColor(stderr, .red); - try stderr.writeAll(" failure\n"); - try ttyconf.setColor(stderr, .reset); - } - }, - } + try printStepStatus(s, stderr, ttyconf, run); const last_index = if (!failures_only) s.dependencies.items.len -| 1 else blk: { var i: usize = s.dependencies.items.len; @@ -888,26 +915,15 @@ fn workerMakeOneStep( const make_result = s.make(&sub_prog_node); // No matter the result, we want to display error/warning messages. - if (s.result_error_msgs.items.len > 0) { + const show_compile_errors = !run.prominent_compile_errors and + s.result_error_bundle.errorMessageCount() > 0; + const show_error_msgs = s.result_error_msgs.items.len > 0; + + if (show_error_msgs or show_compile_errors) { sub_prog_node.context.lock_stderr(); defer sub_prog_node.context.unlock_stderr(); - const stderr = run.stderr; - const ttyconf = run.ttyconf; - - for (s.result_error_msgs.items) |msg| { - // Sometimes it feels like you just can't catch a break. Finally, - // with Zig, you can. - ttyconf.setColor(stderr, .bold) catch break; - stderr.writeAll(s.owner.dep_prefix) catch break; - stderr.writeAll(s.name) catch break; - stderr.writeAll(": ") catch break; - ttyconf.setColor(stderr, .red) catch break; - stderr.writeAll("error: ") catch break; - ttyconf.setColor(stderr, .reset) catch break; - stderr.writeAll(msg) catch break; - stderr.writeAll("\n") catch break; - } + printErrorMessages(b, s, run) catch {}; } handle_result: { @@ -962,6 +978,54 @@ fn workerMakeOneStep( } } +fn printErrorMessages(b: *std.Build, failing_step: *Step, run: *const Run) !void { + const gpa = b.allocator; + const stderr = run.stderr; + const ttyconf = run.ttyconf; + + // Provide context for where these error messages are coming from by + // printing the corresponding Step subtree. + + var step_stack: std.ArrayListUnmanaged(*Step) = .{}; + defer step_stack.deinit(gpa); + try step_stack.append(gpa, failing_step); + while (step_stack.items[step_stack.items.len - 1].dependants.items.len != 0) { + try step_stack.append(gpa, step_stack.items[step_stack.items.len - 1].dependants.items[0]); + } + + // Now, `step_stack` has the subtree that we want to print, in reverse order. + try ttyconf.setColor(stderr, .dim); + var indent: usize = 0; + while (step_stack.popOrNull()) |s| : (indent += 1) { + if (indent > 0) { + try stderr.writer().writeByteNTimes(' ', (indent - 1) * 3); + try printChildNodePrefix(stderr, ttyconf); + } + + try stderr.writeAll(s.name); + + if (s == failing_step) { + try printStepFailure(s, stderr, ttyconf); + } else { + try stderr.writeAll("\n"); + } + } + try ttyconf.setColor(stderr, .reset); + + // Penultimately, the compilation errors. + if (!run.prominent_compile_errors and failing_step.result_error_bundle.errorMessageCount() > 0) + try failing_step.result_error_bundle.renderToWriter(renderOptions(ttyconf), stderr.writer()); + + // Finally, generic error messages. + for (failing_step.result_error_msgs.items) |msg| { + try ttyconf.setColor(stderr, .red); + try stderr.writeAll("error: "); + try ttyconf.setColor(stderr, .reset); + try stderr.writeAll(msg); + try stderr.writeAll("\n"); + } +} + fn steps(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void { // run the build script to collect the options if (!already_ran_build) { @@ -1027,6 +1091,7 @@ fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !voi \\ -l, --list-steps Print available steps \\ --verbose Print commands before executing them \\ --color [auto|off|on] Enable or disable colored error messages + \\ --prominent-compile-errors Buffer compile errors and display at end \\ --summary [mode] Control the printing of the build summary \\ all Print the build summary in its entirety \\ failures (Default) Only print failed steps @@ -1111,7 +1176,7 @@ fn cleanExit() void { const Color = enum { auto, off, on }; const Summary = enum { all, failures, none }; -fn get_tty_conf(color: Color, stderr: std.fs.File) std.io.tty.Config { +fn get_tty_conf(color: Color, stderr: File) std.io.tty.Config { return switch (color) { .auto => std.io.tty.detectConfig(stderr), .on => .escape_codes, diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 7ace95947368..02f34ebfb1df 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -10,55 +10,17 @@ const log = std.log; const ArrayList = std.ArrayList; const StringHashMap = std.StringHashMap; const Allocator = mem.Allocator; +const Target = std.Target; const process = std.process; const EnvMap = std.process.EnvMap; const fmt_lib = std.fmt; const File = std.fs.File; -const CrossTarget = std.zig.CrossTarget; -const NativeTargetInfo = std.zig.system.NativeTargetInfo; const Sha256 = std.crypto.hash.sha2.Sha256; const Build = @This(); pub const Cache = @import("Build/Cache.zig"); - -/// deprecated: use `Step.Compile`. -pub const LibExeObjStep = Step.Compile; -/// deprecated: use `Build`. -pub const Builder = Build; -/// deprecated: use `Step.InstallDir.Options` -pub const InstallDirectoryOptions = Step.InstallDir.Options; - pub const Step = @import("Build/Step.zig"); -/// deprecated: use `Step.CheckFile`. -pub const CheckFileStep = @import("Build/Step/CheckFile.zig"); -/// deprecated: use `Step.CheckObject`. -pub const CheckObjectStep = @import("Build/Step/CheckObject.zig"); -/// deprecated: use `Step.ConfigHeader`. -pub const ConfigHeaderStep = @import("Build/Step/ConfigHeader.zig"); -/// deprecated: use `Step.Fmt`. -pub const FmtStep = @import("Build/Step/Fmt.zig"); -/// deprecated: use `Step.InstallArtifact`. -pub const InstallArtifactStep = @import("Build/Step/InstallArtifact.zig"); -/// deprecated: use `Step.InstallDir`. -pub const InstallDirStep = @import("Build/Step/InstallDir.zig"); -/// deprecated: use `Step.InstallFile`. -pub const InstallFileStep = @import("Build/Step/InstallFile.zig"); -/// deprecated: use `Step.ObjCopy`. -pub const ObjCopyStep = @import("Build/Step/ObjCopy.zig"); -/// deprecated: use `Step.Compile`. -pub const CompileStep = @import("Build/Step/Compile.zig"); -/// deprecated: use `Step.Options`. -pub const OptionsStep = @import("Build/Step/Options.zig"); -/// deprecated: use `Step.RemoveDir`. -pub const RemoveDirStep = @import("Build/Step/RemoveDir.zig"); -/// deprecated: use `Step.Run`. -pub const RunStep = @import("Build/Step/Run.zig"); -/// deprecated: use `Step.TranslateC`. -pub const TranslateCStep = @import("Build/Step/TranslateC.zig"); -/// deprecated: use `Step.WriteFile`. -pub const WriteFileStep = @import("Build/Step/WriteFile.zig"); -/// deprecated: use `LazyPath`. -pub const FileSource = LazyPath; +pub const Module = @import("Build/Module.zig"); install_tls: TopLevelStep, uninstall_tls: TopLevelStep, @@ -96,7 +58,6 @@ cache_root: Cache.Directory, global_cache_root: Cache.Directory, cache: *Cache, zig_lib_dir: ?LazyPath, -vcpkg_root: VcpkgRoot = .unattempted, pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, args: ?[][]const u8 = null, debug_log_scopes: []const []const u8 = &.{}, @@ -124,7 +85,7 @@ enable_wine: bool = false, glibc_runtimes_dir: ?[]const u8 = null, /// Information about the native target. Computed before build() is invoked. -host: NativeTargetInfo, +host: ResolvedTarget, dep_prefix: []const u8 = "", @@ -250,7 +211,7 @@ pub fn create( build_root: Cache.Directory, cache_root: Cache.Directory, global_cache_root: Cache.Directory, - host: NativeTargetInfo, + host: ResolvedTarget, cache: *Cache, available_deps: AvailableDeps, ) !*Build { @@ -414,7 +375,7 @@ fn userInputOptionsFromArgs(allocator: Allocator, args: anytype) UserInputOption const v = @field(args, field.name); const T = @TypeOf(v); switch (T) { - CrossTarget => { + Target.Query => { user_input_options.put(field.name, .{ .name = field.name, .value = .{ .scalar = v.zigTriple(allocator) catch @panic("OOM") }, @@ -422,12 +383,19 @@ fn userInputOptionsFromArgs(allocator: Allocator, args: anytype) UserInputOption }) catch @panic("OOM"); user_input_options.put("cpu", .{ .name = "cpu", - .value = .{ - .scalar = if (v.isNativeCpu()) - "native" - else - serializeCpu(allocator, v.getCpu()) catch unreachable, - }, + .value = .{ .scalar = v.serializeCpuAlloc(allocator) catch @panic("OOM") }, + .used = false, + }) catch @panic("OOM"); + }, + ResolvedTarget => { + user_input_options.put(field.name, .{ + .name = field.name, + .value = .{ .scalar = v.query.zigTriple(allocator) catch @panic("OOM") }, + .used = false, + }) catch @panic("OOM"); + user_input_options.put("cpu", .{ + .name = "cpu", + .value = .{ .scalar = v.query.serializeCpuAlloc(allocator) catch @panic("OOM") }, .used = false, }) catch @panic("OOM"); }, @@ -623,45 +591,57 @@ pub fn addOptions(self: *Build) *Step.Options { pub const ExecutableOptions = struct { name: []const u8, + /// If you want the executable to run on the same computer as the one + /// building the package, pass the `host` field of the package's `Build` + /// instance. + target: ResolvedTarget, root_source_file: ?LazyPath = null, version: ?std.SemanticVersion = null, - target: CrossTarget = .{}, optimize: std.builtin.OptimizeMode = .Debug, linkage: ?Step.Compile.Linkage = null, max_rss: usize = 0, link_libc: ?bool = null, single_threaded: ?bool = null, + pic: ?bool = null, + strip: ?bool = null, + unwind_tables: ?bool = null, + omit_frame_pointer: ?bool = null, + sanitize_thread: ?bool = null, + error_tracing: ?bool = null, use_llvm: ?bool = null, use_lld: ?bool = null, zig_lib_dir: ?LazyPath = null, - main_mod_path: ?LazyPath = null, /// Embed a `.manifest` file in the compilation if the object format supports it. /// https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-files-reference /// Manifest files must have the extension `.manifest`. /// Can be set regardless of target. The `.manifest` file will be ignored /// if the target object format does not support embedded manifests. win32_manifest: ?LazyPath = null, - - /// Deprecated; use `main_mod_path`. - main_pkg_path: ?LazyPath = null, }; pub fn addExecutable(b: *Build, options: ExecutableOptions) *Step.Compile { return Step.Compile.create(b, .{ .name = options.name, - .root_source_file = options.root_source_file, + .root_module = .{ + .root_source_file = options.root_source_file, + .target = options.target, + .optimize = options.optimize, + .link_libc = options.link_libc, + .single_threaded = options.single_threaded, + .pic = options.pic, + .strip = options.strip, + .unwind_tables = options.unwind_tables, + .omit_frame_pointer = options.omit_frame_pointer, + .sanitize_thread = options.sanitize_thread, + .error_tracing = options.error_tracing, + }, .version = options.version, - .target = options.target, - .optimize = options.optimize, .kind = .exe, .linkage = options.linkage, .max_rss = options.max_rss, - .link_libc = options.link_libc, - .single_threaded = options.single_threaded, .use_llvm = options.use_llvm, .use_lld = options.use_lld, .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir, - .main_mod_path = options.main_mod_path orelse options.main_pkg_path, .win32_manifest = options.win32_manifest, }); } @@ -669,77 +649,99 @@ pub fn addExecutable(b: *Build, options: ExecutableOptions) *Step.Compile { pub const ObjectOptions = struct { name: []const u8, root_source_file: ?LazyPath = null, - target: CrossTarget, + /// To choose the same computer as the one building the package, pass the + /// `host` field of the package's `Build` instance. + target: ResolvedTarget, optimize: std.builtin.OptimizeMode, max_rss: usize = 0, link_libc: ?bool = null, single_threaded: ?bool = null, + pic: ?bool = null, + strip: ?bool = null, + unwind_tables: ?bool = null, + omit_frame_pointer: ?bool = null, + sanitize_thread: ?bool = null, + error_tracing: ?bool = null, use_llvm: ?bool = null, use_lld: ?bool = null, zig_lib_dir: ?LazyPath = null, - main_mod_path: ?LazyPath = null, - - /// Deprecated; use `main_mod_path`. - main_pkg_path: ?LazyPath = null, }; pub fn addObject(b: *Build, options: ObjectOptions) *Step.Compile { return Step.Compile.create(b, .{ .name = options.name, - .root_source_file = options.root_source_file, - .target = options.target, - .optimize = options.optimize, + .root_module = .{ + .root_source_file = options.root_source_file, + .target = options.target, + .optimize = options.optimize, + .link_libc = options.link_libc, + .single_threaded = options.single_threaded, + .pic = options.pic, + .strip = options.strip, + .unwind_tables = options.unwind_tables, + .omit_frame_pointer = options.omit_frame_pointer, + .sanitize_thread = options.sanitize_thread, + .error_tracing = options.error_tracing, + }, .kind = .obj, .max_rss = options.max_rss, - .link_libc = options.link_libc, - .single_threaded = options.single_threaded, .use_llvm = options.use_llvm, .use_lld = options.use_lld, .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir, - .main_mod_path = options.main_mod_path orelse options.main_pkg_path, }); } pub const SharedLibraryOptions = struct { name: []const u8, + /// To choose the same computer as the one building the package, pass the + /// `host` field of the package's `Build` instance. + target: ResolvedTarget, + optimize: std.builtin.OptimizeMode, root_source_file: ?LazyPath = null, version: ?std.SemanticVersion = null, - target: CrossTarget, - optimize: std.builtin.OptimizeMode, max_rss: usize = 0, link_libc: ?bool = null, single_threaded: ?bool = null, + pic: ?bool = null, + strip: ?bool = null, + unwind_tables: ?bool = null, + omit_frame_pointer: ?bool = null, + sanitize_thread: ?bool = null, + error_tracing: ?bool = null, use_llvm: ?bool = null, use_lld: ?bool = null, zig_lib_dir: ?LazyPath = null, - main_mod_path: ?LazyPath = null, /// Embed a `.manifest` file in the compilation if the object format supports it. /// https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-files-reference /// Manifest files must have the extension `.manifest`. /// Can be set regardless of target. The `.manifest` file will be ignored /// if the target object format does not support embedded manifests. win32_manifest: ?LazyPath = null, - - /// Deprecated; use `main_mod_path`. - main_pkg_path: ?LazyPath = null, }; pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *Step.Compile { return Step.Compile.create(b, .{ .name = options.name, - .root_source_file = options.root_source_file, + .root_module = .{ + .target = options.target, + .optimize = options.optimize, + .root_source_file = options.root_source_file, + .link_libc = options.link_libc, + .single_threaded = options.single_threaded, + .pic = options.pic, + .strip = options.strip, + .unwind_tables = options.unwind_tables, + .omit_frame_pointer = options.omit_frame_pointer, + .sanitize_thread = options.sanitize_thread, + .error_tracing = options.error_tracing, + }, .kind = .lib, .linkage = .dynamic, .version = options.version, - .target = options.target, - .optimize = options.optimize, .max_rss = options.max_rss, - .link_libc = options.link_libc, - .single_threaded = options.single_threaded, .use_llvm = options.use_llvm, .use_lld = options.use_lld, .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir, - .main_mod_path = options.main_mod_path orelse options.main_pkg_path, .win32_manifest = options.win32_manifest, }); } @@ -747,44 +749,55 @@ pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *Step.Compile pub const StaticLibraryOptions = struct { name: []const u8, root_source_file: ?LazyPath = null, - target: CrossTarget, + /// To choose the same computer as the one building the package, pass the + /// `host` field of the package's `Build` instance. + target: ResolvedTarget, optimize: std.builtin.OptimizeMode, version: ?std.SemanticVersion = null, max_rss: usize = 0, link_libc: ?bool = null, single_threaded: ?bool = null, + pic: ?bool = null, + strip: ?bool = null, + unwind_tables: ?bool = null, + omit_frame_pointer: ?bool = null, + sanitize_thread: ?bool = null, + error_tracing: ?bool = null, use_llvm: ?bool = null, use_lld: ?bool = null, zig_lib_dir: ?LazyPath = null, - main_mod_path: ?LazyPath = null, - - /// Deprecated; use `main_mod_path`. - main_pkg_path: ?LazyPath = null, }; pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *Step.Compile { return Step.Compile.create(b, .{ .name = options.name, - .root_source_file = options.root_source_file, + .root_module = .{ + .target = options.target, + .optimize = options.optimize, + .root_source_file = options.root_source_file, + .link_libc = options.link_libc, + .single_threaded = options.single_threaded, + .pic = options.pic, + .strip = options.strip, + .unwind_tables = options.unwind_tables, + .omit_frame_pointer = options.omit_frame_pointer, + .sanitize_thread = options.sanitize_thread, + .error_tracing = options.error_tracing, + }, .kind = .lib, .linkage = .static, .version = options.version, - .target = options.target, - .optimize = options.optimize, .max_rss = options.max_rss, - .link_libc = options.link_libc, - .single_threaded = options.single_threaded, .use_llvm = options.use_llvm, .use_lld = options.use_lld, .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir, - .main_mod_path = options.main_mod_path orelse options.main_pkg_path, }); } pub const TestOptions = struct { name: []const u8 = "test", root_source_file: LazyPath, - target: CrossTarget = .{}, + target: ?ResolvedTarget = null, optimize: std.builtin.OptimizeMode = .Debug, version: ?std.SemanticVersion = null, max_rss: usize = 0, @@ -792,38 +805,49 @@ pub const TestOptions = struct { test_runner: ?[]const u8 = null, link_libc: ?bool = null, single_threaded: ?bool = null, + pic: ?bool = null, + strip: ?bool = null, + unwind_tables: ?bool = null, + omit_frame_pointer: ?bool = null, + sanitize_thread: ?bool = null, + error_tracing: ?bool = null, use_llvm: ?bool = null, use_lld: ?bool = null, zig_lib_dir: ?LazyPath = null, - main_mod_path: ?LazyPath = null, - - /// Deprecated; use `main_mod_path`. - main_pkg_path: ?LazyPath = null, }; pub fn addTest(b: *Build, options: TestOptions) *Step.Compile { return Step.Compile.create(b, .{ .name = options.name, .kind = .@"test", - .root_source_file = options.root_source_file, - .target = options.target, - .optimize = options.optimize, + .root_module = .{ + .root_source_file = options.root_source_file, + .target = options.target orelse b.host, + .optimize = options.optimize, + .link_libc = options.link_libc, + .single_threaded = options.single_threaded, + .pic = options.pic, + .strip = options.strip, + .unwind_tables = options.unwind_tables, + .omit_frame_pointer = options.omit_frame_pointer, + .sanitize_thread = options.sanitize_thread, + .error_tracing = options.error_tracing, + }, .max_rss = options.max_rss, .filter = options.filter, .test_runner = options.test_runner, - .link_libc = options.link_libc, - .single_threaded = options.single_threaded, .use_llvm = options.use_llvm, .use_lld = options.use_lld, .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir, - .main_mod_path = options.main_mod_path orelse options.main_pkg_path, }); } pub const AssemblyOptions = struct { name: []const u8, source_file: LazyPath, - target: CrossTarget, + /// To choose the same computer as the one building the package, pass the + /// `host` field of the package's `Build` instance. + target: ResolvedTarget, optimize: std.builtin.OptimizeMode, max_rss: usize = 0, zig_lib_dir: ?LazyPath = null, @@ -833,9 +857,10 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *Step.Compile { const obj_step = Step.Compile.create(b, .{ .name = options.name, .kind = .obj, - .root_source_file = null, - .target = options.target, - .optimize = options.optimize, + .root_module = .{ + .target = options.target, + .optimize = options.optimize, + }, .max_rss = options.max_rss, .zig_lib_dir = options.zig_lib_dir orelse b.zig_lib_dir, }); @@ -846,41 +871,17 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *Step.Compile { /// This function creates a module and adds it to the package's module set, making /// it available to other packages which depend on this one. /// `createModule` can be used instead to create a private module. -pub fn addModule(b: *Build, name: []const u8, options: CreateModuleOptions) *Module { - const module = b.createModule(options); +pub fn addModule(b: *Build, name: []const u8, options: Module.CreateOptions) *Module { + const module = Module.create(b, options); b.modules.put(b.dupe(name), module) catch @panic("OOM"); return module; } -pub const ModuleDependency = struct { - name: []const u8, - module: *Module, -}; - -pub const CreateModuleOptions = struct { - source_file: LazyPath, - dependencies: []const ModuleDependency = &.{}, -}; - /// This function creates a private module, to be used by the current package, /// but not exposed to other packages depending on this one. /// `addModule` can be used instead to create a public module. -pub fn createModule(b: *Build, options: CreateModuleOptions) *Module { - const module = b.allocator.create(Module) catch @panic("OOM"); - module.* = .{ - .builder = b, - .source_file = options.source_file.dupe(b), - .dependencies = moduleDependenciesToArrayHashMap(b.allocator, options.dependencies), - }; - return module; -} - -fn moduleDependenciesToArrayHashMap(arena: Allocator, deps: []const ModuleDependency) std.StringArrayHashMap(*Module) { - var result = std.StringArrayHashMap(*Module).init(arena); - for (deps) |dep| { - result.put(dep.name, dep.module) catch @panic("OOM"); - } - return result; +pub fn createModule(b: *Build, options: Module.CreateOptions) *Module { + return Module.create(b, options); } /// Initializes a `Step.Run` with argv, which must at least have the path to the @@ -908,10 +909,6 @@ pub fn addRunArtifact(b: *Build, exe: *Step.Compile) *Step.Run { run_step.enableTestRunnerMode(); } - if (exe.vcpkg_bin_path) |path| { - run_step.addPathDir(path); - } - return run_step; } @@ -1133,7 +1130,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_ return null; }, .scalar => |s| { - if (Step.Compile.BuildId.parse(s)) |build_id| { + if (std.zig.BuildId.parse(s)) |build_id| { return build_id; } else |err| { log.err("unable to parse option '-D{s}': {s}", .{ name, @errorName(err) }); @@ -1198,19 +1195,25 @@ pub fn standardOptimizeOption(self: *Build, options: StandardOptimizeOptionOptio } pub const StandardTargetOptionsArgs = struct { - whitelist: ?[]const CrossTarget = null, - - default_target: CrossTarget = CrossTarget{}, + whitelist: ?[]const Target.Query = null, + default_target: Target.Query = .{}, }; +/// Exposes standard `zig build` options for choosing a target and additionally +/// resolves the target query. +pub fn standardTargetOptions(b: *Build, args: StandardTargetOptionsArgs) ResolvedTarget { + const query = b.standardTargetOptionsQueryOnly(args); + return b.resolveTargetQuery(query); +} + /// Exposes standard `zig build` options for choosing a target. -pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) CrossTarget { - const maybe_triple = self.option( +pub fn standardTargetOptionsQueryOnly(b: *Build, args: StandardTargetOptionsArgs) Target.Query { + const maybe_triple = b.option( []const u8, "target", "The CPU architecture, OS, and ABI to build for", ); - const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract"); + const mcpu = b.option([]const u8, "cpu", "Target CPU features to add or subtract"); if (maybe_triple == null and mcpu == null) { return args.default_target; @@ -1218,8 +1221,8 @@ pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) Cros const triple = maybe_triple orelse "native"; - var diags: CrossTarget.ParseOptions.Diagnostics = .{}; - const selected_target = CrossTarget.parse(.{ + var diags: Target.Query.ParseOptions.Diagnostics = .{}; + const selected_target = Target.Query.parse(.{ .arch_os_abi = triple, .cpu_features = mcpu, .diagnostics = &diags, @@ -1232,7 +1235,7 @@ pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) Cros for (diags.arch.?.allCpuModels()) |cpu| { log.err(" {s}", .{cpu.name}); } - self.markInvalidUserInput(); + b.markInvalidUserInput(); return args.default_target; }, error.UnknownCpuFeature => { @@ -1247,7 +1250,7 @@ pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) Cros for (diags.arch.?.allFeaturesList()) |feature| { log.err(" {s}: {s}", .{ feature.name, feature.description }); } - self.markInvalidUserInput(); + b.markInvalidUserInput(); return args.default_target; }, error.UnknownOperatingSystem => { @@ -1256,83 +1259,38 @@ pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) Cros \\Available operating systems: \\ , .{diags.os_name.?}); - inline for (std.meta.fields(std.Target.Os.Tag)) |field| { + inline for (std.meta.fields(Target.Os.Tag)) |field| { log.err(" {s}", .{field.name}); } - self.markInvalidUserInput(); + b.markInvalidUserInput(); return args.default_target; }, else => |e| { log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) }); - self.markInvalidUserInput(); + b.markInvalidUserInput(); return args.default_target; }, }; - const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch @panic("OOM"); - - if (args.whitelist) |list| whitelist_check: { - // Make sure it's a match of one of the list. - var mismatch_triple = true; - var mismatch_cpu_features = true; - var whitelist_item = CrossTarget{}; - for (list) |t| { - mismatch_cpu_features = true; - mismatch_triple = true; - - const t_triple = t.zigTriple(self.allocator) catch @panic("OOM"); - if (mem.eql(u8, t_triple, selected_canonicalized_triple)) { - mismatch_triple = false; - whitelist_item = t; - if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) { - mismatch_cpu_features = false; - break :whitelist_check; - } else { - break; - } - } - } - if (mismatch_triple) { - log.err("Chosen target '{s}' does not match one of the supported targets:", .{ - selected_canonicalized_triple, - }); - for (list) |t| { - const t_triple = t.zigTriple(self.allocator) catch @panic("OOM"); - log.err(" {s}", .{t_triple}); - } - } else { - assert(mismatch_cpu_features); - const whitelist_cpu = whitelist_item.getCpu(); - const selected_cpu = selected_target.getCpu(); - log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{ - selected_cpu.model.name, - }); - log.err(" Supported feature Set: ", .{}); - const all_features = whitelist_cpu.arch.allFeaturesList(); - var populated_cpu_features = whitelist_cpu.model.features; - populated_cpu_features.populateDependencies(all_features); - for (all_features, 0..) |feature, i_usize| { - const i = @as(std.Target.Cpu.Feature.Set.Index, @intCast(i_usize)); - const in_cpu_set = populated_cpu_features.isEnabled(i); - if (in_cpu_set) { - log.err("{s} ", .{feature.name}); - } - } - log.err(" Remove: ", .{}); - for (all_features, 0..) |feature, i_usize| { - const i = @as(std.Target.Cpu.Feature.Set.Index, @intCast(i_usize)); - const in_cpu_set = populated_cpu_features.isEnabled(i); - const in_actual_set = selected_cpu.features.isEnabled(i); - if (in_actual_set and !in_cpu_set) { - log.err("{s} ", .{feature.name}); - } - } - } - self.markInvalidUserInput(); - return args.default_target; + const whitelist = args.whitelist orelse return selected_target; + + // Make sure it's a match of one of the list. + for (whitelist) |q| { + if (q.eql(selected_target)) + return selected_target; } - return selected_target; + for (whitelist) |q| { + log.info("allowed target: -Dtarget={s} -Dcpu={s}", .{ + q.zigTriple(b.allocator) catch @panic("OOM"), + q.serializeCpuAlloc(b.allocator) catch @panic("OOM"), + }); + } + log.err("chosen target '{s}' does not match one of the allowed targets", .{ + selected_target.zigTriple(b.allocator) catch @panic("OOM"), + }); + b.markInvalidUserInput(); + return args.default_target; } pub fn addUserInputOption(self: *Build, name_raw: []const u8, value_raw: []const u8) !bool { @@ -1412,7 +1370,7 @@ pub fn addUserInputFlag(self: *Build, name_raw: []const u8) !bool { fn typeToEnum(comptime T: type) TypeId { return switch (T) { - Step.Compile.BuildId => .build_id, + std.zig.BuildId => .build_id, else => return switch (@typeInfo(T)) { .Int => .int, .Float => .float, @@ -1480,7 +1438,7 @@ pub fn installFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step); } -pub fn installDirectory(self: *Build, options: InstallDirectoryOptions) void { +pub fn installDirectory(self: *Build, options: Step.InstallDir.Options) void { self.getInstallStep().dependOn(&self.addInstallDirectory(options).step); } @@ -1526,7 +1484,7 @@ pub fn addInstallFileWithDir( return Step.InstallFile.create(self, source.dupe(self), install_dir, dest_rel_path); } -pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *Step.InstallDir { +pub fn addInstallDirectory(self: *Build, options: Step.InstallDir.Options) *Step.InstallDir { return Step.InstallDir.create(self, options); } @@ -1583,7 +1541,7 @@ pub fn fmt(self: *Build, comptime format: []const u8, args: anytype) []u8 { pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []const u8) ![]const u8 { // TODO report error for ambiguous situations - const exe_extension = @as(CrossTarget, .{}).exeFileExt(); + const exe_extension = @as(Target.Query, .{}).exeFileExt(); for (self.search_prefixes.items) |search_prefix| { for (names) |name| { if (fs.path.isAbsolute(name)) { @@ -1885,15 +1843,6 @@ pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void { } } -pub const Module = struct { - builder: *Build, - /// This could either be a generated file, in which case the module - /// contains exactly one file, or it could be a path to the root source - /// file of directory of files which constitute the module. - source_file: LazyPath, - dependencies: std.StringArrayHashMap(*Module), -}; - /// A file that is generated by a build step. /// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic. pub const GeneratedFile = struct { @@ -2038,34 +1987,6 @@ pub fn dumpBadGetPathHelp( tty_config.setColor(w, .reset) catch {}; } -/// Allocates a new string for assigning a value to a named macro. -/// If the value is omitted, it is set to 1. -/// `name` and `value` need not live longer than the function call. -pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 { - var macro = allocator.alloc( - u8, - name.len + if (value) |value_slice| value_slice.len + 1 else 0, - ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable; - @memcpy(macro[0..name.len], name); - if (value) |value_slice| { - macro[name.len] = '='; - @memcpy(macro[name.len + 1 ..][0..value_slice.len], value_slice); - } - return macro; -} - -pub const VcpkgRoot = union(VcpkgRootStatus) { - unattempted: void, - not_found: void, - found: []const u8, -}; - -pub const VcpkgRootStatus = enum { - unattempted, - not_found, - found, -}; - pub const InstallDir = union(enum) { prefix: void, lib: void, @@ -2097,34 +2018,6 @@ pub const InstalledFile = struct { } }; -pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 { - // TODO this logic can disappear if cpu model + features becomes part of the target triple - const all_features = cpu.arch.allFeaturesList(); - var populated_cpu_features = cpu.model.features; - populated_cpu_features.populateDependencies(all_features); - - if (populated_cpu_features.eql(cpu.features)) { - // The CPU name alone is sufficient. - return cpu.model.name; - } else { - var mcpu_buffer = ArrayList(u8).init(allocator); - try mcpu_buffer.appendSlice(cpu.model.name); - - for (all_features, 0..) |feature, i_usize| { - const i = @as(std.Target.Cpu.Feature.Set.Index, @intCast(i_usize)); - const in_cpu_set = populated_cpu_features.isEnabled(i); - const in_actual_set = cpu.features.isEnabled(i); - if (in_cpu_set and !in_actual_set) { - try mcpu_buffer.writer().print("-{s}", .{feature.name}); - } else if (!in_cpu_set and in_actual_set) { - try mcpu_buffer.writer().print("+{s}", .{feature.name}); - } - } - - return try mcpu_buffer.toOwnedSlice(); - } -} - /// This function is intended to be called in the `configure` phase only. /// It returns an absolute directory path, which is potentially going to be a /// source of API breakage in the future, so keep that in mind when using this @@ -2155,6 +2048,33 @@ pub fn hex64(x: u64) [16]u8 { return result; } +/// A pair of target query and fully resolved target. +/// This type is generally required by build system API that need to be given a +/// target. The query is kept because the Zig toolchain needs to know which parts +/// of the target are "native". This can apply to the CPU, the OS, or even the ABI. +pub const ResolvedTarget = struct { + query: Target.Query, + result: Target, +}; + +/// Converts a target query into a fully resolved target that can be passed to +/// various parts of the API. +pub fn resolveTargetQuery(b: *Build, query: Target.Query) ResolvedTarget { + // This context will likely be required in the future when the target is + // resolved via a WASI API or via the build protocol. + _ = b; + + return .{ + .query = query, + .result = std.zig.system.resolveTargetQuery(query) catch + @panic("unable to resolve target query"), + }; +} + +pub fn wantSharedLibSymLinks(target: Target) bool { + return target.os.tag != .windows; +} + test { _ = Cache; _ = Step; diff --git a/lib/std/Build/Cache.zig b/lib/std/Build/Cache.zig index 283a3d1f5f72..18c1ffa07a2a 100644 --- a/lib/std/Build/Cache.zig +++ b/lib/std/Build/Cache.zig @@ -270,7 +270,7 @@ pub const HashHelper = struct { .none => {}, } }, - std.Build.Step.Compile.BuildId => switch (x) { + std.zig.BuildId => switch (x) { .none, .fast, .uuid, .sha1, .md5 => hh.add(std.meta.activeTag(x)), .hexstring => |hex_string| hh.addBytes(hex_string.toSlice()), }, @@ -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 new file mode 100644 index 000000000000..ce17d655a028 --- /dev/null +++ b/lib/std/Build/Module.zig @@ -0,0 +1,753 @@ +/// The one responsible for creating this module. +owner: *std.Build, +/// Tracks the set of steps that depend on this `Module`. This ensures that +/// when making this `Module` depend on other `Module` objects and `Step` +/// objects, respective `Step` dependencies can be added. +depending_steps: std.AutoArrayHashMapUnmanaged(*Step.Compile, void), +root_source_file: ?LazyPath, +/// The modules that are mapped into this module's import table. +/// Use `addImport` rather than modifying this field directly in order to +/// maintain step dependency edges. +import_table: std.StringArrayHashMapUnmanaged(*Module), + +resolved_target: ?std.Build.ResolvedTarget = null, +optimize: ?std.builtin.OptimizeMode = null, +dwarf_format: ?std.dwarf.Format, + +c_macros: std.ArrayListUnmanaged([]const u8), +include_dirs: std.ArrayListUnmanaged(IncludeDir), +lib_paths: std.ArrayListUnmanaged(LazyPath), +rpaths: std.ArrayListUnmanaged(RPath), +frameworks: std.StringArrayHashMapUnmanaged(LinkFrameworkOptions), +c_std: std.Build.CStd, +link_objects: std.ArrayListUnmanaged(LinkObject), + +strip: ?bool, +unwind_tables: ?bool, +single_threaded: ?bool, +stack_protector: ?bool, +stack_check: ?bool, +sanitize_c: ?bool, +sanitize_thread: ?bool, +code_model: std.builtin.CodeModel, +valgrind: ?bool, +pic: ?bool, +red_zone: ?bool, +omit_frame_pointer: ?bool, +error_tracing: ?bool, +link_libc: ?bool, +link_libcpp: ?bool, + +/// Symbols to be exported when compiling to WebAssembly. +export_symbol_names: []const []const u8 = &.{}, + +pub const RPath = union(enum) { + lazy_path: LazyPath, + special: []const u8, +}; + +pub const LinkObject = union(enum) { + static_path: LazyPath, + other_step: *Step.Compile, + system_lib: SystemLib, + assembly_file: LazyPath, + c_source_file: *CSourceFile, + c_source_files: *CSourceFiles, + win32_resource_file: *RcSourceFile, +}; + +pub const SystemLib = struct { + name: []const u8, + needed: bool, + weak: bool, + use_pkg_config: UsePkgConfig, + preferred_link_mode: std.builtin.LinkMode, + search_strategy: SystemLib.SearchStrategy, + + pub const UsePkgConfig = enum { + /// Don't use pkg-config, just pass -lfoo where foo is name. + no, + /// Try to get information on how to link the library from pkg-config. + /// If that fails, fall back to passing -lfoo where foo is name. + yes, + /// Try to get information on how to link the library from pkg-config. + /// If that fails, error out. + force, + }; + + pub const SearchStrategy = enum { paths_first, mode_first, no_fallback }; +}; + +pub const CSourceFiles = struct { + dependency: ?*std.Build.Dependency, + /// If `dependency` is not null relative to it, + /// else relative to the build root. + files: []const []const u8, + flags: []const []const u8, +}; + +pub const CSourceFile = struct { + file: LazyPath, + flags: []const []const u8 = &.{}, + + pub fn dupe(self: CSourceFile, b: *std.Build) CSourceFile { + return .{ + .file = self.file.dupe(b), + .flags = b.dupeStrings(self.flags), + }; + } +}; + +pub const RcSourceFile = struct { + file: LazyPath, + /// Any option that rc.exe accepts will work here, with the exception of: + /// - `/fo`: The output filename is set by the build system + /// - `/p`: Only running the preprocessor is not supported in this context + /// - `/:no-preprocess` (non-standard option): Not supported in this context + /// - Any MUI-related option + /// https://learn.microsoft.com/en-us/windows/win32/menurc/using-rc-the-rc-command-line- + /// + /// Implicitly defined options: + /// /x (ignore the INCLUDE environment variable) + /// /D_DEBUG or /DNDEBUG depending on the optimization mode + flags: []const []const u8 = &.{}, + + pub fn dupe(self: RcSourceFile, b: *std.Build) RcSourceFile { + return .{ + .file = self.file.dupe(b), + .flags = b.dupeStrings(self.flags), + }; + } +}; + +pub const IncludeDir = union(enum) { + path: LazyPath, + path_system: LazyPath, + path_after: LazyPath, + framework_path: LazyPath, + framework_path_system: LazyPath, + other_step: *Step.Compile, + config_header_step: *Step.ConfigHeader, +}; + +pub const LinkFrameworkOptions = struct { + needed: bool = false, + weak: bool = false, +}; + +/// Unspecified options here will be inherited from parent `Module` when +/// inserted into an import table. +pub const CreateOptions = struct { + /// This could either be a generated file, in which case the module + /// contains exactly one file, or it could be a path to the root source + /// file of directory of files which constitute the module. + /// If `null`, it means this module is made up of only `link_objects`. + root_source_file: ?LazyPath = null, + + /// The table of other modules that this module can access via `@import`. + /// Imports are allowed to be cyclical, so this table can be added to after + /// the `Module` is created via `addImport`. + imports: []const Import = &.{}, + + target: ?std.Build.ResolvedTarget = null, + optimize: ?std.builtin.OptimizeMode = null, + + /// `true` requires a compilation that includes this Module to link libc. + /// `false` causes a build failure if a compilation that includes this Module would link libc. + /// `null` neither requires nor prevents libc from being linked. + link_libc: ?bool = null, + /// `true` requires a compilation that includes this Module to link libc++. + /// `false` causes a build failure if a compilation that includes this Module would link libc++. + /// `null` neither requires nor prevents libc++ from being linked. + link_libcpp: ?bool = null, + single_threaded: ?bool = null, + strip: ?bool = null, + unwind_tables: ?bool = null, + dwarf_format: ?std.dwarf.Format = null, + c_std: std.Build.CStd = .C99, + code_model: std.builtin.CodeModel = .default, + stack_protector: ?bool = null, + stack_check: ?bool = null, + sanitize_c: ?bool = null, + sanitize_thread: ?bool = null, + /// Whether to emit machine code that integrates with Valgrind. + valgrind: ?bool = null, + /// Position Independent Code + pic: ?bool = null, + red_zone: ?bool = null, + /// 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 { + name: []const u8, + module: *Module, +}; + +pub fn init(m: *Module, owner: *std.Build, options: CreateOptions, compile: ?*Step.Compile) void { + const allocator = owner.allocator; + + m.* = .{ + .owner = owner, + .depending_steps = .{}, + .root_source_file = if (options.root_source_file) |lp| lp.dupe(owner) else null, + .import_table = .{}, + .resolved_target = options.target, + .optimize = options.optimize, + .link_libc = options.link_libc, + .link_libcpp = options.link_libcpp, + .dwarf_format = options.dwarf_format, + .c_macros = .{}, + .include_dirs = .{}, + .lib_paths = .{}, + .rpaths = .{}, + .frameworks = .{}, + .c_std = options.c_std, + .link_objects = .{}, + .strip = options.strip, + .unwind_tables = options.unwind_tables, + .single_threaded = options.single_threaded, + .stack_protector = options.stack_protector, + .stack_check = options.stack_check, + .sanitize_c = options.sanitize_c, + .sanitize_thread = options.sanitize_thread, + .code_model = options.code_model, + .valgrind = options.valgrind, + .pic = options.pic, + .red_zone = options.red_zone, + .omit_frame_pointer = options.omit_frame_pointer, + .error_tracing = options.error_tracing, + .export_symbol_names = &.{}, + }; + + m.import_table.ensureUnusedCapacity(allocator, options.imports.len) catch @panic("OOM"); + for (options.imports) |dep| { + m.import_table.putAssumeCapacity(dep.name, dep.module); + } + + if (compile) |c| { + m.depending_steps.put(allocator, c, {}) catch @panic("OOM"); + } + + // This logic accesses `depending_steps` which was just modified above. + var it = m.iterateDependencies(null, false); + while (it.next()) |item| addShallowDependencies(m, item.module); +} + +pub fn create(owner: *std.Build, options: CreateOptions) *Module { + const m = owner.allocator.create(Module) catch @panic("OOM"); + m.init(owner, options, null); + return m; +} + +/// Adds an existing module to be used with `@import`. +pub fn addImport(m: *Module, name: []const u8, module: *Module) void { + const b = m.owner; + m.import_table.put(b.allocator, b.dupe(name), module) catch @panic("OOM"); + + var it = module.iterateDependencies(null, false); + while (it.next()) |item| addShallowDependencies(m, item.module); +} + +/// Creates step dependencies and updates `depending_steps` of `dependee` so that +/// subsequent calls to `addImport` on `dependee` will additionally create step +/// dependencies on `m`'s `depending_steps`. +fn addShallowDependencies(m: *Module, dependee: *Module) void { + if (dependee.root_source_file) |lazy_path| addLazyPathDependencies(m, dependee, lazy_path); + for (dependee.lib_paths.items) |lib_path| addLazyPathDependencies(m, dependee, lib_path); + for (dependee.rpaths.items) |rpath| switch (rpath) { + .lazy_path => |lp| addLazyPathDependencies(m, dependee, lp), + .special => {}, + }; + + for (dependee.link_objects.items) |link_object| switch (link_object) { + .other_step => |compile| addStepDependencies(m, dependee, &compile.step), + + .static_path, + .assembly_file, + => |lp| addLazyPathDependencies(m, dependee, lp), + + .c_source_file => |x| addLazyPathDependencies(m, dependee, x.file), + .win32_resource_file => |x| addLazyPathDependencies(m, dependee, x.file), + + .c_source_files, + .system_lib, + => {}, + }; +} + +fn addLazyPathDependencies(m: *Module, module: *Module, lazy_path: LazyPath) void { + addLazyPathDependenciesOnly(m, lazy_path); + if (m != module) { + for (m.depending_steps.keys()) |compile| { + module.depending_steps.put(m.owner.allocator, compile, {}) catch @panic("OOM"); + } + } +} + +fn addLazyPathDependenciesOnly(m: *Module, lazy_path: LazyPath) void { + for (m.depending_steps.keys()) |compile| { + lazy_path.addStepDependencies(&compile.step); + } +} + +fn addStepDependencies(m: *Module, module: *Module, dependee: *Step) void { + addStepDependenciesOnly(m, dependee); + if (m != module) { + for (m.depending_steps.keys()) |compile| { + module.depending_steps.put(m.owner.allocator, compile, {}) catch @panic("OOM"); + } + } +} + +fn addStepDependenciesOnly(m: *Module, dependee: *Step) void { + for (m.depending_steps.keys()) |compile| { + compile.step.dependOn(dependee); + } +} + +/// Creates a new module and adds it to be used with `@import`. +pub fn addAnonymousImport(m: *Module, name: []const u8, options: CreateOptions) void { + const module = create(m.owner, options); + return addImport(m, name, module); +} + +pub fn addOptions(m: *Module, module_name: []const u8, options: *Step.Options) void { + addImport(m, module_name, options.createModule()); +} + +pub const DependencyIterator = struct { + allocator: std.mem.Allocator, + index: usize, + set: std.AutoArrayHashMapUnmanaged(Key, []const u8), + chase_dyn_libs: bool, + + pub const Key = struct { + /// The compilation that contains the `Module`. Note that a `Module` might be + /// used by more than one compilation. + compile: ?*Step.Compile, + module: *Module, + }; + + pub const Item = struct { + /// The compilation that contains the `Module`. Note that a `Module` might be + /// used by more than one compilation. + compile: ?*Step.Compile, + module: *Module, + name: []const u8, + }; + + pub fn deinit(it: *DependencyIterator) void { + it.set.deinit(it.allocator); + it.* = undefined; + } + + pub fn next(it: *DependencyIterator) ?Item { + if (it.index >= it.set.count()) { + it.set.clearAndFree(it.allocator); + return null; + } + const key = it.set.keys()[it.index]; + const name = it.set.values()[it.index]; + it.index += 1; + const module = key.module; + it.set.ensureUnusedCapacity(it.allocator, module.import_table.count()) catch + @panic("OOM"); + for (module.import_table.keys(), module.import_table.values()) |dep_name, dep| { + it.set.putAssumeCapacity(.{ + .module = dep, + .compile = key.compile, + }, dep_name); + } + + if (key.compile != null) { + for (module.link_objects.items) |link_object| switch (link_object) { + .other_step => |compile| { + if (!it.chase_dyn_libs and compile.isDynamicLibrary()) continue; + + it.set.put(it.allocator, .{ + .module = &compile.root_module, + .compile = compile, + }, "root") catch @panic("OOM"); + }, + else => {}, + }; + } + + return .{ + .compile = key.compile, + .module = key.module, + .name = name, + }; + } +}; + +pub fn iterateDependencies( + m: *Module, + chase_steps: ?*Step.Compile, + chase_dyn_libs: bool, +) DependencyIterator { + var it: DependencyIterator = .{ + .allocator = m.owner.allocator, + .index = 0, + .set = .{}, + .chase_dyn_libs = chase_dyn_libs, + }; + it.set.ensureUnusedCapacity(m.owner.allocator, m.import_table.count() + 1) catch @panic("OOM"); + it.set.putAssumeCapacity(.{ + .module = m, + .compile = chase_steps, + }, "root"); + return it; +} + +pub const LinkSystemLibraryOptions = struct { + needed: bool = false, + weak: bool = false, + use_pkg_config: SystemLib.UsePkgConfig = .yes, + preferred_link_mode: std.builtin.LinkMode = .Dynamic, + search_strategy: SystemLib.SearchStrategy = .paths_first, +}; + +pub fn linkSystemLibrary( + m: *Module, + name: []const u8, + options: LinkSystemLibraryOptions, +) void { + const b = m.owner; + + const target = m.requireKnownTarget(); + if (target.is_libc_lib_name(name)) { + m.link_libc = true; + return; + } + if (target.is_libcpp_lib_name(name)) { + m.link_libcpp = true; + return; + } + + m.link_objects.append(b.allocator, .{ + .system_lib = .{ + .name = b.dupe(name), + .needed = options.needed, + .weak = options.weak, + .use_pkg_config = options.use_pkg_config, + .preferred_link_mode = options.preferred_link_mode, + .search_strategy = options.search_strategy, + }, + }) catch @panic("OOM"); +} + +pub fn linkFramework(m: *Module, name: []const u8, options: LinkFrameworkOptions) void { + const b = m.owner; + m.frameworks.put(b.allocator, b.dupe(name), options) catch @panic("OOM"); +} + +pub const AddCSourceFilesOptions = struct { + /// When provided, `files` are relative to `dependency` rather than the + /// package that owns the `Compile` step. + dependency: ?*std.Build.Dependency = null, + files: []const []const u8, + flags: []const []const u8 = &.{}, +}; + +/// Handy when you have many C/C++ source files and want them all to have the same flags. +pub fn addCSourceFiles(m: *Module, options: AddCSourceFilesOptions) void { + const b = m.owner; + const allocator = b.allocator; + const c_source_files = allocator.create(CSourceFiles) catch @panic("OOM"); + c_source_files.* = .{ + .dependency = options.dependency, + .files = b.dupeStrings(options.files), + .flags = b.dupeStrings(options.flags), + }; + m.link_objects.append(allocator, .{ .c_source_files = c_source_files }) catch @panic("OOM"); +} + +pub fn addCSourceFile(m: *Module, source: CSourceFile) void { + const b = m.owner; + const allocator = b.allocator; + const c_source_file = allocator.create(CSourceFile) catch @panic("OOM"); + c_source_file.* = source.dupe(b); + m.link_objects.append(allocator, .{ .c_source_file = c_source_file }) catch @panic("OOM"); + addLazyPathDependenciesOnly(m, source.file); +} + +/// Resource files must have the extension `.rc`. +/// Can be called regardless of target. The .rc file will be ignored +/// if the target object format does not support embedded resources. +pub fn addWin32ResourceFile(m: *Module, source: RcSourceFile) void { + const b = m.owner; + const allocator = b.allocator; + const target = m.requireKnownTarget(); + // Only the PE/COFF format has a Resource Table, so for any other target + // the resource file is ignored. + if (target.ofmt != .coff) return; + + const rc_source_file = allocator.create(RcSourceFile) catch @panic("OOM"); + rc_source_file.* = source.dupe(b); + m.link_objects.append(allocator, .{ .win32_resource_file = rc_source_file }) catch @panic("OOM"); + addLazyPathDependenciesOnly(m, source.file); +} + +pub fn addAssemblyFile(m: *Module, source: LazyPath) void { + const b = m.owner; + m.link_objects.append(b.allocator, .{ .assembly_file = source.dupe(b) }) catch @panic("OOM"); + addLazyPathDependenciesOnly(m, source); +} + +pub fn addObjectFile(m: *Module, object: LazyPath) void { + const b = m.owner; + m.link_objects.append(b.allocator, .{ .static_path = object.dupe(b) }) catch @panic("OOM"); + addLazyPathDependenciesOnly(m, object); +} + +pub fn addObject(m: *Module, object: *Step.Compile) void { + assert(object.kind == .obj); + m.linkLibraryOrObject(object); +} + +pub fn linkLibrary(m: *Module, library: *Step.Compile) void { + assert(library.kind == .lib); + m.linkLibraryOrObject(library); +} + +pub fn addAfterIncludePath(m: *Module, lazy_path: LazyPath) void { + const b = m.owner; + m.include_dirs.append(b.allocator, .{ .path_after = lazy_path.dupe(b) }) catch @panic("OOM"); + addLazyPathDependenciesOnly(m, lazy_path); +} + +pub fn addSystemIncludePath(m: *Module, lazy_path: LazyPath) void { + const b = m.owner; + m.include_dirs.append(b.allocator, .{ .path_system = lazy_path.dupe(b) }) catch @panic("OOM"); + addLazyPathDependenciesOnly(m, lazy_path); +} + +pub fn addIncludePath(m: *Module, lazy_path: LazyPath) void { + const b = m.owner; + m.include_dirs.append(b.allocator, .{ .path = lazy_path.dupe(b) }) catch @panic("OOM"); + addLazyPathDependenciesOnly(m, lazy_path); +} + +pub fn addConfigHeader(m: *Module, config_header: *Step.ConfigHeader) void { + const allocator = m.owner.allocator; + m.include_dirs.append(allocator, .{ .config_header_step = config_header }) catch @panic("OOM"); + addStepDependenciesOnly(m, &config_header.step); +} + +pub fn addSystemFrameworkPath(m: *Module, directory_path: LazyPath) void { + const b = m.owner; + m.include_dirs.append(b.allocator, .{ .framework_path_system = directory_path.dupe(b) }) catch + @panic("OOM"); + addLazyPathDependenciesOnly(m, directory_path); +} + +pub fn addFrameworkPath(m: *Module, directory_path: LazyPath) void { + const b = m.owner; + m.include_dirs.append(b.allocator, .{ .framework_path = directory_path.dupe(b) }) catch + @panic("OOM"); + addLazyPathDependenciesOnly(m, directory_path); +} + +pub fn addLibraryPath(m: *Module, directory_path: LazyPath) void { + const b = m.owner; + m.lib_paths.append(b.allocator, directory_path.dupe(b)) catch @panic("OOM"); + addLazyPathDependenciesOnly(m, directory_path); +} + +pub fn addRPath(m: *Module, directory_path: LazyPath) void { + const b = m.owner; + switch (directory_path) { + .path, .cwd_relative => |path| { + // TODO: remove this check after people upgrade and stop expecting it to work + if (std.mem.startsWith(u8, path, "@executable_path") or + std.mem.startsWith(u8, path, "@loader_path")) + { + @panic("this function is for adding directory paths. It does not support special rpaths. use addRPathSpecial for that."); + } + }, + else => {}, + } + m.rpaths.append(b.allocator, .{ .lazy_path = directory_path.dupe(b) }) catch @panic("OOM"); + addLazyPathDependenciesOnly(m, directory_path); +} + +pub fn addRPathSpecial(m: *Module, bytes: []const u8) void { + const b = m.owner; + m.rpaths.append(b.allocator, .{ .special = b.dupe(bytes) }) catch @panic("OOM"); +} + +/// Equvialent to the following C code, applied to all C source files owned by +/// this `Module`: +/// ```c +/// #define name value +/// ``` +/// `name` and `value` need not live longer than the function call. +pub fn addCMacro(m: *Module, name: []const u8, value: []const u8) void { + const b = m.owner; + m.c_macros.append(b.allocator, b.fmt("-D{s}={s}", .{ name, value })) catch @panic("OOM"); +} + +pub fn appendZigProcessFlags( + m: *Module, + zig_args: *std.ArrayList([]const u8), + asking_step: ?*Step, +) !void { + const b = m.owner; + + try addFlag(zig_args, m.strip, "-fstrip", "-fno-strip"); + try addFlag(zig_args, m.unwind_tables, "-funwind-tables", "-fno-unwind-tables"); + try addFlag(zig_args, m.single_threaded, "-fsingle-threaded", "-fno-single-threaded"); + 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"); + try addFlag(zig_args, m.pic, "-fPIC", "-fno-PIC"); + try addFlag(zig_args, m.red_zone, "-mred-zone", "-mno-red-zone"); + + if (m.dwarf_format) |dwarf_format| { + try zig_args.append(switch (dwarf_format) { + .@"32" => "-gdwarf32", + .@"64" => "-gdwarf64", + }); + } + + try zig_args.ensureUnusedCapacity(1); + if (m.optimize) |optimize| switch (optimize) { + .Debug => zig_args.appendAssumeCapacity("-ODebug"), + .ReleaseSmall => zig_args.appendAssumeCapacity("-OReleaseSmall"), + .ReleaseFast => zig_args.appendAssumeCapacity("-OReleaseFast"), + .ReleaseSafe => zig_args.appendAssumeCapacity("-OReleaseSafe"), + }; + + if (m.code_model != .default) { + try zig_args.append("-mcmodel"); + try zig_args.append(@tagName(m.code_model)); + } + + if (m.resolved_target) |*target| { + // Communicate the query via CLI since it's more compact. + if (!target.query.isNative()) { + try zig_args.appendSlice(&.{ + "-target", try target.query.zigTriple(b.allocator), + "-mcpu", try target.query.serializeCpuAlloc(b.allocator), + }); + + if (target.query.dynamic_linker.get()) |dynamic_linker| { + try zig_args.append("--dynamic-linker"); + try zig_args.append(dynamic_linker); + } + } + } + + for (m.export_symbol_names) |symbol_name| { + try zig_args.append(b.fmt("--export={s}", .{symbol_name})); + } + + for (m.include_dirs.items) |include_dir| { + switch (include_dir) { + .path => |include_path| { + try zig_args.append("-I"); + try zig_args.append(include_path.getPath(b)); + }, + .path_system => |include_path| { + try zig_args.append("-isystem"); + try zig_args.append(include_path.getPath(b)); + }, + .path_after => |include_path| { + try zig_args.append("-idirafter"); + try zig_args.append(include_path.getPath(b)); + }, + .framework_path => |include_path| { + try zig_args.append("-F"); + try zig_args.append(include_path.getPath2(b, asking_step)); + }, + .framework_path_system => |include_path| { + try zig_args.append("-iframework"); + try zig_args.append(include_path.getPath2(b, asking_step)); + }, + .other_step => |other| { + if (other.generated_h) |header| { + try zig_args.append("-isystem"); + try zig_args.append(std.fs.path.dirname(header.path.?).?); + } + if (other.installed_headers.items.len > 0) { + try zig_args.append("-I"); + try zig_args.append(b.pathJoin(&.{ + other.step.owner.install_prefix, "include", + })); + } + }, + .config_header_step => |config_header| { + const full_file_path = config_header.output_file.path.?; + const header_dir_path = full_file_path[0 .. full_file_path.len - config_header.include_path.len]; + try zig_args.appendSlice(&.{ "-I", header_dir_path }); + }, + } + } + + try zig_args.appendSlice(m.c_macros.items); + + try zig_args.ensureUnusedCapacity(2 * m.lib_paths.items.len); + for (m.lib_paths.items) |lib_path| { + zig_args.appendAssumeCapacity("-L"); + zig_args.appendAssumeCapacity(lib_path.getPath2(b, asking_step)); + } + + try zig_args.ensureUnusedCapacity(2 * m.rpaths.items.len); + for (m.rpaths.items) |rpath| switch (rpath) { + .lazy_path => |lp| { + zig_args.appendAssumeCapacity("-rpath"); + zig_args.appendAssumeCapacity(lp.getPath2(b, asking_step)); + }, + .special => |bytes| { + zig_args.appendAssumeCapacity("-rpath"); + zig_args.appendAssumeCapacity(bytes); + }, + }; +} + +fn addFlag( + args: *std.ArrayList([]const u8), + opt: ?bool, + then_name: []const u8, + else_name: []const u8, +) !void { + const cond = opt orelse return; + return args.append(if (cond) then_name else else_name); +} + +fn linkLibraryOrObject(m: *Module, other: *Step.Compile) void { + const allocator = m.owner.allocator; + _ = other.getEmittedBin(); // Indicate there is a dependency on the outputted binary. + addStepDependenciesOnly(m, &other.step); + + if (other.rootModuleTarget().os.tag == .windows and other.isDynamicLibrary()) { + _ = other.getEmittedImplib(); // Indicate dependency on the outputted implib. + } + + m.link_objects.append(allocator, .{ .other_step = other }) catch @panic("OOM"); + m.include_dirs.append(allocator, .{ .other_step = other }) catch @panic("OOM"); + + for (other.installed_headers.items) |install_step| { + addStepDependenciesOnly(m, install_step); + } +} + +fn requireKnownTarget(m: *Module) std.Target { + const resolved_target = m.resolved_target orelse + @panic("this API requires the Module to be created with a known 'target' field"); + return resolved_target.result; +} + +const Module = @This(); +const std = @import("std"); +const assert = std.debug.assert; +const LazyPath = std.Build.LazyPath; +const Step = std.Build.Step; diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index 3be9cdc83cdb..c5eb1f776bbd 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/lib/std/Build/Step/CheckObject.zig @@ -44,11 +44,11 @@ pub fn create( const SearchPhrase = struct { string: []const u8, - file_source: ?std.Build.LazyPath = null, + lazy_path: ?std.Build.LazyPath = null, fn resolve(phrase: SearchPhrase, b: *std.Build, step: *Step) []const u8 { - const file_source = phrase.file_source orelse return phrase.string; - return b.fmt("{s} {s}", .{ phrase.string, file_source.getPath2(b, step) }); + const lazy_path = phrase.lazy_path orelse return phrase.string; + return b.fmt("{s} {s}", .{ phrase.string, lazy_path.getPath2(b, step) }); } }; @@ -321,14 +321,14 @@ pub fn checkExact(self: *CheckObject, phrase: []const u8) void { /// Like `checkExact()` but takes an additional argument `LazyPath` which will be /// resolved to a full search query in `make()`. -pub fn checkExactPath(self: *CheckObject, phrase: []const u8, file_source: std.Build.LazyPath) void { - self.checkExactInner(phrase, file_source); +pub fn checkExactPath(self: *CheckObject, phrase: []const u8, lazy_path: std.Build.LazyPath) void { + self.checkExactInner(phrase, lazy_path); } -fn checkExactInner(self: *CheckObject, phrase: []const u8, file_source: ?std.Build.LazyPath) void { +fn checkExactInner(self: *CheckObject, phrase: []const u8, lazy_path: ?std.Build.LazyPath) void { assert(self.checks.items.len > 0); const last = &self.checks.items[self.checks.items.len - 1]; - last.exact(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source }); + last.exact(.{ .string = self.step.owner.dupe(phrase), .lazy_path = lazy_path }); } /// Adds a fuzzy match phrase to the latest created Check. @@ -336,16 +336,20 @@ pub fn checkContains(self: *CheckObject, phrase: []const u8) void { self.checkContainsInner(phrase, null); } -/// Like `checkContains()` but takes an additional argument `FileSource` which will be +/// Like `checkContains()` but takes an additional argument `lazy_path` which will be /// resolved to a full search query in `make()`. -pub fn checkContainsPath(self: *CheckObject, phrase: []const u8, file_source: std.Build.LazyPath) void { - self.checkContainsInner(phrase, file_source); +pub fn checkContainsPath( + self: *CheckObject, + phrase: []const u8, + lazy_path: std.Build.LazyPath, +) void { + self.checkContainsInner(phrase, lazy_path); } -fn checkContainsInner(self: *CheckObject, phrase: []const u8, file_source: ?std.Build.FileSource) void { +fn checkContainsInner(self: *CheckObject, phrase: []const u8, lazy_path: ?std.Build.LazyPath) void { assert(self.checks.items.len > 0); const last = &self.checks.items[self.checks.items.len - 1]; - last.contains(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source }); + last.contains(.{ .string = self.step.owner.dupe(phrase), .lazy_path = lazy_path }); } /// Adds an exact match phrase with variable extractor to the latest created Check. @@ -353,16 +357,16 @@ pub fn checkExtract(self: *CheckObject, phrase: []const u8) void { self.checkExtractInner(phrase, null); } -/// Like `checkExtract()` but takes an additional argument `FileSource` which will be +/// Like `checkExtract()` but takes an additional argument `LazyPath` which will be /// resolved to a full search query in `make()`. -pub fn checkExtractFileSource(self: *CheckObject, phrase: []const u8, file_source: std.Build.FileSource) void { - self.checkExtractInner(phrase, file_source); +pub fn checkExtractLazyPath(self: *CheckObject, phrase: []const u8, lazy_path: std.Build.LazyPath) void { + self.checkExtractInner(phrase, lazy_path); } -fn checkExtractInner(self: *CheckObject, phrase: []const u8, file_source: ?std.Build.FileSource) void { +fn checkExtractInner(self: *CheckObject, phrase: []const u8, lazy_path: ?std.Build.LazyPath) void { assert(self.checks.items.len > 0); const last = &self.checks.items[self.checks.items.len - 1]; - last.extract(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source }); + last.extract(.{ .string = self.step.owner.dupe(phrase), .lazy_path = lazy_path }); } /// Adds another searched phrase to the latest created Check @@ -371,16 +375,16 @@ pub fn checkNotPresent(self: *CheckObject, phrase: []const u8) void { self.checkNotPresentInner(phrase, null); } -/// Like `checkExtract()` but takes an additional argument `FileSource` which will be +/// Like `checkExtract()` but takes an additional argument `LazyPath` which will be /// resolved to a full search query in `make()`. -pub fn checkNotPresentFileSource(self: *CheckObject, phrase: []const u8, file_source: std.Build.FileSource) void { - self.checkNotPresentInner(phrase, file_source); +pub fn checkNotPresentLazyPath(self: *CheckObject, phrase: []const u8, lazy_path: std.Build.LazyPath) void { + self.checkNotPresentInner(phrase, lazy_path); } -fn checkNotPresentInner(self: *CheckObject, phrase: []const u8, file_source: ?std.Build.FileSource) void { +fn checkNotPresentInner(self: *CheckObject, phrase: []const u8, lazy_path: ?std.Build.LazyPath) void { assert(self.checks.items.len > 0); const last = &self.checks.items[self.checks.items.len - 1]; - last.notPresent(.{ .string = self.step.owner.dupe(phrase), .file_source = file_source }); + last.notPresent(.{ .string = self.step.owner.dupe(phrase), .lazy_path = lazy_path }); } /// Creates a new check checking in the file headers (section, program headers, etc.). diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index c90f0cb1033e..d6a016f183b7 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -9,14 +9,11 @@ const StringHashMap = std.StringHashMap; const Sha256 = std.crypto.hash.sha2.Sha256; const Allocator = mem.Allocator; const Step = std.Build.Step; -const CrossTarget = std.zig.CrossTarget; -const NativeTargetInfo = std.zig.system.NativeTargetInfo; const LazyPath = std.Build.LazyPath; const PkgConfigPkg = std.Build.PkgConfigPkg; const PkgConfigError = std.Build.PkgConfigError; const RunError = std.Build.RunError; const Module = std.Build.Module; -const VcpkgRoot = std.Build.VcpkgRoot; const InstallDir = std.Build.InstallDir; const GeneratedFile = std.Build.GeneratedFile; const Compile = @This(); @@ -24,36 +21,24 @@ const Compile = @This(); pub const base_id: Step.Id = .compile; step: Step, +root_module: Module, + name: []const u8, -target: CrossTarget, -target_info: NativeTargetInfo, -optimize: std.builtin.OptimizeMode, linker_script: ?LazyPath = null, version_script: ?[]const u8 = null, out_filename: []const u8, +out_lib_filename: []const u8, linkage: ?Linkage = null, version: ?std.SemanticVersion, kind: Kind, major_only_filename: ?[]const u8, name_only_filename: ?[]const u8, -strip: ?bool, -formatted_panics: ?bool = null, -unwind_tables: ?bool, // keep in sync with src/link.zig:CompressDebugSections compress_debug_sections: enum { none, zlib, zstd } = .none, -lib_paths: ArrayList(LazyPath), -rpaths: ArrayList(LazyPath), -frameworks: StringHashMap(FrameworkLinkInfo), verbose_link: bool, verbose_cc: bool, bundle_compiler_rt: ?bool = null, -single_threaded: ?bool, -stack_protector: ?bool = null, -disable_stack_probing: bool, -disable_sanitize_c: bool, -sanitize_thread: bool, rdynamic: bool, -dwarf_format: ?std.dwarf.Format = null, import_memory: bool = false, export_memory: bool = false, /// For WebAssembly targets, this will allow for undefined symbols to @@ -65,32 +50,16 @@ initial_memory: ?u64 = null, max_memory: ?u64 = null, shared_memory: bool = false, global_base: ?u64 = null, -c_std: std.Build.CStd, /// Set via options; intended to be read-only after that. zig_lib_dir: ?LazyPath, -/// Set via options; intended to be read-only after that. -main_mod_path: ?LazyPath, exec_cmd_args: ?[]const ?[]const u8, filter: ?[]const u8, test_evented_io: bool = false, test_runner: ?[]const u8, test_server_mode: bool, -code_model: std.builtin.CodeModel = .default, wasi_exec_model: ?std.builtin.WasiExecModel = null, -/// Symbols to be exported when compiling to wasm -export_symbol_names: []const []const u8 = &.{}, -root_src: ?LazyPath, -out_lib_filename: []const u8, -modules: std.StringArrayHashMap(*Module), - -link_objects: ArrayList(LinkObject), -include_dirs: ArrayList(IncludeDir), -c_macros: ArrayList([]const u8), installed_headers: ArrayList(*Step), -is_linking_libc: bool, -is_linking_libcpp: bool, -vcpkg_bin_path: ?[]const u8 = null, // keep in sync with src/Compilation.zig:RcIncludes /// Behavior of automatic detection of include directories when compiling .rc files. @@ -111,14 +80,13 @@ image_base: ?u64 = null, libc_file: ?LazyPath = null, -valgrind_support: ?bool = null, each_lib_rpath: ?bool = null, /// On ELF targets, this will emit a link section called ".note.gnu.build-id" /// which can be used to coordinate a stripped binary with its debug symbols. /// As an example, the bloaty project refuses to work unless its inputs have /// build ids, in order to prevent accidental mismatches. /// The default is to not include this section because it slows down linking. -build_id: ?BuildId = null, +build_id: ?std.zig.BuildId = null, /// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF /// file. @@ -177,15 +145,9 @@ headerpad_max_install_names: bool = false, /// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols. dead_strip_dylibs: bool = false, -/// Position Independent Code -force_pic: ?bool = null, - /// Position Independent Executable pie: ?bool = null, -red_zone: ?bool = null, - -omit_frame_pointer: ?bool = null, dll_export_fns: ?bool = null, subsystem: ?std.Target.SubSystem = null, @@ -226,91 +188,17 @@ generated_h: ?*GeneratedFile, /// Defaults to `std.math.maxInt(u16)` error_limit: ?u32 = null, +/// Computed during make(). +is_linking_libc: bool = false, +/// Computed during make(). +is_linking_libcpp: bool = false, + pub const ExpectedCompileErrors = union(enum) { contains: []const u8, exact: []const []const u8, }; -pub const CSourceFiles = struct { - dependency: ?*std.Build.Dependency, - /// If `dependency` is not null relative to it, - /// else relative to the build root. - files: []const []const u8, - flags: []const []const u8, -}; - -pub const CSourceFile = struct { - file: LazyPath, - flags: []const []const u8, - - pub fn dupe(self: CSourceFile, b: *std.Build) CSourceFile { - return .{ - .file = self.file.dupe(b), - .flags = b.dupeStrings(self.flags), - }; - } -}; - -pub const RcSourceFile = struct { - file: LazyPath, - /// Any option that rc.exe accepts will work here, with the exception of: - /// - `/fo`: The output filename is set by the build system - /// - `/p`: Only running the preprocessor is not supported in this context - /// - `/:no-preprocess` (non-standard option): Not supported in this context - /// - Any MUI-related option - /// https://learn.microsoft.com/en-us/windows/win32/menurc/using-rc-the-rc-command-line- - /// - /// Implicitly defined options: - /// /x (ignore the INCLUDE environment variable) - /// /D_DEBUG or /DNDEBUG depending on the optimization mode - flags: []const []const u8 = &.{}, - - pub fn dupe(self: RcSourceFile, b: *std.Build) RcSourceFile { - return .{ - .file = self.file.dupe(b), - .flags = b.dupeStrings(self.flags), - }; - } -}; - -pub const LinkObject = union(enum) { - static_path: LazyPath, - other_step: *Compile, - system_lib: SystemLib, - assembly_file: LazyPath, - c_source_file: *CSourceFile, - c_source_files: *CSourceFiles, - win32_resource_file: *RcSourceFile, -}; - -pub const SystemLib = struct { - name: []const u8, - needed: bool, - weak: bool, - use_pkg_config: UsePkgConfig, - preferred_link_mode: std.builtin.LinkMode, - search_strategy: SystemLib.SearchStrategy, - - pub const UsePkgConfig = enum { - /// Don't use pkg-config, just pass -lfoo where foo is name. - no, - /// Try to get information on how to link the library from pkg-config. - /// If that fails, fall back to passing -lfoo where foo is name. - yes, - /// Try to get information on how to link the library from pkg-config. - /// If that fails, error out. - force, - }; - - pub const SearchStrategy = enum { paths_first, mode_first, no_fallback }; -}; - -const FrameworkLinkInfo = struct { - needed: bool = false, - weak: bool = false, -}; - -const Entry = union(enum) { +pub const Entry = union(enum) { /// Let the compiler decide whether to make an entry point and what to name /// it. default, @@ -322,118 +210,24 @@ const Entry = union(enum) { symbol_name: []const u8, }; -pub const IncludeDir = union(enum) { - path: LazyPath, - path_system: LazyPath, - path_after: LazyPath, - framework_path: LazyPath, - framework_path_system: LazyPath, - other_step: *Compile, - config_header_step: *Step.ConfigHeader, -}; - pub const Options = struct { name: []const u8, - root_source_file: ?LazyPath = null, - target: CrossTarget, - optimize: std.builtin.OptimizeMode, + root_module: Module.CreateOptions, kind: Kind, linkage: ?Linkage = null, version: ?std.SemanticVersion = null, max_rss: usize = 0, filter: ?[]const u8 = null, test_runner: ?[]const u8 = null, - link_libc: ?bool = null, - single_threaded: ?bool = null, use_llvm: ?bool = null, use_lld: ?bool = null, zig_lib_dir: ?LazyPath = null, - main_mod_path: ?LazyPath = null, /// Embed a `.manifest` file in the compilation if the object format supports it. /// https://learn.microsoft.com/en-us/windows/win32/sbscs/manifest-files-reference /// Manifest files must have the extension `.manifest`. /// Can be set regardless of target. The `.manifest` file will be ignored /// if the target object format does not support embedded manifests. win32_manifest: ?LazyPath = null, - - /// deprecated; use `main_mod_path`. - main_pkg_path: ?LazyPath = null, -}; - -pub const BuildId = union(enum) { - none, - fast, - uuid, - sha1, - md5, - hexstring: HexString, - - pub fn eql(a: BuildId, b: BuildId) bool { - const a_tag = std.meta.activeTag(a); - const b_tag = std.meta.activeTag(b); - if (a_tag != b_tag) return false; - return switch (a) { - .none, .fast, .uuid, .sha1, .md5 => true, - .hexstring => |a_hexstring| mem.eql(u8, a_hexstring.toSlice(), b.hexstring.toSlice()), - }; - } - - pub const HexString = struct { - bytes: [32]u8, - len: u8, - - /// Result is byte values, *not* hex-encoded. - pub fn toSlice(hs: *const HexString) []const u8 { - return hs.bytes[0..hs.len]; - } - }; - - /// Input is byte values, *not* hex-encoded. - /// Asserts `bytes` fits inside `HexString` - pub fn initHexString(bytes: []const u8) BuildId { - var result: BuildId = .{ .hexstring = .{ - .bytes = undefined, - .len = @as(u8, @intCast(bytes.len)), - } }; - @memcpy(result.hexstring.bytes[0..bytes.len], bytes); - return result; - } - - /// Converts UTF-8 text to a `BuildId`. - pub fn parse(text: []const u8) !BuildId { - if (mem.eql(u8, text, "none")) { - return .none; - } else if (mem.eql(u8, text, "fast")) { - return .fast; - } else if (mem.eql(u8, text, "uuid")) { - return .uuid; - } else if (mem.eql(u8, text, "sha1") or mem.eql(u8, text, "tree")) { - return .sha1; - } else if (mem.eql(u8, text, "md5")) { - return .md5; - } else if (mem.startsWith(u8, text, "0x")) { - var result: BuildId = .{ .hexstring = undefined }; - const slice = try std.fmt.hexToBytes(&result.hexstring.bytes, text[2..]); - result.hexstring.len = @as(u8, @intCast(slice.len)); - return result; - } - return error.InvalidBuildIdStyle; - } - - test parse { - try std.testing.expectEqual(BuildId.md5, try parse("md5")); - try std.testing.expectEqual(BuildId.none, try parse("none")); - try std.testing.expectEqual(BuildId.fast, try parse("fast")); - try std.testing.expectEqual(BuildId.uuid, try parse("uuid")); - try std.testing.expectEqual(BuildId.sha1, try parse("sha1")); - try std.testing.expectEqual(BuildId.sha1, try parse("tree")); - - try std.testing.expect(BuildId.initHexString("").eql(try parse("0x"))); - try std.testing.expect(BuildId.initHexString("\x12\x34\x56").eql(try parse("0x123456"))); - try std.testing.expectError(error.InvalidLength, parse("0x12-34")); - try std.testing.expectError(error.InvalidCharacter, parse("0xfoobbb")); - try std.testing.expectError(error.InvalidBuildIdStyle, parse("yaddaxxx")); - } }; pub const Kind = enum { @@ -447,7 +241,6 @@ pub const Linkage = enum { dynamic, static }; pub fn create(owner: *std.Build, options: Options) *Compile { const name = owner.dupe(options.name); - const root_src: ?LazyPath = if (options.root_source_file) |rsrc| rsrc.dupe(owner) else null; if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); } @@ -458,6 +251,9 @@ pub fn create(owner: *std.Build, options: Options) *Compile { else owner.fmt("{s} ", .{name}); + const resolved_target = options.root_module.target.?; + const target = resolved_target.result; + const step_name = owner.fmt("{s} {s}{s} {s}", .{ switch (options.kind) { .exe => "zig build-exe", @@ -466,15 +262,13 @@ pub fn create(owner: *std.Build, options: Options) *Compile { .@"test" => "zig test", }, name_adjusted, - @tagName(options.optimize), - options.target.zigTriple(owner.allocator) catch @panic("OOM"), + @tagName(options.root_module.optimize orelse .Debug), + resolved_target.query.zigTriple(owner.allocator) catch @panic("OOM"), }); - const target_info = NativeTargetInfo.detect(options.target) catch @panic("unhandled error"); - const out_filename = std.zig.binNameAlloc(owner.allocator, .{ .root_name = name, - .target = target_info.target, + .target = target, .output_mode = switch (options.kind) { .lib => .Lib, .obj => .Obj, @@ -489,17 +283,12 @@ pub fn create(owner: *std.Build, options: Options) *Compile { const self = owner.allocator.create(Compile) catch @panic("OOM"); self.* = .{ - .strip = null, - .unwind_tables = null, + .root_module = undefined, .verbose_link = false, .verbose_cc = false, - .optimize = options.optimize, - .target = options.target, .linkage = options.linkage, .kind = options.kind, - .root_src = root_src, .name = name, - .frameworks = StringHashMap(FrameworkLinkInfo).init(owner.allocator), .step = Step.init(.{ .id = base_id, .name = step_name, @@ -512,23 +301,12 @@ pub fn create(owner: *std.Build, options: Options) *Compile { .out_lib_filename = undefined, .major_only_filename = null, .name_only_filename = null, - .modules = std.StringArrayHashMap(*Module).init(owner.allocator), - .include_dirs = ArrayList(IncludeDir).init(owner.allocator), - .link_objects = ArrayList(LinkObject).init(owner.allocator), - .c_macros = ArrayList([]const u8).init(owner.allocator), - .lib_paths = ArrayList(LazyPath).init(owner.allocator), - .rpaths = ArrayList(LazyPath).init(owner.allocator), .installed_headers = ArrayList(*Step).init(owner.allocator), - .c_std = std.Build.CStd.C99, .zig_lib_dir = null, - .main_mod_path = null, .exec_cmd_args = null, .filter = options.filter, .test_runner = options.test_runner, .test_server_mode = options.test_runner == null, - .disable_stack_probing = false, - .disable_sanitize_c = false, - .sanitize_thread = false, .rdynamic = false, .installed_path = null, .force_undefined_symbols = StringHashMap(void).init(owner.allocator), @@ -543,28 +321,20 @@ pub fn create(owner: *std.Build, options: Options) *Compile { .generated_llvm_ir = null, .generated_h = null, - .target_info = target_info, - - .is_linking_libc = options.link_libc orelse false, - .is_linking_libcpp = false, - .single_threaded = options.single_threaded, .use_llvm = options.use_llvm, .use_lld = options.use_lld, }; + self.root_module.init(owner, options.root_module, self); + if (options.zig_lib_dir) |lp| { self.zig_lib_dir = lp.dupe(self.step.owner); lp.addStepDependencies(&self.step); } - if (options.main_mod_path orelse options.main_pkg_path) |lp| { - self.main_mod_path = lp.dupe(self.step.owner); - lp.addStepDependencies(&self.step); - } - // Only the PE/COFF format has a Resource Table which is where the manifest // gets embedded, so for any other target the manifest file is just ignored. - if (self.target.getObjectFormat() == .coff) { + if (target.ofmt == .coff) { if (options.win32_manifest) |lp| { self.win32_manifest = lp.dupe(self.step.owner); lp.addStepDependencies(&self.step); @@ -575,14 +345,14 @@ pub fn create(owner: *std.Build, options: Options) *Compile { if (self.linkage != null and self.linkage.? == .static) { self.out_lib_filename = self.out_filename; } else if (self.version) |version| { - if (target_info.target.isDarwin()) { + if (target.isDarwin()) { self.major_only_filename = owner.fmt("lib{s}.{d}.dylib", .{ self.name, version.major, }); self.name_only_filename = owner.fmt("lib{s}.dylib", .{self.name}); self.out_lib_filename = self.out_filename; - } else if (target_info.target.os.tag == .windows) { + } else if (target.os.tag == .windows) { self.out_lib_filename = owner.fmt("{s}.lib", .{self.name}); } else { self.major_only_filename = owner.fmt("lib{s}.so.{d}", .{ self.name, version.major }); @@ -590,9 +360,9 @@ pub fn create(owner: *std.Build, options: Options) *Compile { self.out_lib_filename = self.out_filename; } } else { - if (target_info.target.isDarwin()) { + if (target.isDarwin()) { self.out_lib_filename = self.out_filename; - } else if (target_info.target.os.tag == .windows) { + } else if (target.os.tag == .windows) { self.out_lib_filename = owner.fmt("{s}.lib", .{self.name}); } else { self.out_lib_filename = self.out_filename; @@ -600,8 +370,6 @@ pub fn create(owner: *std.Build, options: Options) *Compile { } } - if (root_src) |rs| rs.addStepDependencies(&self.step); - return self; } @@ -701,7 +469,7 @@ pub const run = @compileError("deprecated; use std.Build.addRunArtifact"); pub const install = @compileError("deprecated; use std.Build.installArtifact"); pub fn checkObject(self: *Compile) *Step.CheckObject { - return Step.CheckObject.create(self.step.owner, self.getEmittedBin(), self.target_info.target.ofmt); + return Step.CheckObject.create(self.step.owner, self.getEmittedBin(), self.rootModuleTarget().ofmt); } /// deprecated: use `setLinkerScript` @@ -718,113 +486,75 @@ pub fn forceUndefinedSymbol(self: *Compile, symbol_name: []const u8) void { self.force_undefined_symbols.put(b.dupe(symbol_name), {}) catch @panic("OOM"); } -pub fn linkFramework(self: *Compile, framework_name: []const u8) void { - const b = self.step.owner; - self.frameworks.put(b.dupe(framework_name), .{}) catch @panic("OOM"); -} - -pub fn linkFrameworkNeeded(self: *Compile, framework_name: []const u8) void { - const b = self.step.owner; - self.frameworks.put(b.dupe(framework_name), .{ - .needed = true, - }) catch @panic("OOM"); -} - -pub fn linkFrameworkWeak(self: *Compile, framework_name: []const u8) void { - const b = self.step.owner; - self.frameworks.put(b.dupe(framework_name), .{ - .weak = true, - }) catch @panic("OOM"); -} - /// Returns whether the library, executable, or object depends on a particular system library. -pub fn dependsOnSystemLibrary(self: Compile, name: []const u8) bool { - if (isLibCLibrary(name)) { - return self.is_linking_libc; +/// Includes transitive dependencies. +pub fn dependsOnSystemLibrary(self: *const Compile, name: []const u8) bool { + var is_linking_libc = false; + var is_linking_libcpp = false; + + var it = self.root_module.iterateDependencies(self, true); + while (it.next()) |module| { + for (module.link_objects.items) |link_object| { + switch (link_object) { + .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true, + else => continue, + } + } + is_linking_libc = is_linking_libc or module.link_libcpp == true; + is_linking_libcpp = is_linking_libcpp or module.link_libcpp == true; } - if (isLibCppLibrary(name)) { - return self.is_linking_libcpp; + + if (self.rootModuleTarget().is_libc_lib_name(name)) { + return is_linking_libc; } - for (self.link_objects.items) |link_object| { - switch (link_object) { - .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true, - else => continue, - } + + if (self.rootModuleTarget().is_libcpp_lib_name(name)) { + return is_linking_libcpp; } - return false; -} -pub fn linkLibrary(self: *Compile, lib: *Compile) void { - assert(lib.kind == .lib); - self.linkLibraryOrObject(lib); + return false; } -pub fn isDynamicLibrary(self: *Compile) bool { +pub fn isDynamicLibrary(self: *const Compile) bool { return self.kind == .lib and self.linkage == Linkage.dynamic; } -pub fn isStaticLibrary(self: *Compile) bool { +pub fn isStaticLibrary(self: *const Compile) bool { return self.kind == .lib and self.linkage != Linkage.dynamic; } pub fn producesPdbFile(self: *Compile) bool { + const target = self.rootModuleTarget(); // TODO: Is this right? Isn't PDB for *any* PE/COFF file? // TODO: just share this logic with the compiler, silly! - if (!self.target.isWindows() and !self.target.isUefi()) return false; - if (self.target.getObjectFormat() == .c) return false; - if (self.strip == true or (self.strip == null and self.optimize == .ReleaseSmall)) return false; + switch (target.os.tag) { + .windows, .uefi => {}, + else => return false, + } + if (target.ofmt == .c) return false; + if (self.root_module.strip == true or + (self.root_module.strip == null and self.root_module.optimize == .ReleaseSmall)) + { + return false; + } return self.isDynamicLibrary() or self.kind == .exe or self.kind == .@"test"; } pub fn producesImplib(self: *Compile) bool { - return self.isDynamicLibrary() and self.target.isWindows(); + return self.isDynamicLibrary() and self.rootModuleTarget().os.tag == .windows; } pub fn linkLibC(self: *Compile) void { - self.is_linking_libc = true; + self.root_module.link_libc = true; } pub fn linkLibCpp(self: *Compile) void { - self.is_linking_libcpp = true; -} - -/// If the value is omitted, it is set to 1. -/// `name` and `value` need not live longer than the function call. -pub fn defineCMacro(self: *Compile, name: []const u8, value: ?[]const u8) void { - const b = self.step.owner; - const macro = std.Build.constructCMacro(b.allocator, name, value); - self.c_macros.append(macro) catch @panic("OOM"); -} - -/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. -pub fn defineCMacroRaw(self: *Compile, name_and_value: []const u8) void { - const b = self.step.owner; - self.c_macros.append(b.dupe(name_and_value)) catch @panic("OOM"); -} - -/// deprecated: use linkSystemLibrary2 -pub fn linkSystemLibraryName(self: *Compile, name: []const u8) void { - return linkSystemLibrary2(self, name, .{ .use_pkg_config = .no }); -} - -/// deprecated: use linkSystemLibrary2 -pub fn linkSystemLibraryNeededName(self: *Compile, name: []const u8) void { - return linkSystemLibrary2(self, name, .{ .needed = true, .use_pkg_config = .no }); + self.root_module.link_libcpp = true; } -/// deprecated: use linkSystemLibrary2 -pub fn linkSystemLibraryWeakName(self: *Compile, name: []const u8) void { - return linkSystemLibrary2(self, name, .{ .weak = true, .use_pkg_config = .no }); -} - -/// deprecated: use linkSystemLibrary2 -pub fn linkSystemLibraryPkgConfigOnly(self: *Compile, lib_name: []const u8) void { - return linkSystemLibrary2(self, lib_name, .{ .use_pkg_config = .force }); -} - -/// deprecated: use linkSystemLibrary2 -pub fn linkSystemLibraryNeededPkgConfigOnly(self: *Compile, lib_name: []const u8) void { - return linkSystemLibrary2(self, lib_name, .{ .needed = true, .use_pkg_config = .force }); +/// Deprecated. Use `c.root_module.addCMacro`. +pub fn defineCMacro(c: *Compile, name: []const u8, value: ?[]const u8) void { + c.root_module.addCMacro(name, value orelse "1"); } /// Run pkg-config for the given library name and parse the output, returning the arguments @@ -924,98 +654,45 @@ fn runPkgConfig(self: *Compile, lib_name: []const u8) ![]const []const u8 { } pub fn linkSystemLibrary(self: *Compile, name: []const u8) void { - self.linkSystemLibrary2(name, .{}); -} - -/// deprecated: use linkSystemLibrary2 -pub fn linkSystemLibraryNeeded(self: *Compile, name: []const u8) void { - return linkSystemLibrary2(self, name, .{ .needed = true }); + return self.root_module.linkSystemLibrary(name, .{}); } -/// deprecated: use linkSystemLibrary2 -pub fn linkSystemLibraryWeak(self: *Compile, name: []const u8) void { - return linkSystemLibrary2(self, name, .{ .weak = true }); -} - -pub const LinkSystemLibraryOptions = struct { - needed: bool = false, - weak: bool = false, - use_pkg_config: SystemLib.UsePkgConfig = .yes, - preferred_link_mode: std.builtin.LinkMode = .Dynamic, - search_strategy: SystemLib.SearchStrategy = .paths_first, -}; - pub fn linkSystemLibrary2( self: *Compile, name: []const u8, - options: LinkSystemLibraryOptions, + options: Module.LinkSystemLibraryOptions, ) void { - const b = self.step.owner; - if (isLibCLibrary(name)) { - self.linkLibC(); - return; - } - if (isLibCppLibrary(name)) { - self.linkLibCpp(); - return; - } - - self.link_objects.append(.{ - .system_lib = .{ - .name = b.dupe(name), - .needed = options.needed, - .weak = options.weak, - .use_pkg_config = options.use_pkg_config, - .preferred_link_mode = options.preferred_link_mode, - .search_strategy = options.search_strategy, - }, - }) catch @panic("OOM"); + return self.root_module.linkSystemLibrary(name, options); } -pub const AddCSourceFilesOptions = struct { - /// When provided, `files` are relative to `dependency` rather than the package that owns the `Compile` step. - dependency: ?*std.Build.Dependency = null, - files: []const []const u8, - flags: []const []const u8 = &.{}, -}; +pub fn linkFramework(c: *Compile, name: []const u8) void { + c.root_module.linkFramework(name, .{}); +} -/// Handy when you have many C/C++ source files and want them all to have the same flags. -pub fn addCSourceFiles(self: *Compile, options: AddCSourceFilesOptions) void { - const b = self.step.owner; - const c_source_files = b.allocator.create(CSourceFiles) catch @panic("OOM"); +/// Deprecated. Use `c.root_module.linkFramework`. +pub fn linkFrameworkNeeded(c: *Compile, name: []const u8) void { + c.root_module.linkFramework(name, .{ .needed = true }); +} - const files_copy = b.dupeStrings(options.files); - const flags_copy = b.dupeStrings(options.flags); +/// Deprecated. Use `c.root_module.linkFramework`. +pub fn linkFrameworkWeak(c: *Compile, name: []const u8) void { + c.root_module.linkFramework(name, .{ .weak = true }); +} - c_source_files.* = .{ - .dependency = options.dependency, - .files = files_copy, - .flags = flags_copy, - }; - self.link_objects.append(.{ .c_source_files = c_source_files }) catch @panic("OOM"); +/// Handy when you have many C/C++ source files and want them all to have the same flags. +pub fn addCSourceFiles(self: *Compile, options: Module.AddCSourceFilesOptions) void { + self.root_module.addCSourceFiles(options); } -pub fn addCSourceFile(self: *Compile, source: CSourceFile) void { - const b = self.step.owner; - const c_source_file = b.allocator.create(CSourceFile) catch @panic("OOM"); - c_source_file.* = source.dupe(b); - self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM"); - source.file.addStepDependencies(&self.step); +pub fn addCSourceFile(self: *Compile, source: Module.CSourceFile) void { + self.root_module.addCSourceFile(source); } /// Resource files must have the extension `.rc`. /// Can be called regardless of target. The .rc file will be ignored /// if the target object format does not support embedded resources. -pub fn addWin32ResourceFile(self: *Compile, source: RcSourceFile) void { - // Only the PE/COFF format has a Resource Table, so for any other target - // the resource file is just ignored. - if (self.target.getObjectFormat() != .coff) return; - - const b = self.step.owner; - const rc_source_file = b.allocator.create(RcSourceFile) catch @panic("OOM"); - rc_source_file.* = source.dupe(b); - self.link_objects.append(.{ .win32_resource_file = rc_source_file }) catch @panic("OOM"); - source.file.addStepDependencies(&self.step); +pub fn addWin32ResourceFile(self: *Compile, source: Module.RcSourceFile) void { + self.root_module.addWin32ResourceFile(source); } pub fn setVerboseLink(self: *Compile, value: bool) void { @@ -1042,27 +719,18 @@ fn getEmittedFileGeneric(self: *Compile, output_file: *?*GeneratedFile) LazyPath return .{ .generated = generated_file }; } -/// deprecated: use `getEmittedBinDirectory` -pub const getOutputDirectorySource = getEmittedBinDirectory; - /// Returns the path to the directory that contains the emitted binary file. pub fn getEmittedBinDirectory(self: *Compile) LazyPath { _ = self.getEmittedBin(); return self.getEmittedFileGeneric(&self.emit_directory); } -/// deprecated: use `getEmittedBin` -pub const getOutputSource = getEmittedBin; - /// Returns the path to the generated executable, library or object file. /// To run an executable built with zig build, use `run`, or create an install step and invoke it. pub fn getEmittedBin(self: *Compile) LazyPath { return self.getEmittedFileGeneric(&self.generated_bin); } -/// deprecated: use `getEmittedImplib` -pub const getOutputLibSource = getEmittedImplib; - /// Returns the path to the generated import library. /// This function can only be called for libraries. pub fn getEmittedImplib(self: *Compile) LazyPath { @@ -1070,9 +738,6 @@ pub fn getEmittedImplib(self: *Compile) LazyPath { return self.getEmittedFileGeneric(&self.generated_implib); } -/// deprecated: use `getEmittedH` -pub const getOutputHSource = getEmittedH; - /// Returns the path to the generated header file. /// This function can only be called for libraries or objects. pub fn getEmittedH(self: *Compile) LazyPath { @@ -1080,9 +745,6 @@ pub fn getEmittedH(self: *Compile) LazyPath { return self.getEmittedFileGeneric(&self.generated_h); } -/// deprecated: use `getEmittedPdb`. -pub const getOutputPdbSource = getEmittedPdb; - /// Returns the generated PDB file. /// If the compilation does not produce a PDB file, this causes a FileNotFound error /// at build time. @@ -1112,138 +774,51 @@ pub fn getEmittedLlvmBc(self: *Compile) LazyPath { } pub fn addAssemblyFile(self: *Compile, source: LazyPath) void { - const b = self.step.owner; - const source_duped = source.dupe(b); - self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM"); - source_duped.addStepDependencies(&self.step); + self.root_module.addAssemblyFile(source); } pub fn addObjectFile(self: *Compile, source: LazyPath) void { - const b = self.step.owner; - self.link_objects.append(.{ .static_path = source.dupe(b) }) catch @panic("OOM"); - source.addStepDependencies(&self.step); -} - -pub fn addObject(self: *Compile, obj: *Compile) void { - assert(obj.kind == .obj); - self.linkLibraryOrObject(obj); + self.root_module.addObjectFile(source); } -pub fn addAfterIncludePath(self: *Compile, path: LazyPath) void { - const b = self.step.owner; - self.include_dirs.append(IncludeDir{ .path_after = path.dupe(b) }) catch @panic("OOM"); - path.addStepDependencies(&self.step); -} - -pub fn addSystemIncludePath(self: *Compile, path: LazyPath) void { - const b = self.step.owner; - self.include_dirs.append(IncludeDir{ .path_system = path.dupe(b) }) catch @panic("OOM"); - path.addStepDependencies(&self.step); +pub fn addObject(self: *Compile, object: *Compile) void { + self.root_module.addObject(object); } -pub fn addIncludePath(self: *Compile, path: LazyPath) void { - const b = self.step.owner; - self.include_dirs.append(IncludeDir{ .path = path.dupe(b) }) catch @panic("OOM"); - path.addStepDependencies(&self.step); -} - -pub fn addConfigHeader(self: *Compile, config_header: *Step.ConfigHeader) void { - self.step.dependOn(&config_header.step); - self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM"); -} - -pub fn addLibraryPath(self: *Compile, directory_source: LazyPath) void { - const b = self.step.owner; - self.lib_paths.append(directory_source.dupe(b)) catch @panic("OOM"); - directory_source.addStepDependencies(&self.step); +pub fn linkLibrary(self: *Compile, library: *Compile) void { + self.root_module.linkLibrary(library); } -pub fn addRPath(self: *Compile, directory_source: LazyPath) void { - const b = self.step.owner; - self.rpaths.append(directory_source.dupe(b)) catch @panic("OOM"); - directory_source.addStepDependencies(&self.step); +pub fn addAfterIncludePath(self: *Compile, lazy_path: LazyPath) void { + self.root_module.addAfterIncludePath(lazy_path); } -pub fn addSystemFrameworkPath(self: *Compile, directory_source: LazyPath) void { - const b = self.step.owner; - self.include_dirs.append(IncludeDir{ .framework_path_system = directory_source.dupe(b) }) catch @panic("OOM"); - directory_source.addStepDependencies(&self.step); +pub fn addSystemIncludePath(self: *Compile, lazy_path: LazyPath) void { + self.root_module.addSystemIncludePath(lazy_path); } -pub fn addFrameworkPath(self: *Compile, directory_source: LazyPath) void { - const b = self.step.owner; - self.include_dirs.append(IncludeDir{ .framework_path = directory_source.dupe(b) }) catch @panic("OOM"); - directory_source.addStepDependencies(&self.step); +pub fn addIncludePath(self: *Compile, lazy_path: LazyPath) void { + self.root_module.addIncludePath(lazy_path); } -/// Adds a module to be used with `@import` and exposing it in the current -/// package's module table using `name`. -pub fn addModule(cs: *Compile, name: []const u8, module: *Module) void { - const b = cs.step.owner; - cs.modules.put(b.dupe(name), module) catch @panic("OOM"); - - var done = std.AutoHashMap(*Module, void).init(b.allocator); - defer done.deinit(); - cs.addRecursiveBuildDeps(module, &done) catch @panic("OOM"); +pub fn addConfigHeader(self: *Compile, config_header: *Step.ConfigHeader) void { + self.root_module.addConfigHeader(config_header); } -/// Adds a module to be used with `@import` without exposing it in the current -/// package's module table. -pub fn addAnonymousModule(cs: *Compile, name: []const u8, options: std.Build.CreateModuleOptions) void { - const b = cs.step.owner; - const module = b.createModule(options); - return addModule(cs, name, module); +pub fn addLibraryPath(self: *Compile, directory_path: LazyPath) void { + self.root_module.addLibraryPath(directory_path); } -pub fn addOptions(cs: *Compile, module_name: []const u8, options: *Step.Options) void { - addModule(cs, module_name, options.createModule()); +pub fn addRPath(self: *Compile, directory_path: LazyPath) void { + self.root_module.addRPath(directory_path); } -fn addRecursiveBuildDeps(cs: *Compile, module: *Module, done: *std.AutoHashMap(*Module, void)) !void { - if (done.contains(module)) return; - try done.put(module, {}); - module.source_file.addStepDependencies(&cs.step); - for (module.dependencies.values()) |dep| { - try cs.addRecursiveBuildDeps(dep, done); - } +pub fn addSystemFrameworkPath(self: *Compile, directory_path: LazyPath) void { + self.root_module.addSystemFrameworkPath(directory_path); } -/// If Vcpkg was found on the system, it will be added to include and lib -/// paths for the specified target. -pub fn addVcpkgPaths(self: *Compile, linkage: Compile.Linkage) !void { - const b = self.step.owner; - // Ideally in the Unattempted case we would call the function recursively - // after findVcpkgRoot and have only one switch statement, but the compiler - // cannot resolve the error set. - switch (b.vcpkg_root) { - .unattempted => { - b.vcpkg_root = if (try findVcpkgRoot(b.allocator)) |root| - VcpkgRoot{ .found = root } - else - .not_found; - }, - .not_found => return error.VcpkgNotFound, - .found => {}, - } - - switch (b.vcpkg_root) { - .unattempted => unreachable, - .not_found => return error.VcpkgNotFound, - .found => |root| { - const allocator = b.allocator; - const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic); - defer b.allocator.free(triplet); - - const include_path = b.pathJoin(&.{ root, "installed", triplet, "include" }); - errdefer allocator.free(include_path); - try self.include_dirs.append(IncludeDir{ .path = .{ .path = include_path } }); - - const lib_path = b.pathJoin(&.{ root, "installed", triplet, "lib" }); - try self.lib_paths.append(.{ .path = lib_path }); - - self.vcpkg_bin_path = b.pathJoin(&.{ root, "installed", triplet, "bin" }); - }, - } +pub fn addFrameworkPath(self: *Compile, directory_path: LazyPath) void { + self.root_module.addFrameworkPath(directory_path); } pub fn setExecCmd(self: *Compile, args: []const ?[]const u8) void { @@ -1256,129 +831,42 @@ pub fn setExecCmd(self: *Compile, args: []const ?[]const u8) void { self.exec_cmd_args = duped_args; } -fn linkLibraryOrObject(self: *Compile, other: *Compile) void { - other.getEmittedBin().addStepDependencies(&self.step); - if (other.target.isWindows() and other.isDynamicLibrary()) { - other.getEmittedImplib().addStepDependencies(&self.step); - } +const CliNamedModules = struct { + modules: std.AutoArrayHashMapUnmanaged(*Module, void), + names: std.StringArrayHashMapUnmanaged(void), - self.link_objects.append(.{ .other_step = other }) catch @panic("OOM"); - self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM"); - - for (other.installed_headers.items) |install_step| { - self.step.dependOn(install_step); - } -} - -fn appendModuleArgs( - cs: *Compile, - zig_args: *ArrayList([]const u8), -) error{OutOfMemory}!void { - const b = cs.step.owner; - // First, traverse the whole dependency graph and give every module a unique name, ideally one - // named after what it's called somewhere in the graph. It will help here to have both a mapping - // from module to name and a set of all the currently-used names. - var mod_names = std.AutoHashMap(*Module, []const u8).init(b.allocator); - var names = std.StringHashMap(void).init(b.allocator); - - var to_name = std.ArrayList(struct { - name: []const u8, - mod: *Module, - }).init(b.allocator); - { - var it = cs.modules.iterator(); - while (it.next()) |kv| { - // While we're traversing the root dependencies, let's make sure that no module names - // have colons in them, since the CLI forbids it. We handle this for transitive - // dependencies further down. - if (std.mem.indexOfScalar(u8, kv.key_ptr.*, ':') != null) { - @panic("Module names cannot contain colons"); - } - try to_name.append(.{ - .name = kv.key_ptr.*, - .mod = kv.value_ptr.*, - }); - } - } - - while (to_name.popOrNull()) |dep| { - if (mod_names.contains(dep.mod)) continue; - - // We'll use this buffer to store the name we decide on - var buf = try b.allocator.alloc(u8, dep.name.len + 32); - // First, try just the exposed dependency name - @memcpy(buf[0..dep.name.len], dep.name); - var name = buf[0..dep.name.len]; - var n: usize = 0; - while (names.contains(name)) { - // If that failed, append an incrementing number to the end - name = std.fmt.bufPrint(buf, "{s}{}", .{ dep.name, n }) catch unreachable; - n += 1; + /// Traverse the whole dependency graph and give every module a unique + /// name, ideally one named after what it's called somewhere in the graph. + /// It will help here to have both a mapping from module to name and a set + /// of all the currently-used names. + fn init(arena: Allocator, root_module: *Module) Allocator.Error!CliNamedModules { + var self: CliNamedModules = .{ + .modules = .{}, + .names = .{}, + }; + var it = root_module.iterateDependencies(null, false); + { + const item = it.next().?; + assert(root_module == item.module); + try self.modules.put(arena, root_module, {}); + try self.names.put(arena, "root", {}); } - - try mod_names.put(dep.mod, name); - try names.put(name, {}); - - var it = dep.mod.dependencies.iterator(); - while (it.next()) |kv| { - // Same colon-in-name check as above, but for transitive dependencies. - if (std.mem.indexOfScalar(u8, kv.key_ptr.*, ':') != null) { - @panic("Module names cannot contain colons"); + while (it.next()) |item| { + var name = item.name; + var n: usize = 0; + while (true) { + const gop = try self.names.getOrPut(arena, name); + if (!gop.found_existing) { + try self.modules.putNoClobber(arena, item.module, {}); + break; + } + name = try std.fmt.allocPrint(arena, "{s}{d}", .{ item.name, n }); + n += 1; } - try to_name.append(.{ - .name = kv.key_ptr.*, - .mod = kv.value_ptr.*, - }); - } - } - - // Since the module names given to the CLI are based off of the exposed names, we already know - // that none of the CLI names have colons in them, so there's no need to check that explicitly. - - // Every module in the graph is now named; output their definitions - { - var it = mod_names.iterator(); - while (it.next()) |kv| { - const mod = kv.key_ptr.*; - const name = kv.value_ptr.*; - - const deps_str = try constructDepString(b.allocator, mod_names, mod.dependencies); - const src = mod.source_file.getPath(mod.builder); - try zig_args.append("--mod"); - try zig_args.append(try std.fmt.allocPrint(b.allocator, "{s}:{s}:{s}", .{ name, deps_str, src })); - } - } - - // Lastly, output the root dependencies - const deps_str = try constructDepString(b.allocator, mod_names, cs.modules); - if (deps_str.len > 0) { - try zig_args.append("--deps"); - try zig_args.append(deps_str); - } -} - -fn constructDepString( - allocator: std.mem.Allocator, - mod_names: std.AutoHashMap(*Module, []const u8), - deps: std.StringArrayHashMap(*Module), -) ![]const u8 { - var deps_str = std.ArrayList(u8).init(allocator); - var it = deps.iterator(); - while (it.next()) |kv| { - const expose = kv.key_ptr.*; - const name = mod_names.get(kv.value_ptr.*).?; - if (std.mem.eql(u8, expose, name)) { - try deps_str.writer().print("{s},", .{name}); - } else { - try deps_str.writer().print("{s}={s},", .{ expose, name }); } + return self; } - if (deps_str.items.len > 0) { - return deps_str.items[0 .. deps_str.items.len - 1]; // omit trailing comma - } else { - return ""; - } -} +}; fn getGeneratedFilePath(self: *Compile, comptime tag_name: []const u8, asking_step: ?*Step) []const u8 { const maybe_path: ?*GeneratedFile = @field(self, tag_name); @@ -1406,13 +894,10 @@ fn getGeneratedFilePath(self: *Compile, comptime tag_name: []const u8, asking_st fn make(step: *Step, prog_node: *std.Progress.Node) !void { const b = step.owner; + const arena = b.allocator; const self = @fieldParentPtr(Compile, "step", step); - if (self.root_src == null and self.link_objects.items.len == 0) { - return step.fail("the linker needs one or more objects to link", .{}); - } - - var zig_args = ArrayList([]const u8).init(b.allocator); + var zig_args = ArrayList([]const u8).init(arena); defer zig_args.deinit(); try zig_args.append(b.zig_exe); @@ -1426,14 +911,14 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(cmd); if (b.reference_trace) |some| { - try zig_args.append(try std.fmt.allocPrint(b.allocator, "-freference-trace={d}", .{some})); + try zig_args.append(try std.fmt.allocPrint(arena, "-freference-trace={d}", .{some})); } try addFlag(&zig_args, "llvm", self.use_llvm); try addFlag(&zig_args, "lld", self.use_lld); - if (self.target.ofmt) |ofmt| { - try zig_args.append(try std.fmt.allocPrint(b.allocator, "-ofmt={s}", .{@tagName(ofmt)})); + if (self.root_module.resolved_target.?.query.ofmt) |ofmt| { + try zig_args.append(try std.fmt.allocPrint(arena, "-ofmt={s}", .{@tagName(ofmt)})); } switch (self.entry) { @@ -1441,7 +926,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { .disabled => try zig_args.append("-fno-entry"), .enabled => try zig_args.append("-fentry"), .symbol_name => |entry_name| { - try zig_args.append(try std.fmt.allocPrint(b.allocator, "-fentry={s}", .{entry_name})); + try zig_args.append(try std.fmt.allocPrint(arena, "-fentry={s}", .{entry_name})); }, } @@ -1455,207 +940,316 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (self.stack_size) |stack_size| { try zig_args.append("--stack"); - try zig_args.append(try std.fmt.allocPrint(b.allocator, "{}", .{stack_size})); + try zig_args.append(try std.fmt.allocPrint(arena, "{}", .{stack_size})); } - if (self.root_src) |root_src| try zig_args.append(root_src.getPath(b)); + { + var seen_system_libs: std.StringHashMapUnmanaged(void) = .{}; + var frameworks: std.StringArrayHashMapUnmanaged(Module.LinkFrameworkOptions) = .{}; + + var prev_has_cflags = false; + var prev_has_rcflags = false; + var prev_search_strategy: Module.SystemLib.SearchStrategy = .paths_first; + var prev_preferred_link_mode: std.builtin.LinkMode = .Dynamic; + // Track the number of positional arguments so that a nice error can be + // emitted if there is nothing to link. + var total_linker_objects: usize = @intFromBool(self.root_module.root_source_file != null); + + { + // Fully recursive iteration including dynamic libraries to detect + // libc and libc++ linkage. + var it = self.root_module.iterateDependencies(self, true); + while (it.next()) |key| { + if (key.module.link_libc == true) self.is_linking_libc = true; + if (key.module.link_libcpp == true) self.is_linking_libcpp = true; + } + } - // We will add link objects from transitive dependencies, but we want to keep - // all link objects in the same order provided. - // This array is used to keep self.link_objects immutable. - var transitive_deps: TransitiveDeps = .{ - .link_objects = ArrayList(LinkObject).init(b.allocator), - .seen_system_libs = StringHashMap(void).init(b.allocator), - .seen_steps = std.AutoHashMap(*const Step, void).init(b.allocator), - .is_linking_libcpp = self.is_linking_libcpp, - .is_linking_libc = self.is_linking_libc, - .frameworks = &self.frameworks, - }; + var cli_named_modules = try CliNamedModules.init(arena, &self.root_module); - try transitive_deps.seen_steps.put(&self.step, {}); - try transitive_deps.add(self.link_objects.items); - - var prev_has_cflags = false; - var prev_has_rcflags = false; - var prev_search_strategy: SystemLib.SearchStrategy = .paths_first; - var prev_preferred_link_mode: std.builtin.LinkMode = .Dynamic; - - for (transitive_deps.link_objects.items) |link_object| { - switch (link_object) { - .static_path => |static_path| try zig_args.append(static_path.getPath(b)), - - .other_step => |other| switch (other.kind) { - .exe => @panic("Cannot link with an executable build artifact"), - .@"test" => @panic("Cannot link with a test"), - .obj => { - try zig_args.append(other.getEmittedBin().getPath(b)); - }, - .lib => l: { - if (self.isStaticLibrary() and other.isStaticLibrary()) { - // Avoid putting a static library inside a static library. - break :l; - } + // For this loop, don't chase dynamic libraries because their link + // objects are already linked. + var it = self.root_module.iterateDependencies(self, false); - // For DLLs, we gotta link against the implib. For - // everything else, we directly link against the library file. - const full_path_lib = if (other.producesImplib()) - other.getGeneratedFilePath("generated_implib", &self.step) - else - other.getGeneratedFilePath("generated_bin", &self.step); - try zig_args.append(full_path_lib); - - if (other.linkage == Linkage.dynamic and !self.target.isWindows()) { - if (fs.path.dirname(full_path_lib)) |dirname| { - try zig_args.append("-rpath"); - try zig_args.append(dirname); - } - } - }, - }, + while (it.next()) |key| { + const module = key.module; + const compile = key.compile.?; - .system_lib => |system_lib| { - if ((system_lib.search_strategy != prev_search_strategy or - system_lib.preferred_link_mode != prev_preferred_link_mode) and - self.linkage != .static) - { - switch (system_lib.search_strategy) { - .no_fallback => switch (system_lib.preferred_link_mode) { - .Dynamic => try zig_args.append("-search_dylibs_only"), - .Static => try zig_args.append("-search_static_only"), - }, - .paths_first => switch (system_lib.preferred_link_mode) { - .Dynamic => try zig_args.append("-search_paths_first"), - .Static => try zig_args.append("-search_paths_first_static"), - }, - .mode_first => switch (system_lib.preferred_link_mode) { - .Dynamic => try zig_args.append("-search_dylibs_first"), - .Static => try zig_args.append("-search_static_first"), - }, - } - prev_search_strategy = system_lib.search_strategy; - prev_preferred_link_mode = system_lib.preferred_link_mode; + // While walking transitive dependencies, if a given link object is + // already included in a library, it should not redundantly be + // placed on the linker line of the dependee. + const my_responsibility = compile == self; + const already_linked = !my_responsibility and compile.isDynamicLibrary(); + + // Inherit dependencies on darwin frameworks. + if (!already_linked) { + for (module.frameworks.keys(), module.frameworks.values()) |name, info| { + try frameworks.put(arena, name, info); } + } - const prefix: []const u8 = prefix: { - if (system_lib.needed) break :prefix "-needed-l"; - if (system_lib.weak) break :prefix "-weak-l"; - break :prefix "-l"; - }; - switch (system_lib.use_pkg_config) { - .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })), - .yes, .force => { - if (self.runPkgConfig(system_lib.name)) |args| { - try zig_args.appendSlice(args); - } else |err| switch (err) { - error.PkgConfigInvalidOutput, - error.PkgConfigCrashed, - error.PkgConfigFailed, - error.PkgConfigNotInstalled, - error.PackageNotFound, - => switch (system_lib.use_pkg_config) { - .yes => { - // pkg-config failed, so fall back to linking the library - // by name directly. - try zig_args.append(b.fmt("{s}{s}", .{ - prefix, - system_lib.name, - })); + // Inherit dependencies on system libraries and static libraries. + for (module.link_objects.items) |link_object| { + switch (link_object) { + .static_path => |static_path| { + if (my_responsibility) { + try zig_args.append(static_path.getPath(b)); + total_linker_objects += 1; + } + }, + .system_lib => |system_lib| { + if ((try seen_system_libs.fetchPut(arena, system_lib.name, {})) != null) + continue; + + if (already_linked) + continue; + + if ((system_lib.search_strategy != prev_search_strategy or + system_lib.preferred_link_mode != prev_preferred_link_mode) and + self.linkage != .static) + { + switch (system_lib.search_strategy) { + .no_fallback => switch (system_lib.preferred_link_mode) { + .Dynamic => try zig_args.append("-search_dylibs_only"), + .Static => try zig_args.append("-search_static_only"), + }, + .paths_first => switch (system_lib.preferred_link_mode) { + .Dynamic => try zig_args.append("-search_paths_first"), + .Static => try zig_args.append("-search_paths_first_static"), }, - .force => { - panic("pkg-config failed for library {s}", .{system_lib.name}); + .mode_first => switch (system_lib.preferred_link_mode) { + .Dynamic => try zig_args.append("-search_dylibs_first"), + .Static => try zig_args.append("-search_static_first"), }, - .no => unreachable, + } + prev_search_strategy = system_lib.search_strategy; + prev_preferred_link_mode = system_lib.preferred_link_mode; + } + + const prefix: []const u8 = prefix: { + if (system_lib.needed) break :prefix "-needed-l"; + if (system_lib.weak) break :prefix "-weak-l"; + break :prefix "-l"; + }; + switch (system_lib.use_pkg_config) { + .no => try zig_args.append(b.fmt("{s}{s}", .{ prefix, system_lib.name })), + .yes, .force => { + if (self.runPkgConfig(system_lib.name)) |args| { + try zig_args.appendSlice(args); + } else |err| switch (err) { + error.PkgConfigInvalidOutput, + error.PkgConfigCrashed, + error.PkgConfigFailed, + error.PkgConfigNotInstalled, + error.PackageNotFound, + => switch (system_lib.use_pkg_config) { + .yes => { + // pkg-config failed, so fall back to linking the library + // by name directly. + try zig_args.append(b.fmt("{s}{s}", .{ + prefix, + system_lib.name, + })); + }, + .force => { + panic("pkg-config failed for library {s}", .{system_lib.name}); + }, + .no => unreachable, + }, + + else => |e| return e, + } }, + } + }, + .other_step => |other| { + switch (other.kind) { + .exe => return step.fail("cannot link with an executable build artifact", .{}), + .@"test" => return step.fail("cannot link with a test", .{}), + .obj => { + const included_in_lib = !my_responsibility and + compile.kind == .lib and other.kind == .obj; + if (!already_linked and !included_in_lib) { + try zig_args.append(other.getEmittedBin().getPath(b)); + total_linker_objects += 1; + } + }, + .lib => l: { + const other_produces_implib = other.producesImplib(); + const other_is_static = other_produces_implib or other.isStaticLibrary(); + + if (self.isStaticLibrary() and other_is_static) { + // Avoid putting a static library inside a static library. + break :l; + } + + // For DLLs, we must link against the implib. + // For everything else, we directly link + // against the library file. + const full_path_lib = if (other_produces_implib) + other.getGeneratedFilePath("generated_implib", &self.step) + else + other.getGeneratedFilePath("generated_bin", &self.step); + + try zig_args.append(full_path_lib); + total_linker_objects += 1; + + if (other.linkage == Linkage.dynamic and + self.rootModuleTarget().os.tag != .windows) + { + if (fs.path.dirname(full_path_lib)) |dirname| { + try zig_args.append("-rpath"); + try zig_args.append(dirname); + } + } + }, + } + }, + .assembly_file => |asm_file| l: { + if (!my_responsibility) break :l; - else => |e| return e, + if (prev_has_cflags) { + try zig_args.append("-cflags"); + try zig_args.append("--"); + prev_has_cflags = false; } + try zig_args.append(asm_file.getPath(b)); + total_linker_objects += 1; }, - } - }, - .assembly_file => |asm_file| { - if (prev_has_cflags) { - try zig_args.append("-cflags"); - try zig_args.append("--"); - prev_has_cflags = false; - } - try zig_args.append(asm_file.getPath(b)); - }, + .c_source_file => |c_source_file| l: { + if (!my_responsibility) break :l; + + if (c_source_file.flags.len == 0) { + if (prev_has_cflags) { + try zig_args.append("-cflags"); + try zig_args.append("--"); + prev_has_cflags = false; + } + } else { + try zig_args.append("-cflags"); + for (c_source_file.flags) |arg| { + try zig_args.append(arg); + } + try zig_args.append("--"); + prev_has_cflags = true; + } + try zig_args.append(c_source_file.file.getPath(b)); + total_linker_objects += 1; + }, - .c_source_file => |c_source_file| { - if (c_source_file.flags.len == 0) { - if (prev_has_cflags) { - try zig_args.append("-cflags"); - try zig_args.append("--"); - prev_has_cflags = false; - } - } else { - try zig_args.append("-cflags"); - for (c_source_file.flags) |arg| { - try zig_args.append(arg); - } - try zig_args.append("--"); - prev_has_cflags = true; - } - try zig_args.append(c_source_file.file.getPath(b)); - }, + .c_source_files => |c_source_files| l: { + if (!my_responsibility) break :l; + + if (c_source_files.flags.len == 0) { + if (prev_has_cflags) { + try zig_args.append("-cflags"); + try zig_args.append("--"); + prev_has_cflags = false; + } + } else { + try zig_args.append("-cflags"); + for (c_source_files.flags) |flag| { + try zig_args.append(flag); + } + try zig_args.append("--"); + prev_has_cflags = true; + } - .c_source_files => |c_source_files| { - if (c_source_files.flags.len == 0) { - if (prev_has_cflags) { - try zig_args.append("-cflags"); - try zig_args.append("--"); - prev_has_cflags = false; - } - } else { - try zig_args.append("-cflags"); - for (c_source_files.flags) |flag| { - try zig_args.append(flag); - } - try zig_args.append("--"); - prev_has_cflags = true; + if (c_source_files.dependency) |dep| { + for (c_source_files.files) |file| { + try zig_args.append(dep.builder.pathFromRoot(file)); + } + } else { + for (c_source_files.files) |file| { + try zig_args.append(b.pathFromRoot(file)); + } + } + total_linker_objects += c_source_files.files.len; + }, + + .win32_resource_file => |rc_source_file| l: { + if (!my_responsibility) break :l; + + if (rc_source_file.flags.len == 0) { + if (prev_has_rcflags) { + try zig_args.append("-rcflags"); + try zig_args.append("--"); + prev_has_rcflags = false; + } + } else { + try zig_args.append("-rcflags"); + for (rc_source_file.flags) |arg| { + try zig_args.append(arg); + } + try zig_args.append("--"); + prev_has_rcflags = true; + } + try zig_args.append(rc_source_file.file.getPath(b)); + total_linker_objects += 1; + }, } - if (c_source_files.dependency) |dep| { - for (c_source_files.files) |file| { - try zig_args.append(dep.builder.pathFromRoot(file)); - } - } else { - for (c_source_files.files) |file| { - try zig_args.append(b.pathFromRoot(file)); + } + + // We need to emit the --mod argument here so that the above link objects + // have the correct parent module, but only if the module is part of + // this compilation. + if (cli_named_modules.modules.getIndex(module)) |module_cli_index| { + const module_cli_name = cli_named_modules.names.keys()[module_cli_index]; + try module.appendZigProcessFlags(&zig_args, step); + + // --dep arguments + try zig_args.ensureUnusedCapacity(module.import_table.count() * 2); + for (module.import_table.keys(), module.import_table.values()) |name, dep| { + const dep_index = cli_named_modules.modules.getIndex(dep).?; + const dep_cli_name = cli_named_modules.names.keys()[dep_index]; + zig_args.appendAssumeCapacity("--dep"); + if (std.mem.eql(u8, dep_cli_name, name)) { + zig_args.appendAssumeCapacity(dep_cli_name); + } else { + zig_args.appendAssumeCapacity(b.fmt("{s}={s}", .{ name, dep_cli_name })); } } - }, - .win32_resource_file => |rc_source_file| { - if (rc_source_file.flags.len == 0) { - if (prev_has_rcflags) { - try zig_args.append("-rcflags"); - try zig_args.append("--"); - prev_has_rcflags = false; - } - } else { - try zig_args.append("-rcflags"); - for (rc_source_file.flags) |arg| { - try zig_args.append(arg); - } - try zig_args.append("--"); - prev_has_rcflags = true; + // The CLI assumes if it sees a --mod argument that it is a zig + // compilation unit. If there is no root source file, then this + // is not a zig compilation unit - it is perhaps a set of + // linker objects, or C source files instead. + // In such case, there will be only one module, so we can leave + // off the naming here. + if (module.root_source_file) |lp| { + const src = lp.getPath2(b, step); + try zig_args.appendSlice(&.{ "--mod", module_cli_name, src }); } - try zig_args.append(rc_source_file.file.getPath(b)); - }, + } } - } - if (self.win32_manifest) |manifest_file| { - try zig_args.append(manifest_file.getPath(b)); - } + if (total_linker_objects == 0) { + return step.fail("the linker needs one or more objects to link", .{}); + } - if (transitive_deps.is_linking_libcpp) { - try zig_args.append("-lc++"); + for (frameworks.keys(), frameworks.values()) |name, info| { + if (info.needed) { + try zig_args.append("-needed_framework"); + } else if (info.weak) { + try zig_args.append("-weak_framework"); + } else { + try zig_args.append("-framework"); + } + try zig_args.append(name); + } + + if (self.is_linking_libcpp) { + try zig_args.append("-lc++"); + } + + if (self.is_linking_libc) { + try zig_args.append("-lc"); + } } - if (transitive_deps.is_linking_libc) { - try zig_args.append("-lc"); + if (self.win32_manifest) |manifest_file| { + try zig_args.append(manifest_file.getPath(b)); } if (self.image_base) |image_base| { @@ -1702,17 +1296,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (self.generated_llvm_ir != null) try zig_args.append("-femit-llvm-ir"); if (self.generated_h != null) try zig_args.append("-femit-h"); - try addFlag(&zig_args, "strip", self.strip); - try addFlag(&zig_args, "formatted-panics", self.formatted_panics); - try addFlag(&zig_args, "unwind-tables", self.unwind_tables); - - if (self.dwarf_format) |dwarf_format| { - try zig_args.append(switch (dwarf_format) { - .@"32" => "-gdwarf32", - .@"64" => "-gdwarf64", - }); - } - switch (self.compress_debug_sections) { .none => {}, .zlib => try zig_args.append("--compress-debug-sections=zlib"), @@ -1769,11 +1352,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(libc_file); } - switch (self.optimize) { - .Debug => {}, // Skip since it's the default. - else => try zig_args.append(b.fmt("-O{s}", .{@tagName(self.optimize)})), - } - try zig_args.append("--cache-dir"); try zig_args.append(b.cache_root.path orelse "."); @@ -1793,11 +1371,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(b.fmt("{}", .{version})); } - if (self.target.isDarwin()) { + if (self.rootModuleTarget().isDarwin()) { const install_name = self.install_name orelse b.fmt("@rpath/{s}{s}{s}", .{ - self.target.libPrefix(), + self.rootModuleTarget().libPrefix(), self.name, - self.target.dynamicLibSuffix(), + self.rootModuleTarget().dynamicLibSuffix(), }); try zig_args.append("-install_name"); try zig_args.append(install_name); @@ -1808,11 +1386,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); } if (self.pagezero_size) |pagezero_size| { - const size = try std.fmt.allocPrint(b.allocator, "{x}", .{pagezero_size}); + const size = try std.fmt.allocPrint(arena, "{x}", .{pagezero_size}); try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size }); } if (self.headerpad_size) |headerpad_size| { - const size = try std.fmt.allocPrint(b.allocator, "{x}", .{headerpad_size}); + const size = try std.fmt.allocPrint(arena, "{x}", .{headerpad_size}); try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size }); } if (self.headerpad_max_install_names) { @@ -1823,27 +1401,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt); - try addFlag(&zig_args, "single-threaded", self.single_threaded); - if (self.disable_stack_probing) { - try zig_args.append("-fno-stack-check"); - } - try addFlag(&zig_args, "stack-protector", self.stack_protector); - if (self.red_zone) |red_zone| { - if (red_zone) { - try zig_args.append("-mred-zone"); - } else { - try zig_args.append("-mno-red-zone"); - } - } - try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer); try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns); - - if (self.disable_sanitize_c) { - try zig_args.append("-fno-sanitize-c"); - } - if (self.sanitize_thread) { - try zig_args.append("-fsanitize-thread"); - } if (self.rdynamic) { try zig_args.append("-rdynamic"); } @@ -1875,29 +1433,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(b.fmt("--global-base={d}", .{global_base})); } - if (self.code_model != .default) { - try zig_args.append("-mcmodel"); - try zig_args.append(@tagName(self.code_model)); - } if (self.wasi_exec_model) |model| { try zig_args.append(b.fmt("-mexec-model={s}", .{@tagName(model)})); } - for (self.export_symbol_names) |symbol_name| { - try zig_args.append(b.fmt("--export={s}", .{symbol_name})); - } - - if (!self.target.isNative()) { - try zig_args.appendSlice(&.{ - "-target", try self.target.zigTriple(b.allocator), - "-mcpu", try std.Build.serializeCpu(b.allocator, self.target.getCpu()), - }); - - if (self.target.dynamic_linker.get()) |dynamic_linker| { - try zig_args.append("--dynamic-linker"); - try zig_args.append(dynamic_linker); - } - } - if (self.linker_script) |linker_script| { try zig_args.append("--script"); try zig_args.append(linker_script.getPath(b)); @@ -1921,103 +1459,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } } - try self.appendModuleArgs(&zig_args); - - for (self.include_dirs.items) |include_dir| { - switch (include_dir) { - .path => |include_path| { - try zig_args.append("-I"); - try zig_args.append(include_path.getPath(b)); - }, - .path_system => |include_path| { - try zig_args.append("-isystem"); - try zig_args.append(include_path.getPath(b)); - }, - .path_after => |include_path| { - try zig_args.append("-idirafter"); - try zig_args.append(include_path.getPath(b)); - }, - .framework_path => |include_path| { - try zig_args.append("-F"); - try zig_args.append(include_path.getPath2(b, step)); - }, - .framework_path_system => |include_path| { - try zig_args.append("-iframework"); - try zig_args.append(include_path.getPath2(b, step)); - }, - .other_step => |other| { - if (other.generated_h) |header| { - try zig_args.append("-isystem"); - try zig_args.append(fs.path.dirname(header.path.?).?); - } - if (other.installed_headers.items.len > 0) { - try zig_args.append("-I"); - try zig_args.append(b.pathJoin(&.{ - other.step.owner.install_prefix, "include", - })); - } - }, - .config_header_step => |config_header| { - const full_file_path = config_header.output_file.path.?; - const header_dir_path = full_file_path[0 .. full_file_path.len - config_header.include_path.len]; - try zig_args.appendSlice(&.{ "-I", header_dir_path }); - }, - } - } - - for (self.c_macros.items) |c_macro| { - try zig_args.append("-D"); - try zig_args.append(c_macro); - } - - try zig_args.ensureUnusedCapacity(2 * self.lib_paths.items.len); - for (self.lib_paths.items) |lib_path| { - zig_args.appendAssumeCapacity("-L"); - zig_args.appendAssumeCapacity(lib_path.getPath2(b, step)); - } - - try zig_args.ensureUnusedCapacity(2 * self.rpaths.items.len); - for (self.rpaths.items) |rpath| { - zig_args.appendAssumeCapacity("-rpath"); - - if (self.target_info.target.isDarwin()) switch (rpath) { - .path, .cwd_relative => |path| { - // On Darwin, we should not try to expand special runtime paths such as - // * @executable_path - // * @loader_path - if (mem.startsWith(u8, path, "@executable_path") or - mem.startsWith(u8, path, "@loader_path")) - { - zig_args.appendAssumeCapacity(path); - continue; - } - }, - .generated, .dependency => {}, - }; - - zig_args.appendAssumeCapacity(rpath.getPath2(b, step)); - } - - { - var it = self.frameworks.iterator(); - while (it.next()) |entry| { - const name = entry.key_ptr.*; - const info = entry.value_ptr.*; - if (info.needed) { - try zig_args.append("-needed_framework"); - } else if (info.weak) { - try zig_args.append("-weak_framework"); - } else { - try zig_args.append("-framework"); - } - try zig_args.append(name); - } - } - if (b.sysroot) |sysroot| { try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot }); } + // -I and -L arguments that appear after the last --mod argument apply to all modules. for (b.search_prefixes.items) |search_prefix| { var prefix_dir = fs.cwd().openDir(search_prefix, .{}) catch |err| { return step.fail("unable to open prefix directory '{s}': {s}", .{ @@ -2032,7 +1478,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (prefix_dir.accessZ("lib", .{})) |_| { try zig_args.appendSlice(&.{ - "-L", try fs.path.join(b.allocator, &.{ search_prefix, "lib" }), + "-L", try fs.path.join(arena, &.{ search_prefix, "lib" }), }); } else |err| switch (err) { error.FileNotFound => {}, @@ -2043,7 +1489,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (prefix_dir.accessZ("include", .{})) |_| { try zig_args.appendSlice(&.{ - "-I", try fs.path.join(b.allocator, &.{ search_prefix, "include" }), + "-I", try fs.path.join(arena, &.{ search_prefix, "include" }), }); } else |err| switch (err) { error.FileNotFound => {}, @@ -2058,7 +1504,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(@tagName(self.rc_includes)); } - try addFlag(&zig_args, "valgrind", self.valgrind_support); try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath); if (self.build_id) |build_id| { @@ -2075,12 +1520,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(dir.getPath(b)); } - if (self.main_mod_path) |dir| { - try zig_args.append("--main-mod-path"); - try zig_args.append(dir.getPath(b)); - } - - try addFlag(&zig_args, "PIC", self.force_pic); try addFlag(&zig_args, "PIE", self.pie); try addFlag(&zig_args, "lto", self.want_lto); @@ -2117,12 +1556,12 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try b.cache_root.handle.makePath("args"); const args_to_escape = zig_args.items[2..]; - var escaped_args = try ArrayList([]const u8).initCapacity(b.allocator, args_to_escape.len); + var escaped_args = try ArrayList([]const u8).initCapacity(arena, args_to_escape.len); arg_blk: for (args_to_escape) |arg| { for (arg, 0..) |c, arg_idx| { if (c == '\\' or c == '"') { // Slow path for arguments that need to be escaped. We'll need to allocate and copy - var escaped = try ArrayList(u8).initCapacity(b.allocator, arg.len + 1); + var escaped = try ArrayList(u8).initCapacity(arena, arg.len + 1); const writer = escaped.writer(); try writer.writeAll(arg[0..arg_idx]); for (arg[arg_idx..]) |to_escape| { @@ -2138,8 +1577,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { // Write the args to zig-cache/args/ to avoid conflicts with // other zig build commands running in parallel. - const partially_quoted = try std.mem.join(b.allocator, "\" \"", escaped_args.items); - const args = try std.mem.concat(b.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" }); + const partially_quoted = try std.mem.join(arena, "\" \"", escaped_args.items); + const args = try std.mem.concat(arena, u8, &[_][]const u8{ "\"", partially_quoted, "\"" }); var args_hash: [Sha256.digest_length]u8 = undefined; Sha256.hash(args, &args_hash, .{}); @@ -2153,9 +1592,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const args_file = "args" ++ fs.path.sep_str ++ args_hex_hash; try b.cache_root.handle.writeFile(args_file, args); - const resolved_args_file = try mem.concat(b.allocator, u8, &.{ + const resolved_args_file = try mem.concat(arena, u8, &.{ "@", - try b.cache_root.join(b.allocator, &.{args_file}), + try b.cache_root.join(arena, &.{args_file}), }); zig_args.shrinkRetainingCapacity(2); @@ -2223,7 +1662,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and - self.version != null and self.target.wantSharedLibSymLinks()) + self.version != null and std.Build.wantSharedLibSymLinks(self.rootModuleTarget())) { try doAtomicSymLinks( step, @@ -2234,43 +1673,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } } -fn isLibCLibrary(name: []const u8) bool { - const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" }; - for (libc_libraries) |libc_lib_name| { - if (mem.eql(u8, name, libc_lib_name)) - return true; - } - return false; -} - -fn isLibCppLibrary(name: []const u8) bool { - const libcpp_libraries = [_][]const u8{ "c++", "stdc++" }; - for (libcpp_libraries) |libcpp_lib_name| { - if (mem.eql(u8, name, libcpp_lib_name)) - return true; - } - return false; -} - -/// Returned slice must be freed by the caller. -fn findVcpkgRoot(allocator: Allocator) !?[]const u8 { - const appdata_path = try fs.getAppDataDir(allocator, "vcpkg"); - defer allocator.free(appdata_path); - - const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" }); - defer allocator.free(path_file); - - const file = fs.cwd().openFile(path_file, .{}) catch return null; - defer file.close(); - - const size = @as(usize, @intCast(try file.getEndPos())); - const vcpkg_path = try allocator.alloc(u8, size); - const size_read = try file.read(vcpkg_path); - std.debug.assert(size == size_read); - - return vcpkg_path; -} - pub fn doAtomicSymLinks( step: *Step, output_path: []const u8, @@ -2345,67 +1747,6 @@ fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) } } -const TransitiveDeps = struct { - link_objects: ArrayList(LinkObject), - seen_system_libs: StringHashMap(void), - seen_steps: std.AutoHashMap(*const Step, void), - is_linking_libcpp: bool, - is_linking_libc: bool, - frameworks: *StringHashMap(FrameworkLinkInfo), - - fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void { - try td.link_objects.ensureUnusedCapacity(link_objects.len); - - for (link_objects) |link_object| { - try td.link_objects.append(link_object); - switch (link_object) { - .other_step => |other| try addInner(td, other, other.isDynamicLibrary()), - else => {}, - } - } - } - - fn addInner(td: *TransitiveDeps, other: *Compile, dyn: bool) !void { - // Inherit dependency on libc and libc++ - td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp; - td.is_linking_libc = td.is_linking_libc or other.is_linking_libc; - - // Inherit dependencies on darwin frameworks - if (!dyn) { - var it = other.frameworks.iterator(); - while (it.next()) |framework| { - try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*); - } - } - - // Inherit dependencies on system libraries and static libraries. - for (other.link_objects.items) |other_link_object| { - switch (other_link_object) { - .system_lib => |system_lib| { - if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null) - continue; - - if (dyn) - continue; - - try td.link_objects.append(other_link_object); - }, - .other_step => |inner_other| { - if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null) - continue; - - const included_in_lib = (other.kind == .lib and inner_other.kind == .obj); - if (!dyn and !included_in_lib) - try td.link_objects.append(other_link_object); - - try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary()); - }, - else => continue, - } - } - } -}; - fn checkCompileErrors(self: *Compile) !void { // Clear this field so that it does not get printed by the build runner. const actual_eb = self.step.result_error_bundle; @@ -2490,3 +1831,8 @@ fn matchCompileError(actual: []const u8, expected: []const u8) bool { } return false; } + +pub fn rootModuleTarget(c: *Compile) std.Target { + // The root module is always given a target, so we know this to be non-null. + return c.root_module.resolved_target.?.result; +} diff --git a/lib/std/Build/Step/ConfigHeader.zig b/lib/std/Build/Step/ConfigHeader.zig index 9f040349c677..c352b2460c01 100644 --- a/lib/std/Build/Step/ConfigHeader.zig +++ b/lib/std/Build/Step/ConfigHeader.zig @@ -15,9 +15,6 @@ pub const Style = union(enum) { /// Start with nothing, like blank, and output a nasm .asm file. nasm, - /// deprecated: use `getPath` - pub const getFileSource = getPath; - pub fn getPath(style: Style) ?std.Build.LazyPath { switch (style) { .autoconf, .cmake => |s| return s, @@ -107,9 +104,6 @@ pub fn addValues(self: *ConfigHeader, values: anytype) void { return addValuesInner(self, values) catch @panic("OOM"); } -/// deprecated: use `getOutput` -pub const getFileSource = getOutput; - pub fn getOutput(self: *ConfigHeader) std.Build.LazyPath { return .{ .generated = &self.output_file }; } diff --git a/lib/std/Build/Step/InstallArtifact.zig b/lib/std/Build/Step/InstallArtifact.zig index f3c9ca3bef0e..b9c3acfbc912 100644 --- a/lib/std/Build/Step/InstallArtifact.zig +++ b/lib/std/Build/Step/InstallArtifact.zig @@ -94,7 +94,7 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile, options: Options) *Ins .dylib_symlinks = if (options.dylib_symlinks orelse (dest_dir != null and artifact.isDynamicLibrary() and artifact.version != null and - artifact.target.wantSharedLibSymLinks())) .{ + std.Build.wantSharedLibSymLinks(artifact.rootModuleTarget()))) .{ .major_only_filename = artifact.major_only_filename.?, .name_only_filename = artifact.name_only_filename.?, } else null, diff --git a/lib/std/Build/Step/Options.zig b/lib/std/Build/Step/Options.zig index 8255be5cf410..7f334a2e69c5 100644 --- a/lib/std/Build/Step/Options.zig +++ b/lib/std/Build/Step/Options.zig @@ -165,9 +165,6 @@ fn printLiteral(out: anytype, val: anytype, indent: u8) !void { } } -/// deprecated: use `addOptionPath` -pub const addOptionFileSource = addOptionPath; - /// The value is the path in the cache dir. /// Adds a dependency automatically. pub fn addOptionPath( @@ -189,8 +186,7 @@ pub fn addOptionArtifact(self: *Options, name: []const u8, artifact: *Step.Compi pub fn createModule(self: *Options) *std.Build.Module { return self.step.owner.createModule(.{ - .source_file = self.getOutput(), - .dependencies = &.{}, + .root_source_file = self.getOutput(), }); } @@ -298,7 +294,10 @@ test Options { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); - const host = try std.zig.system.NativeTargetInfo.detect(.{}); + const host: std.Build.ResolvedTarget = .{ + .query = .{}, + .result = try std.zig.system.resolveTargetQuery(.{}), + }; var cache: std.Build.Cache = .{ .gpa = arena.allocator(), diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 5555b03e94ef..d48fe2a2f178 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -197,16 +197,10 @@ pub fn addPrefixedOutputFileArg( return .{ .generated = &output.generated_file }; } -/// deprecated: use `addFileArg` -pub const addFileSourceArg = addFileArg; - pub fn addFileArg(self: *Run, lp: std.Build.LazyPath) void { self.addPrefixedFileArg("", lp); } -// deprecated: use `addPrefixedFileArg` -pub const addPrefixedFileSourceArg = addPrefixedFileArg; - pub fn addPrefixedFileArg(self: *Run, prefix: []const u8, lp: std.Build.LazyPath) void { const b = self.step.owner; @@ -488,7 +482,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { man.hash.addBytes(file_path); }, .artifact => |artifact| { - if (artifact.target.isWindows()) { + if (artifact.rootModuleTarget().os.tag == .windows) { // On Windows we don't have rpaths so we have to add .dll search paths to PATH self.addPathForDynLibs(artifact); } @@ -682,8 +676,10 @@ fn runCommand( else => break :interpret, } - const need_cross_glibc = exe.target.isGnuLibC() and exe.is_linking_libc; - switch (b.host.getExternalExecutor(&exe.target_info, .{ + const need_cross_glibc = exe.rootModuleTarget().isGnuLibC() and + exe.is_linking_libc; + const other_target = exe.root_module.resolved_target.?.result; + switch (std.zig.system.getExternalExecutor(b.host.result, &other_target, .{ .qemu_fixes_dl = need_cross_glibc and b.glibc_runtimes_dir != null, .link_libc = exe.is_linking_libc, })) { @@ -714,9 +710,9 @@ fn runCommand( // needs the directory to be called "i686" rather than // "x86" which is why we do it manually here. const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; - const cpu_arch = exe.target.getCpuArch(); - const os_tag = exe.target.getOsTag(); - const abi = exe.target.getAbi(); + const cpu_arch = exe.rootModuleTarget().cpu.arch; + const os_tag = exe.rootModuleTarget().os.tag; + const abi = exe.rootModuleTarget().abi; const cpu_arch_name: []const u8 = if (cpu_arch == .x86) "i686" else @@ -756,7 +752,7 @@ fn runCommand( .bad_dl => |foreign_dl| { if (allow_skip) return error.MakeSkipped; - const host_dl = b.host.dynamic_linker.get() orelse "(none)"; + const host_dl = b.host.result.dynamic_linker.get() orelse "(none)"; return step.fail( \\the host system is unable to execute binaries from the target @@ -768,8 +764,8 @@ fn runCommand( .bad_os_or_cpu => { if (allow_skip) return error.MakeSkipped; - const host_name = try b.host.target.zigTriple(b.allocator); - const foreign_name = try exe.target.zigTriple(b.allocator); + const host_name = try b.host.result.zigTriple(b.allocator); + const foreign_name = try exe.rootModuleTarget().zigTriple(b.allocator); return step.fail("the host system ({s}) is unable to execute binaries from the target ({s})", .{ host_name, foreign_name, @@ -777,7 +773,7 @@ fn runCommand( }, } - if (exe.target.isWindows()) { + if (exe.rootModuleTarget().os.tag == .windows) { // On Windows we don't have rpaths so we have to add .dll search paths to PATH self.addPathForDynLibs(exe); } @@ -1295,15 +1291,15 @@ fn evalGeneric(self: *Run, child: *std.process.Child) !StdIoResult { fn addPathForDynLibs(self: *Run, artifact: *Step.Compile) void { const b = self.step.owner; - for (artifact.link_objects.items) |link_object| { - switch (link_object) { - .other_step => |other| { - if (other.target.isWindows() and other.isDynamicLibrary()) { - addPathDir(self, fs.path.dirname(other.getEmittedBin().getPath(b)).?); - addPathForDynLibs(self, other); - } - }, - else => {}, + var it = artifact.root_module.iterateDependencies(artifact, true); + while (it.next()) |item| { + const other = item.compile.?; + if (item.module == &other.root_module) { + if (item.module.resolved_target.?.result.os.tag == .windows and + other.isDynamicLibrary()) + { + addPathDir(self, fs.path.dirname(other.getEmittedBin().getPath(b)).?); + } } } } @@ -1320,8 +1316,8 @@ fn failForeign( return error.MakeSkipped; const b = self.step.owner; - const host_name = try b.host.target.zigTriple(b.allocator); - const foreign_name = try exe.target.zigTriple(b.allocator); + const host_name = try b.host.result.zigTriple(b.allocator); + const foreign_name = try exe.rootModuleTarget().zigTriple(b.allocator); return self.step.fail( \\unable to spawn foreign binary '{s}' ({s}) on host system ({s}) diff --git a/lib/std/Build/Step/TranslateC.zig b/lib/std/Build/Step/TranslateC.zig index 26e59dad9074..31919ba11a5a 100644 --- a/lib/std/Build/Step/TranslateC.zig +++ b/lib/std/Build/Step/TranslateC.zig @@ -2,7 +2,6 @@ const std = @import("std"); const Step = std.Build.Step; const fs = std.fs; const mem = std.mem; -const CrossTarget = std.zig.CrossTarget; const TranslateC = @This(); @@ -13,7 +12,7 @@ source: std.Build.LazyPath, include_dirs: std.ArrayList([]const u8), c_macros: std.ArrayList([]const u8), out_basename: []const u8, -target: CrossTarget, +target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, output_file: std.Build.GeneratedFile, link_libc: bool, @@ -21,7 +20,7 @@ use_clang: bool, pub const Options = struct { source_file: std.Build.LazyPath, - target: CrossTarget, + target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, link_libc: bool = true, use_clang: bool = true, @@ -54,7 +53,7 @@ pub fn create(owner: *std.Build, options: Options) *TranslateC { pub const AddExecutableOptions = struct { name: ?[]const u8 = null, version: ?std.SemanticVersion = null, - target: ?CrossTarget = null, + target: ?std.Build.ResolvedTarget = null, optimize: ?std.builtin.OptimizeMode = null, linkage: ?Step.Compile.Linkage = null, }; @@ -139,9 +138,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try argv_list.append("--listen=-"); - if (!self.target.isNative()) { + if (!self.target.query.isNative()) { try argv_list.append("-target"); - try argv_list.append(try self.target.zigTriple(b.allocator)); + try argv_list.append(try self.target.query.zigTriple(b.allocator)); } switch (self.optimize) { diff --git a/lib/std/Build/Step/WriteFile.zig b/lib/std/Build/Step/WriteFile.zig index 01019274fd91..7fcf06249f38 100644 --- a/lib/std/Build/Step/WriteFile.zig +++ b/lib/std/Build/Step/WriteFile.zig @@ -28,9 +28,6 @@ pub const File = struct { sub_path: []const u8, contents: Contents, - /// deprecated: use `getPath` - pub const getFileSource = getPath; - pub fn getPath(self: *File) std.Build.LazyPath { return .{ .generated = &self.generated_file }; } @@ -126,10 +123,6 @@ pub fn addBytesToSource(wf: *WriteFile, bytes: []const u8, sub_path: []const u8) }) catch @panic("OOM"); } -pub const getFileSource = @compileError("Deprecated; use the return value from add()/addCopyFile(), or use files[i].getPath()"); - -pub const getDirectorySource = getDirectory; - /// Returns a `LazyPath` representing the base directory that contains all the /// files from this `WriteFile`. pub fn getDirectory(wf: *WriteFile) std.Build.LazyPath { diff --git a/lib/std/Target.zig b/lib/std/Target.zig new file mode 100644 index 000000000000..f8ce63b37fb8 --- /dev/null +++ b/lib/std/Target.zig @@ -0,0 +1,2727 @@ +//! All the details about the machine that will be executing code. +//! Unlike `Query` which might leave some things as "default" or "host", this +//! data is fully resolved into a concrete set of OS versions, CPU features, +//! etc. + +cpu: Cpu, +os: Os, +abi: Abi, +ofmt: ObjectFormat, +dynamic_linker: DynamicLinker = DynamicLinker.none, + +pub const Query = @import("Target/Query.zig"); + +pub const Os = struct { + tag: Tag, + version_range: VersionRange, + + pub const Tag = enum { + freestanding, + ananas, + cloudabi, + dragonfly, + freebsd, + fuchsia, + ios, + kfreebsd, + linux, + lv2, + macos, + netbsd, + openbsd, + solaris, + uefi, + windows, + zos, + haiku, + minix, + rtems, + nacl, + aix, + cuda, + nvcl, + amdhsa, + ps4, + ps5, + elfiamcu, + tvos, + watchos, + driverkit, + mesa3d, + contiki, + amdpal, + hermit, + hurd, + wasi, + emscripten, + shadermodel, + liteos, + opencl, + glsl450, + vulkan, + plan9, + illumos, + other, + + pub inline fn isDarwin(tag: Tag) bool { + return switch (tag) { + .ios, .macos, .watchos, .tvos => true, + else => false, + }; + } + + pub inline fn isBSD(tag: Tag) bool { + return tag.isDarwin() or switch (tag) { + .kfreebsd, .freebsd, .openbsd, .netbsd, .dragonfly => true, + else => false, + }; + } + + pub inline fn isSolarish(tag: Tag) bool { + return tag == .solaris or tag == .illumos; + } + + pub fn dynamicLibSuffix(tag: Tag) [:0]const u8 { + if (tag.isDarwin()) { + return ".dylib"; + } + switch (tag) { + .windows => return ".dll", + else => return ".so", + } + } + + pub fn defaultVersionRange(tag: Tag, arch: Cpu.Arch) Os { + return .{ + .tag = tag, + .version_range = VersionRange.default(tag, arch), + }; + } + }; + + /// Based on NTDDI version constants from + /// https://docs.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt + pub const WindowsVersion = enum(u32) { + nt4 = 0x04000000, + win2k = 0x05000000, + xp = 0x05010000, + ws2003 = 0x05020000, + vista = 0x06000000, + win7 = 0x06010000, + win8 = 0x06020000, + win8_1 = 0x06030000, + win10 = 0x0A000000, //aka win10_th1 + win10_th2 = 0x0A000001, + win10_rs1 = 0x0A000002, + win10_rs2 = 0x0A000003, + win10_rs3 = 0x0A000004, + win10_rs4 = 0x0A000005, + win10_rs5 = 0x0A000006, + win10_19h1 = 0x0A000007, + win10_vb = 0x0A000008, //aka win10_19h2 + win10_mn = 0x0A000009, //aka win10_20h1 + win10_fe = 0x0A00000A, //aka win10_20h2 + _, + + /// Latest Windows version that the Zig Standard Library is aware of + pub const latest = WindowsVersion.win10_fe; + + /// Compared against build numbers reported by the runtime to distinguish win10 versions, + /// where 0x0A000000 + index corresponds to the WindowsVersion u32 value. + pub const known_win10_build_numbers = [_]u32{ + 10240, //win10 aka win10_th1 + 10586, //win10_th2 + 14393, //win10_rs1 + 15063, //win10_rs2 + 16299, //win10_rs3 + 17134, //win10_rs4 + 17763, //win10_rs5 + 18362, //win10_19h1 + 18363, //win10_vb aka win10_19h2 + 19041, //win10_mn aka win10_20h1 + 19042, //win10_fe aka win10_20h2 + }; + + /// Returns whether the first version `self` is newer (greater) than or equal to the second version `ver`. + pub inline fn isAtLeast(self: WindowsVersion, ver: WindowsVersion) bool { + return @intFromEnum(self) >= @intFromEnum(ver); + } + + pub const Range = struct { + min: WindowsVersion, + max: WindowsVersion, + + pub inline fn includesVersion(self: Range, ver: WindowsVersion) bool { + return @intFromEnum(ver) >= @intFromEnum(self.min) and @intFromEnum(ver) <= @intFromEnum(self.max); + } + + /// Checks if system is guaranteed to be at least `version` or older than `version`. + /// Returns `null` if a runtime check is required. + pub inline fn isAtLeast(self: Range, ver: WindowsVersion) ?bool { + if (@intFromEnum(self.min) >= @intFromEnum(ver)) return true; + if (@intFromEnum(self.max) < @intFromEnum(ver)) return false; + return null; + } + }; + + /// This function is defined to serialize a Zig source code representation of this + /// type, that, when parsed, will deserialize into the same data. + pub fn format( + self: WindowsVersion, + comptime fmt: []const u8, + _: std.fmt.FormatOptions, + out_stream: anytype, + ) !void { + if (comptime std.mem.eql(u8, fmt, "s")) { + if (@intFromEnum(self) >= @intFromEnum(WindowsVersion.nt4) and @intFromEnum(self) <= @intFromEnum(WindowsVersion.latest)) { + try std.fmt.format(out_stream, ".{s}", .{@tagName(self)}); + } else { + // TODO this code path breaks zig triples, but it is used in `builtin` + try std.fmt.format(out_stream, "@enumFromInt(Target.Os.WindowsVersion, 0x{X:0>8})", .{@intFromEnum(self)}); + } + } else if (fmt.len == 0) { + if (@intFromEnum(self) >= @intFromEnum(WindowsVersion.nt4) and @intFromEnum(self) <= @intFromEnum(WindowsVersion.latest)) { + try std.fmt.format(out_stream, "WindowsVersion.{s}", .{@tagName(self)}); + } else { + try std.fmt.format(out_stream, "WindowsVersion(0x{X:0>8})", .{@intFromEnum(self)}); + } + } else { + std.fmt.invalidFmtError(fmt, self); + } + } + }; + + pub const LinuxVersionRange = struct { + range: std.SemanticVersion.Range, + glibc: std.SemanticVersion, + + pub inline fn includesVersion(self: LinuxVersionRange, ver: std.SemanticVersion) bool { + return self.range.includesVersion(ver); + } + + /// Checks if system is guaranteed to be at least `version` or older than `version`. + /// Returns `null` if a runtime check is required. + pub inline fn isAtLeast(self: LinuxVersionRange, ver: std.SemanticVersion) ?bool { + return self.range.isAtLeast(ver); + } + }; + + /// The version ranges here represent the minimum OS version to be supported + /// and the maximum OS version to be supported. The default values represent + /// the range that the Zig Standard Library bases its abstractions on. + /// + /// The minimum version of the range is the main setting to tweak for a target. + /// Usually, the maximum target OS version will remain the default, which is + /// the latest released version of the OS. + /// + /// To test at compile time if the target is guaranteed to support a given OS feature, + /// one should check that the minimum version of the range is greater than or equal to + /// the version the feature was introduced in. + /// + /// To test at compile time if the target certainly will not support a given OS feature, + /// one should check that the maximum version of the range is less than the version the + /// feature was introduced in. + /// + /// If neither of these cases apply, a runtime check should be used to determine if the + /// target supports a given OS feature. + /// + /// Binaries built with a given maximum version will continue to function on newer + /// operating system versions. However, such a binary may not take full advantage of the + /// newer operating system APIs. + /// + /// See `Os.isAtLeast`. + pub const VersionRange = union { + none: void, + semver: std.SemanticVersion.Range, + linux: LinuxVersionRange, + windows: WindowsVersion.Range, + + /// The default `VersionRange` represents the range that the Zig Standard Library + /// bases its abstractions on. + pub fn default(tag: Tag, arch: Cpu.Arch) VersionRange { + switch (tag) { + .freestanding, + .ananas, + .cloudabi, + .fuchsia, + .kfreebsd, + .lv2, + .zos, + .haiku, + .minix, + .rtems, + .nacl, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .ps5, + .elfiamcu, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .hurd, + .wasi, + .emscripten, + .driverkit, + .shadermodel, + .liteos, + .uefi, + .opencl, // TODO: OpenCL versions + .glsl450, // TODO: GLSL versions + .vulkan, + .plan9, + .illumos, + .other, + => return .{ .none = {} }, + + .freebsd => return .{ + .semver = std.SemanticVersion.Range{ + .min = .{ .major = 12, .minor = 0, .patch = 0 }, + .max = .{ .major = 14, .minor = 0, .patch = 0 }, + }, + }, + .macos => return switch (arch) { + .aarch64 => VersionRange{ + .semver = .{ + .min = .{ .major = 11, .minor = 7, .patch = 1 }, + .max = .{ .major = 14, .minor = 1, .patch = 0 }, + }, + }, + .x86_64 => VersionRange{ + .semver = .{ + .min = .{ .major = 11, .minor = 7, .patch = 1 }, + .max = .{ .major = 14, .minor = 1, .patch = 0 }, + }, + }, + else => unreachable, + }, + .ios => return .{ + .semver = .{ + .min = .{ .major = 12, .minor = 0, .patch = 0 }, + .max = .{ .major = 17, .minor = 1, .patch = 0 }, + }, + }, + .watchos => return .{ + .semver = .{ + .min = .{ .major = 6, .minor = 0, .patch = 0 }, + .max = .{ .major = 10, .minor = 1, .patch = 0 }, + }, + }, + .tvos => return .{ + .semver = .{ + .min = .{ .major = 13, .minor = 0, .patch = 0 }, + .max = .{ .major = 17, .minor = 1, .patch = 0 }, + }, + }, + .netbsd => return .{ + .semver = .{ + .min = .{ .major = 8, .minor = 0, .patch = 0 }, + .max = .{ .major = 10, .minor = 0, .patch = 0 }, + }, + }, + .openbsd => return .{ + .semver = .{ + .min = .{ .major = 6, .minor = 8, .patch = 0 }, + .max = .{ .major = 7, .minor = 4, .patch = 0 }, + }, + }, + .dragonfly => return .{ + .semver = .{ + .min = .{ .major = 5, .minor = 8, .patch = 0 }, + .max = .{ .major = 6, .minor = 4, .patch = 0 }, + }, + }, + .solaris => return .{ + .semver = .{ + .min = .{ .major = 5, .minor = 11, .patch = 0 }, + .max = .{ .major = 5, .minor = 11, .patch = 0 }, + }, + }, + + .linux => return .{ + .linux = .{ + .range = .{ + .min = .{ .major = 4, .minor = 19, .patch = 0 }, + .max = .{ .major = 6, .minor = 5, .patch = 7 }, + }, + .glibc = .{ .major = 2, .minor = 28, .patch = 0 }, + }, + }, + + .windows => return .{ + .windows = .{ + .min = .win8_1, + .max = WindowsVersion.latest, + }, + }, + } + } + }; + + pub const TaggedVersionRange = union(enum) { + none: void, + semver: std.SemanticVersion.Range, + linux: LinuxVersionRange, + windows: WindowsVersion.Range, + }; + + /// Provides a tagged union. `Target` does not store the tag because it is + /// redundant with the OS tag; this function abstracts that part away. + pub inline fn getVersionRange(self: Os) TaggedVersionRange { + switch (self.tag) { + .linux => return TaggedVersionRange{ .linux = self.version_range.linux }, + .windows => return TaggedVersionRange{ .windows = self.version_range.windows }, + + .freebsd, + .macos, + .ios, + .tvos, + .watchos, + .netbsd, + .openbsd, + .dragonfly, + .solaris, + => return TaggedVersionRange{ .semver = self.version_range.semver }, + + else => return .none, + } + } + + /// Checks if system is guaranteed to be at least `version` or older than `version`. + /// Returns `null` if a runtime check is required. + pub inline fn isAtLeast(self: Os, comptime tag: Tag, version: anytype) ?bool { + if (self.tag != tag) return false; + + return switch (tag) { + .linux => self.version_range.linux.isAtLeast(version), + .windows => self.version_range.windows.isAtLeast(version), + else => self.version_range.semver.isAtLeast(version), + }; + } + + /// On Darwin, we always link libSystem which contains libc. + /// Similarly on FreeBSD and NetBSD we always link system libc + /// since this is the stable syscall interface. + pub fn requiresLibC(os: Os) bool { + return switch (os.tag) { + .freebsd, + .netbsd, + .macos, + .ios, + .tvos, + .watchos, + .dragonfly, + .openbsd, + .haiku, + .solaris, + .illumos, + => true, + + .linux, + .windows, + .freestanding, + .ananas, + .cloudabi, + .fuchsia, + .kfreebsd, + .lv2, + .zos, + .minix, + .rtems, + .nacl, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .ps5, + .elfiamcu, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .hurd, + .wasi, + .emscripten, + .driverkit, + .shadermodel, + .liteos, + .uefi, + .opencl, + .glsl450, + .vulkan, + .plan9, + .other, + => false, + }; + } +}; + +pub const aarch64 = @import("Target/aarch64.zig"); +pub const arc = @import("Target/arc.zig"); +pub const amdgpu = @import("Target/amdgpu.zig"); +pub const arm = @import("Target/arm.zig"); +pub const avr = @import("Target/avr.zig"); +pub const bpf = @import("Target/bpf.zig"); +pub const csky = @import("Target/csky.zig"); +pub const hexagon = @import("Target/hexagon.zig"); +pub const loongarch = @import("Target/loongarch.zig"); +pub const m68k = @import("Target/m68k.zig"); +pub const mips = @import("Target/mips.zig"); +pub const msp430 = @import("Target/msp430.zig"); +pub const nvptx = @import("Target/nvptx.zig"); +pub const powerpc = @import("Target/powerpc.zig"); +pub const riscv = @import("Target/riscv.zig"); +pub const sparc = @import("Target/sparc.zig"); +pub const spirv = @import("Target/spirv.zig"); +pub const s390x = @import("Target/s390x.zig"); +pub const ve = @import("Target/ve.zig"); +pub const wasm = @import("Target/wasm.zig"); +pub const x86 = @import("Target/x86.zig"); +pub const xtensa = @import("Target/xtensa.zig"); + +pub const Abi = enum { + none, + gnu, + gnuabin32, + gnuabi64, + gnueabi, + gnueabihf, + gnuf32, + gnuf64, + gnusf, + gnux32, + gnuilp32, + code16, + eabi, + eabihf, + android, + musl, + musleabi, + musleabihf, + muslx32, + msvc, + itanium, + cygnus, + coreclr, + simulator, + macabi, + pixel, + vertex, + geometry, + hull, + domain, + compute, + library, + raygeneration, + intersection, + anyhit, + closesthit, + miss, + callable, + mesh, + amplification, + + pub fn default(arch: Cpu.Arch, target_os: Os) Abi { + if (arch.isWasm()) { + return .musl; + } + switch (target_os.tag) { + .freestanding, + .ananas, + .cloudabi, + .dragonfly, + .lv2, + .zos, + .minix, + .rtems, + .nacl, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .ps5, + .elfiamcu, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .other, + => return .eabi, + .openbsd, + .freebsd, + .fuchsia, + .kfreebsd, + .netbsd, + .hurd, + .haiku, + .windows, + => return .gnu, + .uefi => return .msvc, + .linux, + .wasi, + .emscripten, + => return .musl, + .opencl, // TODO: SPIR-V ABIs with Linkage capability + .glsl450, + .vulkan, + .plan9, // TODO specify abi + .macos, + .ios, + .tvos, + .watchos, + .driverkit, + .shadermodel, + .liteos, // TODO: audit this + .solaris, + .illumos, + => return .none, + } + } + + pub inline fn isGnu(abi: Abi) bool { + return switch (abi) { + .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true, + else => false, + }; + } + + pub inline fn isMusl(abi: Abi) bool { + return switch (abi) { + .musl, .musleabi, .musleabihf => true, + else => false, + }; + } + + pub inline fn floatAbi(abi: Abi) FloatAbi { + return switch (abi) { + .gnueabihf, + .eabihf, + .musleabihf, + => .hard, + else => .soft, + }; + } +}; + +pub const ObjectFormat = enum { + /// Common Object File Format (Windows) + coff, + /// DirectX Container + dxcontainer, + /// Executable and Linking Format + elf, + /// macOS relocatables + macho, + /// Standard, Portable Intermediate Representation V + spirv, + /// WebAssembly + wasm, + /// C source code + c, + /// Intel IHEX + hex, + /// Machine code with no metadata. + raw, + /// Plan 9 from Bell Labs + plan9, + /// Nvidia PTX format + nvptx, + + pub fn fileExt(of: ObjectFormat, cpu_arch: Cpu.Arch) [:0]const u8 { + return switch (of) { + .coff => ".obj", + .elf, .macho, .wasm => ".o", + .c => ".c", + .spirv => ".spv", + .hex => ".ihex", + .raw => ".bin", + .plan9 => plan9Ext(cpu_arch), + .nvptx => ".ptx", + .dxcontainer => ".dxil", + }; + } + + pub fn default(os_tag: Os.Tag, cpu_arch: Cpu.Arch) ObjectFormat { + return switch (os_tag) { + .windows, .uefi => .coff, + .ios, .macos, .watchos, .tvos => .macho, + .plan9 => .plan9, + else => return switch (cpu_arch) { + .wasm32, .wasm64 => .wasm, + .spirv32, .spirv64 => .spirv, + .nvptx, .nvptx64 => .nvptx, + else => .elf, + }, + }; + } +}; + +pub const SubSystem = enum { + Console, + Windows, + Posix, + Native, + EfiApplication, + EfiBootServiceDriver, + EfiRom, + EfiRuntimeDriver, +}; + +pub const Cpu = struct { + /// Architecture + arch: Arch, + + /// The CPU model to target. It has a set of features + /// which are overridden with the `features` field. + model: *const Model, + + /// An explicit list of the entire CPU feature set. It may differ from the specific CPU model's features. + features: Feature.Set, + + pub const Feature = struct { + /// The bit index into `Set`. Has a default value of `undefined` because the canonical + /// structures are populated via comptime logic. + index: Set.Index = undefined, + + /// Has a default value of `undefined` because the canonical + /// structures are populated via comptime logic. + name: []const u8 = undefined, + + /// If this corresponds to an LLVM-recognized feature, this will be populated; + /// otherwise null. + llvm_name: ?[:0]const u8, + + /// Human-friendly UTF-8 text. + description: []const u8, + + /// Sparse `Set` of features this depends on. + dependencies: Set, + + /// A bit set of all the features. + pub const Set = struct { + ints: [usize_count]usize, + + pub const needed_bit_count = 288; + pub const byte_count = (needed_bit_count + 7) / 8; + pub const usize_count = (byte_count + (@sizeOf(usize) - 1)) / @sizeOf(usize); + pub const Index = std.math.Log2Int(std.meta.Int(.unsigned, usize_count * @bitSizeOf(usize))); + pub const ShiftInt = std.math.Log2Int(usize); + + pub const empty = Set{ .ints = [1]usize{0} ** usize_count }; + + pub fn isEmpty(set: Set) bool { + return for (set.ints) |x| { + if (x != 0) break false; + } else true; + } + + pub fn isEnabled(set: Set, arch_feature_index: Index) bool { + const usize_index = arch_feature_index / @bitSizeOf(usize); + const bit_index = @as(ShiftInt, @intCast(arch_feature_index % @bitSizeOf(usize))); + return (set.ints[usize_index] & (@as(usize, 1) << bit_index)) != 0; + } + + /// Adds the specified feature but not its dependencies. + pub fn addFeature(set: *Set, arch_feature_index: Index) void { + const usize_index = arch_feature_index / @bitSizeOf(usize); + const bit_index = @as(ShiftInt, @intCast(arch_feature_index % @bitSizeOf(usize))); + set.ints[usize_index] |= @as(usize, 1) << bit_index; + } + + /// Adds the specified feature set but not its dependencies. + pub fn addFeatureSet(set: *Set, other_set: Set) void { + switch (builtin.zig_backend) { + .stage2_x86_64 => { + for (&set.ints, other_set.ints) |*set_int, other_set_int| set_int.* |= other_set_int; + }, + else => { + set.ints = @as(@Vector(usize_count, usize), set.ints) | @as(@Vector(usize_count, usize), other_set.ints); + }, + } + } + + /// Removes the specified feature but not its dependents. + pub fn removeFeature(set: *Set, arch_feature_index: Index) void { + const usize_index = arch_feature_index / @bitSizeOf(usize); + const bit_index = @as(ShiftInt, @intCast(arch_feature_index % @bitSizeOf(usize))); + set.ints[usize_index] &= ~(@as(usize, 1) << bit_index); + } + + /// Removes the specified feature but not its dependents. + pub fn removeFeatureSet(set: *Set, other_set: Set) void { + switch (builtin.zig_backend) { + .stage2_x86_64 => { + for (&set.ints, other_set.ints) |*set_int, other_set_int| set_int.* &= ~other_set_int; + }, + else => { + set.ints = @as(@Vector(usize_count, usize), set.ints) & ~@as(@Vector(usize_count, usize), other_set.ints); + }, + } + } + + pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void { + @setEvalBranchQuota(1000000); + + var old = set.ints; + while (true) { + for (all_features_list, 0..) |feature, index_usize| { + const index = @as(Index, @intCast(index_usize)); + if (set.isEnabled(index)) { + set.addFeatureSet(feature.dependencies); + } + } + const nothing_changed = std.mem.eql(usize, &old, &set.ints); + if (nothing_changed) return; + old = set.ints; + } + } + + pub fn asBytes(set: *const Set) *const [byte_count]u8 { + return @as(*const [byte_count]u8, @ptrCast(&set.ints)); + } + + pub fn eql(set: Set, other_set: Set) bool { + return std.mem.eql(usize, &set.ints, &other_set.ints); + } + + pub fn isSuperSetOf(set: Set, other_set: Set) bool { + switch (builtin.zig_backend) { + .stage2_x86_64 => { + var result = true; + for (&set.ints, other_set.ints) |*set_int, other_set_int| + result = result and (set_int.* & other_set_int) == other_set_int; + return result; + }, + else => { + const V = @Vector(usize_count, usize); + const set_v: V = set.ints; + const other_v: V = other_set.ints; + return @reduce(.And, (set_v & other_v) == other_v); + }, + } + } + }; + + pub fn feature_set_fns(comptime F: type) type { + return struct { + /// Populates only the feature bits specified. + pub fn featureSet(features: []const F) Set { + var x = Set.empty; + for (features) |feature| { + x.addFeature(@intFromEnum(feature)); + } + return x; + } + + /// Returns true if the specified feature is enabled. + pub fn featureSetHas(set: Set, feature: F) bool { + return set.isEnabled(@intFromEnum(feature)); + } + + /// Returns true if any specified feature is enabled. + pub fn featureSetHasAny(set: Set, features: anytype) bool { + inline for (features) |feature| { + if (set.isEnabled(@intFromEnum(@as(F, feature)))) return true; + } + return false; + } + + /// Returns true if every specified feature is enabled. + pub fn featureSetHasAll(set: Set, features: anytype) bool { + inline for (features) |feature| { + if (!set.isEnabled(@intFromEnum(@as(F, feature)))) return false; + } + return true; + } + }; + } + }; + + pub const Arch = enum { + arm, + armeb, + aarch64, + aarch64_be, + aarch64_32, + arc, + avr, + bpfel, + bpfeb, + csky, + dxil, + hexagon, + loongarch32, + loongarch64, + m68k, + mips, + mipsel, + mips64, + mips64el, + msp430, + powerpc, + powerpcle, + powerpc64, + powerpc64le, + r600, + amdgcn, + riscv32, + riscv64, + sparc, + sparc64, + sparcel, + s390x, + tce, + tcele, + thumb, + thumbeb, + x86, + x86_64, + xcore, + xtensa, + nvptx, + nvptx64, + le32, + le64, + amdil, + amdil64, + hsail, + hsail64, + spir, + spir64, + spirv32, + spirv64, + kalimba, + shave, + lanai, + wasm32, + wasm64, + renderscript32, + renderscript64, + ve, + // Stage1 currently assumes that architectures above this comment + // map one-to-one with the ZigLLVM_ArchType enum. + spu_2, + + pub inline fn isX86(arch: Arch) bool { + return switch (arch) { + .x86, .x86_64 => true, + else => false, + }; + } + + pub inline fn isARM(arch: Arch) bool { + return switch (arch) { + .arm, .armeb => true, + else => false, + }; + } + + pub inline fn isAARCH64(arch: Arch) bool { + return switch (arch) { + .aarch64, .aarch64_be, .aarch64_32 => true, + else => false, + }; + } + + pub inline fn isThumb(arch: Arch) bool { + return switch (arch) { + .thumb, .thumbeb => true, + else => false, + }; + } + + pub inline fn isArmOrThumb(arch: Arch) bool { + return arch.isARM() or arch.isThumb(); + } + + pub inline fn isWasm(arch: Arch) bool { + return switch (arch) { + .wasm32, .wasm64 => true, + else => false, + }; + } + + pub inline fn isRISCV(arch: Arch) bool { + return switch (arch) { + .riscv32, .riscv64 => true, + else => false, + }; + } + + pub inline fn isMIPS(arch: Arch) bool { + return switch (arch) { + .mips, .mipsel, .mips64, .mips64el => true, + else => false, + }; + } + + pub inline fn isPPC(arch: Arch) bool { + return switch (arch) { + .powerpc, .powerpcle => true, + else => false, + }; + } + + pub inline fn isPPC64(arch: Arch) bool { + return switch (arch) { + .powerpc64, .powerpc64le => true, + else => false, + }; + } + + pub inline fn isSPARC(arch: Arch) bool { + return switch (arch) { + .sparc, .sparcel, .sparc64 => true, + else => false, + }; + } + + pub inline fn isSpirV(arch: Arch) bool { + return switch (arch) { + .spirv32, .spirv64 => true, + else => false, + }; + } + + pub inline fn isBpf(arch: Arch) bool { + return switch (arch) { + .bpfel, .bpfeb => true, + else => false, + }; + } + + pub inline fn isNvptx(arch: Arch) bool { + return switch (arch) { + .nvptx, .nvptx64 => true, + else => false, + }; + } + + pub fn parseCpuModel(arch: Arch, cpu_name: []const u8) !*const Cpu.Model { + for (arch.allCpuModels()) |cpu| { + if (std.mem.eql(u8, cpu_name, cpu.name)) { + return cpu; + } + } + return error.UnknownCpuModel; + } + + pub fn toElfMachine(arch: Arch) std.elf.EM { + return switch (arch) { + .avr => .AVR, + .msp430 => .MSP430, + .arc => .ARC, + .arm => .ARM, + .armeb => .ARM, + .hexagon => .HEXAGON, + .dxil => .NONE, + .m68k => .@"68K", + .le32 => .NONE, + .mips => .MIPS, + .mipsel => .MIPS_RS3_LE, + .powerpc, .powerpcle => .PPC, + .r600 => .NONE, + .riscv32 => .RISCV, + .sparc => .SPARC, + .sparcel => .SPARC, + .tce => .NONE, + .tcele => .NONE, + .thumb => .ARM, + .thumbeb => .ARM, + .x86 => .@"386", + .xcore => .XCORE, + .xtensa => .XTENSA, + .nvptx => .NONE, + .amdil => .NONE, + .hsail => .NONE, + .spir => .NONE, + .kalimba => .CSR_KALIMBA, + .shave => .NONE, + .lanai => .LANAI, + .wasm32 => .NONE, + .renderscript32 => .NONE, + .aarch64_32 => .AARCH64, + .aarch64 => .AARCH64, + .aarch64_be => .AARCH64, + .mips64 => .MIPS, + .mips64el => .MIPS_RS3_LE, + .powerpc64 => .PPC64, + .powerpc64le => .PPC64, + .riscv64 => .RISCV, + .x86_64 => .X86_64, + .nvptx64 => .NONE, + .le64 => .NONE, + .amdil64 => .NONE, + .hsail64 => .NONE, + .spir64 => .NONE, + .wasm64 => .NONE, + .renderscript64 => .NONE, + .amdgcn => .AMDGPU, + .bpfel => .BPF, + .bpfeb => .BPF, + .csky => .CSKY, + .sparc64 => .SPARCV9, + .s390x => .S390, + .ve => .NONE, + .spu_2 => .SPU_2, + .spirv32 => .NONE, + .spirv64 => .NONE, + .loongarch32 => .NONE, + .loongarch64 => .NONE, + }; + } + + pub fn toCoffMachine(arch: Arch) std.coff.MachineType { + return switch (arch) { + .avr => .Unknown, + .msp430 => .Unknown, + .arc => .Unknown, + .arm => .ARM, + .armeb => .Unknown, + .dxil => .Unknown, + .hexagon => .Unknown, + .m68k => .Unknown, + .le32 => .Unknown, + .mips => .Unknown, + .mipsel => .Unknown, + .powerpc, .powerpcle => .POWERPC, + .r600 => .Unknown, + .riscv32 => .RISCV32, + .sparc => .Unknown, + .sparcel => .Unknown, + .tce => .Unknown, + .tcele => .Unknown, + .thumb => .Thumb, + .thumbeb => .Thumb, + .x86 => .I386, + .xcore => .Unknown, + .xtensa => .Unknown, + .nvptx => .Unknown, + .amdil => .Unknown, + .hsail => .Unknown, + .spir => .Unknown, + .kalimba => .Unknown, + .shave => .Unknown, + .lanai => .Unknown, + .wasm32 => .Unknown, + .renderscript32 => .Unknown, + .aarch64_32 => .ARM64, + .aarch64 => .ARM64, + .aarch64_be => .ARM64, + .mips64 => .Unknown, + .mips64el => .Unknown, + .powerpc64 => .Unknown, + .powerpc64le => .Unknown, + .riscv64 => .RISCV64, + .x86_64 => .X64, + .nvptx64 => .Unknown, + .le64 => .Unknown, + .amdil64 => .Unknown, + .hsail64 => .Unknown, + .spir64 => .Unknown, + .wasm64 => .Unknown, + .renderscript64 => .Unknown, + .amdgcn => .Unknown, + .bpfel => .Unknown, + .bpfeb => .Unknown, + .csky => .Unknown, + .sparc64 => .Unknown, + .s390x => .Unknown, + .ve => .Unknown, + .spu_2 => .Unknown, + .spirv32 => .Unknown, + .spirv64 => .Unknown, + .loongarch32 => .Unknown, + .loongarch64 => .Unknown, + }; + } + + pub fn endian(arch: Arch) std.builtin.Endian { + return switch (arch) { + .avr, + .arm, + .aarch64_32, + .aarch64, + .amdgcn, + .amdil, + .amdil64, + .bpfel, + .csky, + .xtensa, + .hexagon, + .hsail, + .hsail64, + .kalimba, + .le32, + .le64, + .mipsel, + .mips64el, + .msp430, + .nvptx, + .nvptx64, + .sparcel, + .tcele, + .powerpcle, + .powerpc64le, + .r600, + .riscv32, + .riscv64, + .x86, + .x86_64, + .wasm32, + .wasm64, + .xcore, + .thumb, + .spir, + .spir64, + .renderscript32, + .renderscript64, + .shave, + .ve, + .spu_2, + // GPU bitness is opaque. For now, assume little endian. + .spirv32, + .spirv64, + .dxil, + .loongarch32, + .loongarch64, + .arc, + => .little, + + .armeb, + .aarch64_be, + .bpfeb, + .m68k, + .mips, + .mips64, + .powerpc, + .powerpc64, + .thumbeb, + .sparc, + .sparc64, + .tce, + .lanai, + .s390x, + => .big, + }; + } + + /// Returns whether this architecture supports the address space + pub fn supportsAddressSpace(arch: Arch, address_space: std.builtin.AddressSpace) bool { + const is_nvptx = arch == .nvptx or arch == .nvptx64; + const is_spirv = arch == .spirv32 or arch == .spirv64; + const is_gpu = is_nvptx or is_spirv or arch == .amdgcn; + return switch (address_space) { + .generic => true, + .fs, .gs, .ss => arch == .x86_64 or arch == .x86, + .global, .constant, .local, .shared => is_gpu, + .param => is_nvptx, + // TODO this should also check how many flash banks the cpu has + .flash, .flash1, .flash2, .flash3, .flash4, .flash5 => arch == .avr, + }; + } + + /// Returns a name that matches the lib/std/target/* source file name. + pub fn genericName(arch: Arch) []const u8 { + return switch (arch) { + .arm, .armeb, .thumb, .thumbeb => "arm", + .aarch64, .aarch64_be, .aarch64_32 => "aarch64", + .bpfel, .bpfeb => "bpf", + .loongarch32, .loongarch64 => "loongarch", + .mips, .mipsel, .mips64, .mips64el => "mips", + .powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc", + .amdgcn => "amdgpu", + .riscv32, .riscv64 => "riscv", + .sparc, .sparc64, .sparcel => "sparc", + .s390x => "s390x", + .x86, .x86_64 => "x86", + .nvptx, .nvptx64 => "nvptx", + .wasm32, .wasm64 => "wasm", + .spirv32, .spirv64 => "spirv", + else => @tagName(arch), + }; + } + + /// All CPU features Zig is aware of, sorted lexicographically by name. + pub fn allFeaturesList(arch: Arch) []const Cpu.Feature { + return switch (arch) { + .arm, .armeb, .thumb, .thumbeb => &arm.all_features, + .aarch64, .aarch64_be, .aarch64_32 => &aarch64.all_features, + .arc => &arc.all_features, + .avr => &avr.all_features, + .bpfel, .bpfeb => &bpf.all_features, + .csky => &csky.all_features, + .hexagon => &hexagon.all_features, + .loongarch32, .loongarch64 => &loongarch.all_features, + .m68k => &m68k.all_features, + .mips, .mipsel, .mips64, .mips64el => &mips.all_features, + .msp430 => &msp430.all_features, + .powerpc, .powerpcle, .powerpc64, .powerpc64le => &powerpc.all_features, + .amdgcn => &amdgpu.all_features, + .riscv32, .riscv64 => &riscv.all_features, + .sparc, .sparc64, .sparcel => &sparc.all_features, + .spirv32, .spirv64 => &spirv.all_features, + .s390x => &s390x.all_features, + .x86, .x86_64 => &x86.all_features, + .xtensa => &xtensa.all_features, + .nvptx, .nvptx64 => &nvptx.all_features, + .ve => &ve.all_features, + .wasm32, .wasm64 => &wasm.all_features, + + else => &[0]Cpu.Feature{}, + }; + } + + /// All processors Zig is aware of, sorted lexicographically by name. + pub fn allCpuModels(arch: Arch) []const *const Cpu.Model { + return switch (arch) { + .arc => comptime allCpusFromDecls(arc.cpu), + .arm, .armeb, .thumb, .thumbeb => comptime allCpusFromDecls(arm.cpu), + .aarch64, .aarch64_be, .aarch64_32 => comptime allCpusFromDecls(aarch64.cpu), + .avr => comptime allCpusFromDecls(avr.cpu), + .bpfel, .bpfeb => comptime allCpusFromDecls(bpf.cpu), + .csky => comptime allCpusFromDecls(csky.cpu), + .hexagon => comptime allCpusFromDecls(hexagon.cpu), + .loongarch32, .loongarch64 => comptime allCpusFromDecls(loongarch.cpu), + .m68k => comptime allCpusFromDecls(m68k.cpu), + .mips, .mipsel, .mips64, .mips64el => comptime allCpusFromDecls(mips.cpu), + .msp430 => comptime allCpusFromDecls(msp430.cpu), + .powerpc, .powerpcle, .powerpc64, .powerpc64le => comptime allCpusFromDecls(powerpc.cpu), + .amdgcn => comptime allCpusFromDecls(amdgpu.cpu), + .riscv32, .riscv64 => comptime allCpusFromDecls(riscv.cpu), + .sparc, .sparc64, .sparcel => comptime allCpusFromDecls(sparc.cpu), + .spirv32, .spirv64 => comptime allCpusFromDecls(spirv.cpu), + .s390x => comptime allCpusFromDecls(s390x.cpu), + .x86, .x86_64 => comptime allCpusFromDecls(x86.cpu), + .xtensa => comptime allCpusFromDecls(xtensa.cpu), + .nvptx, .nvptx64 => comptime allCpusFromDecls(nvptx.cpu), + .ve => comptime allCpusFromDecls(ve.cpu), + .wasm32, .wasm64 => comptime allCpusFromDecls(wasm.cpu), + + else => &[0]*const Model{}, + }; + } + + fn allCpusFromDecls(comptime cpus: type) []const *const Cpu.Model { + const decls = @typeInfo(cpus).Struct.decls; + var array: [decls.len]*const Cpu.Model = undefined; + for (decls, 0..) |decl, i| { + array[i] = &@field(cpus, decl.name); + } + return &array; + } + }; + + pub const Model = struct { + name: []const u8, + llvm_name: ?[:0]const u8, + features: Feature.Set, + + pub fn toCpu(model: *const Model, arch: Arch) Cpu { + var features = model.features; + features.populateDependencies(arch.allFeaturesList()); + return .{ + .arch = arch, + .model = model, + .features = features, + }; + } + + pub fn generic(arch: Arch) *const Model { + const S = struct { + const generic_model = Model{ + .name = "generic", + .llvm_name = null, + .features = Cpu.Feature.Set.empty, + }; + }; + return switch (arch) { + .arm, .armeb, .thumb, .thumbeb => &arm.cpu.generic, + .aarch64, .aarch64_be, .aarch64_32 => &aarch64.cpu.generic, + .avr => &avr.cpu.avr2, + .bpfel, .bpfeb => &bpf.cpu.generic, + .hexagon => &hexagon.cpu.generic, + .loongarch32 => &loongarch.cpu.generic_la32, + .loongarch64 => &loongarch.cpu.generic_la64, + .m68k => &m68k.cpu.generic, + .mips, .mipsel => &mips.cpu.mips32, + .mips64, .mips64el => &mips.cpu.mips64, + .msp430 => &msp430.cpu.generic, + .powerpc => &powerpc.cpu.ppc, + .powerpcle => &powerpc.cpu.ppc, + .powerpc64 => &powerpc.cpu.ppc64, + .powerpc64le => &powerpc.cpu.ppc64le, + .amdgcn => &amdgpu.cpu.generic, + .riscv32 => &riscv.cpu.generic_rv32, + .riscv64 => &riscv.cpu.generic_rv64, + .spirv32, .spirv64 => &spirv.cpu.generic, + .sparc, .sparcel => &sparc.cpu.generic, + .sparc64 => &sparc.cpu.v9, // 64-bit SPARC needs v9 as the baseline + .s390x => &s390x.cpu.generic, + .x86 => &x86.cpu.i386, + .x86_64 => &x86.cpu.x86_64, + .nvptx, .nvptx64 => &nvptx.cpu.sm_20, + .ve => &ve.cpu.generic, + .wasm32, .wasm64 => &wasm.cpu.generic, + + else => &S.generic_model, + }; + } + + pub fn baseline(arch: Arch) *const Model { + return switch (arch) { + .arm, .armeb, .thumb, .thumbeb => &arm.cpu.baseline, + .riscv32 => &riscv.cpu.baseline_rv32, + .riscv64 => &riscv.cpu.baseline_rv64, + .x86 => &x86.cpu.pentium4, + .nvptx, .nvptx64 => &nvptx.cpu.sm_20, + .sparc, .sparcel => &sparc.cpu.v8, + + else => generic(arch), + }; + } + }; + + /// The "default" set of CPU features for cross-compiling. A conservative set + /// of features that is expected to be supported on most available hardware. + pub fn baseline(arch: Arch) Cpu { + return Model.baseline(arch).toCpu(arch); + } +}; + +pub fn zigTriple(self: Target, allocator: Allocator) Allocator.Error![]u8 { + return Query.fromTarget(self).zigTriple(allocator); +} + +pub fn linuxTripleSimple(allocator: Allocator, cpu_arch: Cpu.Arch, os_tag: Os.Tag, abi: Abi) ![]u8 { + return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ @tagName(cpu_arch), @tagName(os_tag), @tagName(abi) }); +} + +pub fn linuxTriple(self: Target, allocator: Allocator) ![]u8 { + return linuxTripleSimple(allocator, self.cpu.arch, self.os.tag, self.abi); +} + +pub fn exeFileExtSimple(cpu_arch: Cpu.Arch, os_tag: Os.Tag) [:0]const u8 { + return switch (os_tag) { + .windows => ".exe", + .uefi => ".efi", + .plan9 => plan9Ext(cpu_arch), + else => switch (cpu_arch) { + .wasm32, .wasm64 => ".wasm", + else => "", + }, + }; +} + +pub fn exeFileExt(self: Target) [:0]const u8 { + return exeFileExtSimple(self.cpu.arch, self.os.tag); +} + +pub fn staticLibSuffix_os_abi(os_tag: Os.Tag, abi: Abi) [:0]const u8 { + if (abi == .msvc) { + return ".lib"; + } + switch (os_tag) { + .windows, .uefi => return ".lib", + else => return ".a", + } +} + +pub fn staticLibSuffix(self: Target) [:0]const u8 { + return staticLibSuffix_os_abi(self.os.tag, self.abi); +} + +pub fn dynamicLibSuffix(self: Target) [:0]const u8 { + return self.os.tag.dynamicLibSuffix(); +} + +pub fn libPrefix_os_abi(os_tag: Os.Tag, abi: Abi) [:0]const u8 { + if (abi == .msvc) { + return ""; + } + switch (os_tag) { + .windows, .uefi => return "", + else => return "lib", + } +} + +pub fn libPrefix(self: Target) [:0]const u8 { + return libPrefix_os_abi(self.os.tag, self.abi); +} + +pub inline fn isMinGW(self: Target) bool { + return self.os.tag == .windows and self.isGnu(); +} + +pub inline fn isGnu(self: Target) bool { + return self.abi.isGnu(); +} + +pub inline fn isMusl(self: Target) bool { + return self.abi.isMusl(); +} + +pub inline fn isAndroid(self: Target) bool { + return self.abi == .android; +} + +pub inline fn isWasm(self: Target) bool { + return self.cpu.arch.isWasm(); +} + +pub inline fn isDarwin(self: Target) bool { + return self.os.tag.isDarwin(); +} + +pub inline fn isBSD(self: Target) bool { + return self.os.tag.isBSD(); +} + +pub inline fn isBpfFreestanding(self: Target) bool { + return self.cpu.arch.isBpf() and self.os.tag == .freestanding; +} + +pub inline fn isGnuLibC_os_tag_abi(os_tag: Os.Tag, abi: Abi) bool { + return os_tag == .linux and abi.isGnu(); +} + +pub inline fn isGnuLibC(self: Target) bool { + return isGnuLibC_os_tag_abi(self.os.tag, self.abi); +} + +pub inline fn supportsNewStackCall(self: Target) bool { + return !self.cpu.arch.isWasm(); +} + +pub inline fn isSpirV(self: Target) bool { + return self.cpu.arch.isSpirV(); +} + +pub const FloatAbi = enum { + hard, + soft, +}; + +pub inline fn getFloatAbi(self: Target) FloatAbi { + return self.abi.floatAbi(); +} + +pub inline fn hasDynamicLinker(self: Target) bool { + if (self.cpu.arch.isWasm()) { + return false; + } + switch (self.os.tag) { + .freestanding, + .ios, + .tvos, + .watchos, + .macos, + .uefi, + .windows, + .emscripten, + .opencl, + .glsl450, + .vulkan, + .plan9, + .other, + => return false, + else => return true, + } +} + +pub const DynamicLinker = struct { + /// Contains the memory used to store the dynamic linker path. This field + /// should not be used directly. See `get` and `set`. This field exists so + /// that this API requires no allocator. + buffer: [255]u8, + + /// Used to construct the dynamic linker path. This field should not be used + /// directly. See `get` and `set`. + max_byte: ?u8, + + pub const none: DynamicLinker = .{ + .buffer = undefined, + .max_byte = null, + }; + + /// Asserts that the length is less than or equal to 255 bytes. + pub fn init(dl_or_null: ?[]const u8) DynamicLinker { + var result: DynamicLinker = undefined; + result.set(dl_or_null); + return result; + } + + /// The returned memory has the same lifetime as the `DynamicLinker`. + pub fn get(self: *const DynamicLinker) ?[]const u8 { + const m: usize = self.max_byte orelse return null; + return self.buffer[0 .. m + 1]; + } + + /// Asserts that the length is less than or equal to 255 bytes. + pub fn set(self: *DynamicLinker, dl_or_null: ?[]const u8) void { + if (dl_or_null) |dl| { + @memcpy(self.buffer[0..dl.len], dl); + self.max_byte = @intCast(dl.len - 1); + } else { + self.max_byte = null; + } + } + + pub fn eql(a: DynamicLinker, b: DynamicLinker) bool { + const a_m = a.max_byte orelse return b.max_byte == null; + const b_m = b.max_byte orelse return false; + if (a_m != b_m) return false; + const a_s = a.buffer[0 .. a_m + 1]; + const b_s = b.buffer[0 .. a_m + 1]; + return std.mem.eql(u8, a_s, b_s); + } +}; + +pub fn standardDynamicLinkerPath(target: Target) DynamicLinker { + return standardDynamicLinkerPath_cpu_os_abi(target.cpu, target.os.tag, target.abi); +} + +pub fn standardDynamicLinkerPath_cpu_os_abi(cpu: Cpu, os_tag: Os.Tag, abi: Abi) DynamicLinker { + var result = DynamicLinker.none; + const S = struct { + fn print(r: *DynamicLinker, comptime fmt: []const u8, args: anytype) DynamicLinker { + r.max_byte = @as(u8, @intCast((std.fmt.bufPrint(&r.buffer, fmt, args) catch unreachable).len - 1)); + return r.*; + } + fn copy(r: *DynamicLinker, s: []const u8) DynamicLinker { + @memcpy(r.buffer[0..s.len], s); + r.max_byte = @as(u8, @intCast(s.len - 1)); + return r.*; + } + }; + const print = S.print; + const copy = S.copy; + + if (abi == .android) { + const suffix = if (ptrBitWidth_cpu_abi(cpu, abi) == 64) "64" else ""; + return print(&result, "/system/bin/linker{s}", .{suffix}); + } + + if (abi.isMusl()) { + const is_arm = switch (cpu.arch) { + .arm, .armeb, .thumb, .thumbeb => true, + else => false, + }; + const arch_part = switch (cpu.arch) { + .arm, .thumb => "arm", + .armeb, .thumbeb => "armeb", + else => |arch| @tagName(arch), + }; + const arch_suffix = if (is_arm and abi.floatAbi() == .hard) "hf" else ""; + return print(&result, "/lib/ld-musl-{s}{s}.so.1", .{ arch_part, arch_suffix }); + } + + switch (os_tag) { + .freebsd => return copy(&result, "/libexec/ld-elf.so.1"), + .netbsd => return copy(&result, "/libexec/ld.elf_so"), + .openbsd => return copy(&result, "/usr/libexec/ld.so"), + .dragonfly => return copy(&result, "/libexec/ld-elf.so.2"), + .solaris, .illumos => return copy(&result, "/lib/64/ld.so.1"), + .linux => switch (cpu.arch) { + .x86, + .sparc, + .sparcel, + => return copy(&result, "/lib/ld-linux.so.2"), + + .aarch64 => return copy(&result, "/lib/ld-linux-aarch64.so.1"), + .aarch64_be => return copy(&result, "/lib/ld-linux-aarch64_be.so.1"), + .aarch64_32 => return copy(&result, "/lib/ld-linux-aarch64_32.so.1"), + + .arm, + .armeb, + .thumb, + .thumbeb, + => return copy(&result, switch (abi.floatAbi()) { + .hard => "/lib/ld-linux-armhf.so.3", + else => "/lib/ld-linux.so.3", + }), + + .mips, + .mipsel, + .mips64, + .mips64el, + => { + const lib_suffix = switch (abi) { + .gnuabin32, .gnux32 => "32", + .gnuabi64 => "64", + else => "", + }; + const is_nan_2008 = mips.featureSetHas(cpu.features, .nan2008); + const loader = if (is_nan_2008) "ld-linux-mipsn8.so.1" else "ld.so.1"; + return print(&result, "/lib{s}/{s}", .{ lib_suffix, loader }); + }, + + .powerpc, .powerpcle => return copy(&result, "/lib/ld.so.1"), + .powerpc64, .powerpc64le => return copy(&result, "/lib64/ld64.so.2"), + .s390x => return copy(&result, "/lib64/ld64.so.1"), + .sparc64 => return copy(&result, "/lib64/ld-linux.so.2"), + .x86_64 => return copy(&result, switch (abi) { + .gnux32 => "/libx32/ld-linux-x32.so.2", + else => "/lib64/ld-linux-x86-64.so.2", + }), + + .riscv32 => return copy(&result, "/lib/ld-linux-riscv32-ilp32.so.1"), + .riscv64 => return copy(&result, "/lib/ld-linux-riscv64-lp64.so.1"), + + // Architectures in this list have been verified as not having a standard + // dynamic linker path. + .wasm32, + .wasm64, + .bpfel, + .bpfeb, + .nvptx, + .nvptx64, + .spu_2, + .avr, + .spirv32, + .spirv64, + => return result, + + // TODO go over each item in this list and either move it to the above list, or + // implement the standard dynamic linker path code for it. + .arc, + .csky, + .hexagon, + .m68k, + .msp430, + .r600, + .amdgcn, + .tce, + .tcele, + .xcore, + .le32, + .le64, + .amdil, + .amdil64, + .hsail, + .hsail64, + .spir, + .spir64, + .kalimba, + .shave, + .lanai, + .renderscript32, + .renderscript64, + .ve, + .dxil, + .loongarch32, + .loongarch64, + .xtensa, + => return result, + }, + + .ios, + .tvos, + .watchos, + .macos, + => return copy(&result, "/usr/lib/dyld"), + + // Operating systems in this list have been verified as not having a standard + // dynamic linker path. + .freestanding, + .uefi, + .windows, + .emscripten, + .wasi, + .opencl, + .glsl450, + .vulkan, + .other, + .plan9, + => return result, + + // TODO revisit when multi-arch for Haiku is available + .haiku => return copy(&result, "/system/runtime_loader"), + + // TODO go over each item in this list and either move it to the above list, or + // implement the standard dynamic linker path code for it. + .ananas, + .cloudabi, + .fuchsia, + .kfreebsd, + .lv2, + .zos, + .minix, + .rtems, + .nacl, + .aix, + .cuda, + .nvcl, + .amdhsa, + .ps4, + .ps5, + .elfiamcu, + .mesa3d, + .contiki, + .amdpal, + .hermit, + .hurd, + .driverkit, + .shadermodel, + .liteos, + => return result, + } +} + +/// 0c spim little-endian MIPS 3000 family +/// 1c 68000 Motorola MC68000 +/// 2c 68020 Motorola MC68020 +/// 5c arm little-endian ARM +/// 6c amd64 AMD64 and compatibles (e.g., Intel EM64T) +/// 7c arm64 ARM64 (ARMv8) +/// 8c 386 Intel x86, i486, Pentium, etc. +/// kc sparc Sun SPARC +/// qc power Power PC +/// vc mips big-endian MIPS 3000 family +pub fn plan9Ext(cpu_arch: Cpu.Arch) [:0]const u8 { + return switch (cpu_arch) { + .arm => ".5", + .x86_64 => ".6", + .aarch64 => ".7", + .x86 => ".8", + .sparc => ".k", + .powerpc, .powerpcle => ".q", + .mips, .mipsel => ".v", + // ISAs without designated characters get 'X' for lack of a better option. + else => ".X", + }; +} + +pub fn maxIntAlignment(target: Target) u16 { + return switch (target.cpu.arch) { + .avr => 1, + .msp430 => 2, + .xcore => 4, + + .arm, + .armeb, + .thumb, + .thumbeb, + .hexagon, + .mips, + .mipsel, + .powerpc, + .powerpcle, + .r600, + .amdgcn, + .riscv32, + .sparc, + .sparcel, + .s390x, + .lanai, + .wasm32, + .wasm64, + => 8, + + .x86 => if (target.ofmt == .c) 16 else return switch (target.os.tag) { + .windows, .uefi => 8, + else => 4, + }, + + // For these, LLVMABIAlignmentOfType(i128) reports 8. Note that 16 + // is a relevant number in three cases: + // 1. Different machine code instruction when loading into SIMD register. + // 2. The C ABI wants 16 for extern structs. + // 3. 16-byte cmpxchg needs 16-byte alignment. + // Same logic for powerpc64, mips64, sparc64. + .x86_64, + .powerpc64, + .powerpc64le, + .mips64, + .mips64el, + .sparc64, + => return switch (target.ofmt) { + .c => 16, + else => 8, + }, + + // Even LLVMABIAlignmentOfType(i128) agrees on these targets. + .aarch64, + .aarch64_be, + .aarch64_32, + .riscv64, + .bpfel, + .bpfeb, + .nvptx, + .nvptx64, + => 16, + + // Below this comment are unverified but based on the fact that C requires + // int128_t to be 16 bytes aligned, it's a safe default. + .spu_2, + .csky, + .arc, + .m68k, + .tce, + .tcele, + .le32, + .amdil, + .hsail, + .spir, + .kalimba, + .renderscript32, + .spirv32, + .shave, + .le64, + .amdil64, + .hsail64, + .spir64, + .renderscript64, + .ve, + .spirv64, + .dxil, + .loongarch32, + .loongarch64, + .xtensa, + => 16, + }; +} + +pub fn ptrBitWidth_cpu_abi(cpu: Cpu, abi: Abi) u16 { + switch (abi) { + .gnux32, .muslx32, .gnuabin32, .gnuilp32 => return 32, + .gnuabi64 => return 64, + else => {}, + } + return switch (cpu.arch) { + .avr, + .msp430, + .spu_2, + => 16, + + .arc, + .arm, + .armeb, + .csky, + .hexagon, + .m68k, + .le32, + .mips, + .mipsel, + .powerpc, + .powerpcle, + .r600, + .riscv32, + .sparcel, + .tce, + .tcele, + .thumb, + .thumbeb, + .x86, + .xcore, + .nvptx, + .amdil, + .hsail, + .spir, + .kalimba, + .shave, + .lanai, + .wasm32, + .renderscript32, + .aarch64_32, + .spirv32, + .loongarch32, + .dxil, + .xtensa, + => 32, + + .aarch64, + .aarch64_be, + .mips64, + .mips64el, + .powerpc64, + .powerpc64le, + .riscv64, + .x86_64, + .nvptx64, + .le64, + .amdil64, + .hsail64, + .spir64, + .wasm64, + .renderscript64, + .amdgcn, + .bpfel, + .bpfeb, + .sparc64, + .s390x, + .ve, + .spirv64, + .loongarch64, + => 64, + + .sparc => if (std.Target.sparc.featureSetHas(cpu.features, .v9)) 64 else 32, + }; +} + +pub fn ptrBitWidth(target: Target) u16 { + return ptrBitWidth_cpu_abi(target.cpu, target.abi); +} + +pub fn stackAlignment(target: Target) u16 { + return switch (target.cpu.arch) { + .m68k => 2, + .amdgcn => 4, + .x86 => switch (target.os.tag) { + .windows, .uefi => 4, + else => 16, + }, + .arm, + .armeb, + .thumb, + .thumbeb, + .mips, + .mipsel, + .sparc, + .sparcel, + => 8, + .aarch64, + .aarch64_be, + .aarch64_32, + .bpfeb, + .bpfel, + .mips64, + .mips64el, + .riscv32, + .riscv64, + .sparc64, + .x86_64, + .ve, + .wasm32, + .wasm64, + => 16, + .powerpc64, + .powerpc64le, + => switch (target.os.tag) { + else => 8, + .linux => 16, + }, + else => @divExact(target.ptrBitWidth(), 8), + }; +} + +/// Default signedness of `char` for the native C compiler for this target +/// Note that char signedness is implementation-defined and many compilers provide +/// an option to override the default signedness e.g. GCC's -funsigned-char / -fsigned-char +pub fn charSignedness(target: Target) std.builtin.Signedness { + switch (target.cpu.arch) { + .aarch64, + .aarch64_32, + .aarch64_be, + .arm, + .armeb, + .thumb, + .thumbeb, + => return if (target.os.tag.isDarwin() or target.os.tag == .windows) .signed else .unsigned, + .powerpc, .powerpc64 => return if (target.os.tag.isDarwin()) .signed else .unsigned, + .powerpcle, + .powerpc64le, + .s390x, + .xcore, + .arc, + .msp430, + .riscv32, + .riscv64, + => return .unsigned, + else => return .signed, + } +} + +pub const CType = enum { + char, + short, + ushort, + int, + uint, + long, + ulong, + longlong, + ulonglong, + float, + double, + longdouble, +}; + +pub fn c_type_byte_size(t: Target, c_type: CType) u16 { + return switch (c_type) { + .char, + .short, + .ushort, + .int, + .uint, + .long, + .ulong, + .longlong, + .ulonglong, + .float, + .double, + => @divExact(c_type_bit_size(t, c_type), 8), + + .longdouble => switch (c_type_bit_size(t, c_type)) { + 16 => 2, + 32 => 4, + 64 => 8, + 80 => @as(u16, @intCast(std.mem.alignForward(usize, 10, c_type_alignment(t, .longdouble)))), + 128 => 16, + else => unreachable, + }, + }; +} + +pub fn c_type_bit_size(target: Target, c_type: CType) u16 { + switch (target.os.tag) { + .freestanding, .other => switch (target.cpu.arch) { + .msp430 => switch (c_type) { + .char => return 8, + .short, .ushort, .int, .uint => return 16, + .float, .long, .ulong => return 32, + .longlong, .ulonglong, .double, .longdouble => return 64, + }, + .avr => switch (c_type) { + .char => return 8, + .short, .ushort, .int, .uint => return 16, + .long, .ulong, .float, .double, .longdouble => return 32, + .longlong, .ulonglong => return 64, + }, + .tce, .tcele => switch (c_type) { + .char => return 8, + .short, .ushort => return 16, + .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32, + .float, .double, .longdouble => return 32, + }, + .mips64, .mips64el => switch (c_type) { + .char => return 8, + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32, + .longlong, .ulonglong, .double => return 64, + .longdouble => return 128, + }, + .x86_64 => switch (c_type) { + .char => return 8, + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => switch (target.abi) { + .gnux32, .muslx32 => return 32, + else => return 64, + }, + .longlong, .ulonglong, .double => return 64, + .longdouble => return 80, + }, + else => switch (c_type) { + .char => return 8, + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return target.ptrBitWidth(), + .longlong, .ulonglong, .double => return 64, + .longdouble => switch (target.cpu.arch) { + .x86 => switch (target.abi) { + .android => return 64, + else => return 80, + }, + + .powerpc, + .powerpcle, + .powerpc64, + .powerpc64le, + => switch (target.abi) { + .musl, + .musleabi, + .musleabihf, + .muslx32, + => return 64, + else => return 128, + }, + + .riscv32, + .riscv64, + .aarch64, + .aarch64_be, + .aarch64_32, + .s390x, + .sparc, + .sparc64, + .sparcel, + .wasm32, + .wasm64, + => return 128, + + else => return 64, + }, + }, + }, + + .linux, + .freebsd, + .netbsd, + .dragonfly, + .openbsd, + .wasi, + .emscripten, + .plan9, + .solaris, + .illumos, + .haiku, + .ananas, + .fuchsia, + .minix, + => switch (target.cpu.arch) { + .msp430 => switch (c_type) { + .char => return 8, + .short, .ushort, .int, .uint => return 16, + .long, .ulong, .float => return 32, + .longlong, .ulonglong, .double, .longdouble => return 64, + }, + .avr => switch (c_type) { + .char => return 8, + .short, .ushort, .int, .uint => return 16, + .long, .ulong, .float, .double, .longdouble => return 32, + .longlong, .ulonglong => return 64, + }, + .tce, .tcele => switch (c_type) { + .char => return 8, + .short, .ushort => return 16, + .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32, + .float, .double, .longdouble => return 32, + }, + .mips64, .mips64el => switch (c_type) { + .char => return 8, + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32, + .longlong, .ulonglong, .double => return 64, + .longdouble => if (target.os.tag == .freebsd) return 64 else return 128, + }, + .x86_64 => switch (c_type) { + .char => return 8, + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => switch (target.abi) { + .gnux32, .muslx32 => return 32, + else => return 64, + }, + .longlong, .ulonglong, .double => return 64, + .longdouble => return 80, + }, + else => switch (c_type) { + .char => return 8, + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return target.ptrBitWidth(), + .longlong, .ulonglong, .double => return 64, + .longdouble => switch (target.cpu.arch) { + .x86 => switch (target.abi) { + .android => return 64, + else => return 80, + }, + + .powerpc, + .powerpcle, + => switch (target.abi) { + .musl, + .musleabi, + .musleabihf, + .muslx32, + => return 64, + else => switch (target.os.tag) { + .freebsd, .netbsd, .openbsd => return 64, + else => return 128, + }, + }, + + .powerpc64, + .powerpc64le, + => switch (target.abi) { + .musl, + .musleabi, + .musleabihf, + .muslx32, + => return 64, + else => switch (target.os.tag) { + .freebsd, .openbsd => return 64, + else => return 128, + }, + }, + + .riscv32, + .riscv64, + .aarch64, + .aarch64_be, + .aarch64_32, + .s390x, + .mips64, + .mips64el, + .sparc, + .sparc64, + .sparcel, + .wasm32, + .wasm64, + => return 128, + + else => return 64, + }, + }, + }, + + .windows, .uefi => switch (target.cpu.arch) { + .x86 => switch (c_type) { + .char => return 8, + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return 32, + .longlong, .ulonglong, .double => return 64, + .longdouble => switch (target.abi) { + .gnu, .gnuilp32, .cygnus => return 80, + else => return 64, + }, + }, + .x86_64 => switch (c_type) { + .char => return 8, + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => switch (target.abi) { + .cygnus => return 64, + else => return 32, + }, + .longlong, .ulonglong, .double => return 64, + .longdouble => switch (target.abi) { + .gnu, .gnuilp32, .cygnus => return 80, + else => return 64, + }, + }, + else => switch (c_type) { + .char => return 8, + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return 32, + .longlong, .ulonglong, .double => return 64, + .longdouble => return 64, + }, + }, + + .macos, .ios, .tvos, .watchos => switch (c_type) { + .char => return 8, + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => switch (target.cpu.arch) { + .x86, .arm, .aarch64_32 => return 32, + .x86_64 => switch (target.abi) { + .gnux32, .muslx32 => return 32, + else => return 64, + }, + else => return 64, + }, + .longlong, .ulonglong, .double => return 64, + .longdouble => switch (target.cpu.arch) { + .x86 => switch (target.abi) { + .android => return 64, + else => return 80, + }, + .x86_64 => return 80, + else => return 64, + }, + }, + + .nvcl, .cuda => switch (c_type) { + .char => return 8, + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => switch (target.cpu.arch) { + .nvptx => return 32, + .nvptx64 => return 64, + else => return 64, + }, + .longlong, .ulonglong, .double => return 64, + .longdouble => return 64, + }, + + .amdhsa, .amdpal => switch (c_type) { + .char => return 8, + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong, .longlong, .ulonglong, .double => return 64, + .longdouble => return 128, + }, + + .opencl => switch (c_type) { + .char => return 8, + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong, .double => return 64, + .longlong, .ulonglong => return 128, + // Note: The OpenCL specification does not guarantee a particular size for long double, + // but clang uses 128 bits. + .longdouble => return 128, + }, + + .ps4, .ps5 => switch (c_type) { + .char => return 8, + .short, .ushort => return 16, + .int, .uint, .float => return 32, + .long, .ulong => return 64, + .longlong, .ulonglong, .double => return 64, + .longdouble => return 80, + }, + + .cloudabi, + .kfreebsd, + .lv2, + .zos, + .rtems, + .nacl, + .aix, + .elfiamcu, + .mesa3d, + .contiki, + .hermit, + .hurd, + .glsl450, + .vulkan, + .driverkit, + .shadermodel, + .liteos, + => @panic("TODO specify the C integer and float type sizes for this OS"), + } +} + +pub fn c_type_alignment(target: Target, c_type: CType) u16 { + // Overrides for unusual alignments + switch (target.cpu.arch) { + .avr => return 1, + .x86 => switch (target.os.tag) { + .windows, .uefi => switch (c_type) { + .longlong, .ulonglong, .double => return 8, + .longdouble => switch (target.abi) { + .gnu, .gnuilp32, .cygnus => return 4, + else => return 8, + }, + else => {}, + }, + else => {}, + }, + else => {}, + } + + // Next-power-of-two-aligned, up to a maximum. + return @min( + std.math.ceilPowerOfTwoAssert(u16, (c_type_bit_size(target, c_type) + 7) / 8), + switch (target.cpu.arch) { + .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) { + .netbsd => switch (target.abi) { + .gnueabi, + .gnueabihf, + .eabi, + .eabihf, + .android, + .musleabi, + .musleabihf, + => 8, + + else => @as(u16, 4), + }, + .ios, .tvos, .watchos => 4, + else => 8, + }, + + .msp430, + .avr, + => 2, + + .arc, + .csky, + .x86, + .xcore, + .dxil, + .loongarch32, + .tce, + .tcele, + .le32, + .amdil, + .hsail, + .spir, + .spirv32, + .kalimba, + .shave, + .renderscript32, + .ve, + .spu_2, + .xtensa, + => 4, + + .aarch64_32, + .amdgcn, + .amdil64, + .bpfel, + .bpfeb, + .hexagon, + .hsail64, + .loongarch64, + .m68k, + .mips, + .mipsel, + .sparc, + .sparcel, + .sparc64, + .lanai, + .le64, + .nvptx, + .nvptx64, + .r600, + .s390x, + .spir64, + .spirv64, + .renderscript64, + => 8, + + .aarch64, + .aarch64_be, + .mips64, + .mips64el, + .powerpc, + .powerpcle, + .powerpc64, + .powerpc64le, + .riscv32, + .riscv64, + .x86_64, + .wasm32, + .wasm64, + => 16, + }, + ); +} + +pub fn c_type_preferred_alignment(target: Target, c_type: CType) u16 { + // Overrides for unusual alignments + switch (target.cpu.arch) { + .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) { + .netbsd => switch (target.abi) { + .gnueabi, + .gnueabihf, + .eabi, + .eabihf, + .android, + .musleabi, + .musleabihf, + => {}, + + else => switch (c_type) { + .longdouble => return 4, + else => {}, + }, + }, + .ios, .tvos, .watchos => switch (c_type) { + .longdouble => return 4, + else => {}, + }, + else => {}, + }, + .arc => switch (c_type) { + .longdouble => return 4, + else => {}, + }, + .avr => switch (c_type) { + .char, .int, .uint, .long, .ulong, .float, .longdouble => return 1, + .short, .ushort => return 2, + .double => return 4, + .longlong, .ulonglong => return 8, + }, + .x86 => switch (target.os.tag) { + .windows, .uefi => switch (c_type) { + .longdouble => switch (target.abi) { + .gnu, .gnuilp32, .cygnus => return 4, + else => return 8, + }, + else => {}, + }, + else => switch (c_type) { + .longdouble => return 4, + else => {}, + }, + }, + else => {}, + } + + // Next-power-of-two-aligned, up to a maximum. + return @min( + std.math.ceilPowerOfTwoAssert(u16, (c_type_bit_size(target, c_type) + 7) / 8), + switch (target.cpu.arch) { + .msp430 => @as(u16, 2), + + .csky, + .xcore, + .dxil, + .loongarch32, + .tce, + .tcele, + .le32, + .amdil, + .hsail, + .spir, + .spirv32, + .kalimba, + .shave, + .renderscript32, + .ve, + .spu_2, + .xtensa, + => 4, + + .arc, + .arm, + .armeb, + .avr, + .thumb, + .thumbeb, + .aarch64_32, + .amdgcn, + .amdil64, + .bpfel, + .bpfeb, + .hexagon, + .hsail64, + .x86, + .loongarch64, + .m68k, + .mips, + .mipsel, + .sparc, + .sparcel, + .sparc64, + .lanai, + .le64, + .nvptx, + .nvptx64, + .r600, + .s390x, + .spir64, + .spirv64, + .renderscript64, + => 8, + + .aarch64, + .aarch64_be, + .mips64, + .mips64el, + .powerpc, + .powerpcle, + .powerpc64, + .powerpc64le, + .riscv32, + .riscv64, + .x86_64, + .wasm32, + .wasm64, + => 16, + }, + ); +} + +pub fn is_libc_lib_name(target: std.Target, name: []const u8) bool { + const ignore_case = target.os.tag == .macos or target.os.tag == .windows; + + if (eqlIgnoreCase(ignore_case, name, "c")) + return true; + + if (target.isMinGW()) { + if (eqlIgnoreCase(ignore_case, name, "m")) + return true; + if (eqlIgnoreCase(ignore_case, name, "uuid")) + return true; + if (eqlIgnoreCase(ignore_case, name, "mingw32")) + return true; + if (eqlIgnoreCase(ignore_case, name, "msvcrt-os")) + return true; + if (eqlIgnoreCase(ignore_case, name, "mingwex")) + return true; + + return false; + } + + if (target.abi.isGnu() or target.abi.isMusl()) { + if (eqlIgnoreCase(ignore_case, name, "m")) + return true; + if (eqlIgnoreCase(ignore_case, name, "rt")) + return true; + if (eqlIgnoreCase(ignore_case, name, "pthread")) + return true; + if (eqlIgnoreCase(ignore_case, name, "util")) + return true; + if (eqlIgnoreCase(ignore_case, name, "xnet")) + return true; + if (eqlIgnoreCase(ignore_case, name, "resolv")) + return true; + if (eqlIgnoreCase(ignore_case, name, "dl")) + return true; + } + + if (target.abi.isMusl()) { + if (eqlIgnoreCase(ignore_case, name, "crypt")) + return true; + } + + if (target.os.tag.isDarwin()) { + if (eqlIgnoreCase(ignore_case, name, "System")) + return true; + if (eqlIgnoreCase(ignore_case, name, "c")) + return true; + if (eqlIgnoreCase(ignore_case, name, "dbm")) + return true; + if (eqlIgnoreCase(ignore_case, name, "dl")) + return true; + if (eqlIgnoreCase(ignore_case, name, "info")) + return true; + if (eqlIgnoreCase(ignore_case, name, "m")) + return true; + if (eqlIgnoreCase(ignore_case, name, "poll")) + return true; + if (eqlIgnoreCase(ignore_case, name, "proc")) + return true; + if (eqlIgnoreCase(ignore_case, name, "pthread")) + return true; + if (eqlIgnoreCase(ignore_case, name, "rpcsvc")) + return true; + } + + if (target.os.isAtLeast(.macos, .{ .major = 10, .minor = 8, .patch = 0 }) orelse false) { + if (eqlIgnoreCase(ignore_case, name, "mx")) + return true; + } + + return false; +} + +pub fn is_libcpp_lib_name(target: std.Target, name: []const u8) bool { + const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows; + + return eqlIgnoreCase(ignore_case, name, "c++") or + eqlIgnoreCase(ignore_case, name, "stdc++") or + eqlIgnoreCase(ignore_case, name, "c++abi"); +} + +fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool { + if (ignore_case) { + return std.ascii.eqlIgnoreCase(a, b); + } else { + return std.mem.eql(u8, a, b); + } +} + +const Target = @This(); +const std = @import("std.zig"); +const builtin = @import("builtin"); +const Allocator = std.mem.Allocator; + +test { + std.testing.refAllDecls(Cpu.Arch); +} diff --git a/lib/std/zig/CrossTarget.zig b/lib/std/Target/Query.zig similarity index 67% rename from lib/std/zig/CrossTarget.zig rename to lib/std/Target/Query.zig index ea496051b4ca..10130e03bba6 100644 --- a/lib/std/zig/CrossTarget.zig +++ b/lib/std/Target/Query.zig @@ -1,13 +1,7 @@ -//! Contains all the same data as `Target`, additionally introducing the concept of "the native target". -//! The purpose of this abstraction is to provide meaningful and unsurprising defaults. -//! This struct does reference any resources and it is copyable. - -const CrossTarget = @This(); -const std = @import("../std.zig"); -const builtin = @import("builtin"); -const assert = std.debug.assert; -const Target = std.Target; -const mem = std.mem; +//! Contains all the same data as `Target`, additionally introducing the +//! concept of "the native target". The purpose of this abstraction is to +//! provide meaningful and unsurprising defaults. This struct does reference +//! any resources and it is copyable. /// `null` means native. cpu_arch: ?Target.Cpu.Arch = null, @@ -40,7 +34,7 @@ abi: ?Target.Abi = null, /// When `os_tag` is `null`, then `null` means native. Otherwise it means the standard path /// based on the `os_tag`. -dynamic_linker: DynamicLinker = DynamicLinker{}, +dynamic_linker: Target.DynamicLinker = Target.DynamicLinker.none, /// `null` means default for the cpu/arch/os combo. ofmt: ?Target.ObjectFormat = null, @@ -57,20 +51,47 @@ pub const CpuModel = union(enum) { determined_by_cpu_arch, explicit: *const Target.Cpu.Model, + + pub fn eql(a: CpuModel, b: CpuModel) bool { + const Tag = @typeInfo(CpuModel).Union.tag_type.?; + const a_tag: Tag = a; + const b_tag: Tag = b; + if (a_tag != b_tag) return false; + return switch (a) { + .native, .baseline, .determined_by_cpu_arch => true, + .explicit => |a_model| a_model == b.explicit, + }; + } }; pub const OsVersion = union(enum) { none: void, semver: SemanticVersion, windows: Target.Os.WindowsVersion, + + pub fn eql(a: OsVersion, b: OsVersion) bool { + const Tag = @typeInfo(OsVersion).Union.tag_type.?; + const a_tag: Tag = a; + const b_tag: Tag = b; + if (a_tag != b_tag) return false; + return switch (a) { + .none => true, + .semver => |a_semver| a_semver.order(b.semver) == .eq, + .windows => |a_windows| a_windows == b.windows, + }; + } + + pub fn eqlOpt(a: ?OsVersion, b: ?OsVersion) bool { + if (a == null and b == null) return true; + if (a == null or b == null) return false; + return OsVersion.eql(a.?, b.?); + } }; pub const SemanticVersion = std.SemanticVersion; -pub const DynamicLinker = Target.DynamicLinker; - -pub fn fromTarget(target: Target) CrossTarget { - var result: CrossTarget = .{ +pub fn fromTarget(target: Target) Query { + var result: Query = .{ .cpu_arch = target.cpu.arch, .cpu_model = .{ .explicit = target.cpu.model }, .os_tag = target.os.tag, @@ -102,7 +123,7 @@ pub fn fromTarget(target: Target) CrossTarget { return result; } -fn updateOsVersionRange(self: *CrossTarget, os: Target.Os) void { +fn updateOsVersionRange(self: *Query, os: Target.Os) void { switch (os.tag) { .freestanding, .ananas, @@ -170,16 +191,6 @@ fn updateOsVersionRange(self: *CrossTarget, os: Target.Os) void { } } -/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. -pub fn toTarget(self: CrossTarget) Target { - return .{ - .cpu = self.getCpu(), - .os = self.getOs(), - .abi = self.getAbi(), - .ofmt = self.getObjectFormat(), - }; -} - pub const ParseOptions = struct { /// This is sometimes called a "triple". It looks roughly like this: /// riscv64-linux-musl @@ -233,12 +244,12 @@ pub const ParseOptions = struct { }; }; -pub fn parse(args: ParseOptions) !CrossTarget { +pub fn parse(args: ParseOptions) !Query { var dummy_diags: ParseOptions.Diagnostics = undefined; const diags = args.diagnostics orelse &dummy_diags; - var result: CrossTarget = .{ - .dynamic_linker = DynamicLinker.init(args.dynamic_linker), + var result: Query = .{ + .dynamic_linker = Target.DynamicLinker.init(args.dynamic_linker), }; var it = mem.splitScalar(u8, args.arch_os_abi, '-'); @@ -248,7 +259,7 @@ pub fn parse(args: ParseOptions) !CrossTarget { result.cpu_arch = std.meta.stringToEnum(Target.Cpu.Arch, arch_name) orelse return error.UnknownArchitecture; } - const arch = result.getCpuArch(); + const arch = result.cpu_arch orelse builtin.cpu.arch; diags.arch = arch; if (it.next()) |os_text| { @@ -267,7 +278,7 @@ pub fn parse(args: ParseOptions) !CrossTarget { const abi_ver_text = abi_it.rest(); if (abi_it.next() != null) { - if (result.isGnuLibC()) { + if (Target.isGnuLibC_os_tag_abi(result.os_tag orelse builtin.os.tag, abi)) { result.glibc_version = parseVersion(abi_ver_text) catch |err| switch (err) { error.Overflow => return error.InvalidAbiVersion, error.InvalidVersion => return error.InvalidAbiVersion, @@ -341,7 +352,7 @@ pub fn parse(args: ParseOptions) !CrossTarget { /// Similar to `parse` except instead of fully parsing, it only determines the CPU /// architecture and returns it if it can be determined, and returns `null` otherwise. -/// This is intended to be used if the API user of CrossTarget needs to learn the +/// This is intended to be used if the API user of Query needs to learn the /// target CPU architecture in order to fully populate `ParseOptions`. pub fn parseCpuArch(args: ParseOptions) ?Target.Cpu.Arch { var it = mem.splitScalar(u8, args.arch_os_abi, '-'); @@ -385,184 +396,22 @@ test parseVersion { try std.testing.expectError(error.InvalidVersion, parseVersion("1.2.3.4")); } -/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. -pub fn getCpu(self: CrossTarget) Target.Cpu { - switch (self.cpu_model) { - .native => { - // This works when doing `zig build` because Zig generates a build executable using - // native CPU model & features. However this will not be accurate otherwise, and - // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. - return builtin.cpu; - }, - .baseline => { - var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch()); - self.updateCpuFeatures(&adjusted_baseline.features); - return adjusted_baseline; - }, - .determined_by_cpu_arch => if (self.cpu_arch == null) { - // This works when doing `zig build` because Zig generates a build executable using - // native CPU model & features. However this will not be accurate otherwise, and - // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. - return builtin.cpu; - } else { - var adjusted_baseline = Target.Cpu.baseline(self.getCpuArch()); - self.updateCpuFeatures(&adjusted_baseline.features); - return adjusted_baseline; - }, - .explicit => |model| { - var adjusted_model = model.toCpu(self.getCpuArch()); - self.updateCpuFeatures(&adjusted_model.features); - return adjusted_model; - }, - } -} - -pub fn getCpuArch(self: CrossTarget) Target.Cpu.Arch { - return self.cpu_arch orelse builtin.cpu.arch; -} - -pub fn getCpuModel(self: CrossTarget) *const Target.Cpu.Model { - return switch (self.cpu_model) { - .explicit => |cpu_model| cpu_model, - else => self.getCpu().model, - }; -} - -pub fn getCpuFeatures(self: CrossTarget) Target.Cpu.Feature.Set { - return self.getCpu().features; -} - -/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. -pub fn getOs(self: CrossTarget) Target.Os { - // `builtin.os` works when doing `zig build` because Zig generates a build executable using - // native OS version range. However this will not be accurate otherwise, and - // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. - var adjusted_os = if (self.os_tag) |os_tag| os_tag.defaultVersionRange(self.getCpuArch()) else builtin.os; - - if (self.os_version_min) |min| switch (min) { - .none => {}, - .semver => |semver| switch (self.getOsTag()) { - .linux => adjusted_os.version_range.linux.range.min = semver, - else => adjusted_os.version_range.semver.min = semver, - }, - .windows => |win_ver| adjusted_os.version_range.windows.min = win_ver, - }; - - if (self.os_version_max) |max| switch (max) { - .none => {}, - .semver => |semver| switch (self.getOsTag()) { - .linux => adjusted_os.version_range.linux.range.max = semver, - else => adjusted_os.version_range.semver.max = semver, - }, - .windows => |win_ver| adjusted_os.version_range.windows.max = win_ver, - }; - - if (self.glibc_version) |glibc| { - assert(self.isGnuLibC()); - adjusted_os.version_range.linux.glibc = glibc; - } - - return adjusted_os; -} - -pub fn getOsTag(self: CrossTarget) Target.Os.Tag { - return self.os_tag orelse builtin.os.tag; -} - -/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. -pub fn getOsVersionMin(self: CrossTarget) OsVersion { - if (self.os_version_min) |version_min| return version_min; - var tmp: CrossTarget = undefined; - tmp.updateOsVersionRange(self.getOs()); - return tmp.os_version_min.?; -} - -/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. -pub fn getOsVersionMax(self: CrossTarget) OsVersion { - if (self.os_version_max) |version_max| return version_max; - var tmp: CrossTarget = undefined; - tmp.updateOsVersionRange(self.getOs()); - return tmp.os_version_max.?; -} - -/// TODO deprecated, use `std.zig.system.NativeTargetInfo.detect`. -pub fn getAbi(self: CrossTarget) Target.Abi { - if (self.abi) |abi| return abi; - - if (self.os_tag == null) { - // This works when doing `zig build` because Zig generates a build executable using - // native CPU model & features. However this will not be accurate otherwise, and - // will need to be integrated with `std.zig.system.NativeTargetInfo.detect`. - return builtin.abi; - } - - return Target.Abi.default(self.getCpuArch(), self.getOs()); -} - -pub fn isFreeBSD(self: CrossTarget) bool { - return self.getOsTag() == .freebsd; -} - -pub fn isDarwin(self: CrossTarget) bool { - return self.getOsTag().isDarwin(); -} - -pub fn isNetBSD(self: CrossTarget) bool { - return self.getOsTag() == .netbsd; -} - -pub fn isOpenBSD(self: CrossTarget) bool { - return self.getOsTag() == .openbsd; -} - -pub fn isUefi(self: CrossTarget) bool { - return self.getOsTag() == .uefi; -} - -pub fn isDragonFlyBSD(self: CrossTarget) bool { - return self.getOsTag() == .dragonfly; -} - -pub fn isLinux(self: CrossTarget) bool { - return self.getOsTag() == .linux; -} - -pub fn isWindows(self: CrossTarget) bool { - return self.getOsTag() == .windows; -} - -pub fn exeFileExt(self: CrossTarget) [:0]const u8 { - return Target.exeFileExtSimple(self.getCpuArch(), self.getOsTag()); -} - -pub fn staticLibSuffix(self: CrossTarget) [:0]const u8 { - return Target.staticLibSuffix_os_abi(self.getOsTag(), self.getAbi()); -} - -pub fn dynamicLibSuffix(self: CrossTarget) [:0]const u8 { - return self.getOsTag().dynamicLibSuffix(); -} - -pub fn libPrefix(self: CrossTarget) [:0]const u8 { - return Target.libPrefix_os_abi(self.getOsTag(), self.getAbi()); -} - -pub fn isNativeCpu(self: CrossTarget) bool { +pub fn isNativeCpu(self: Query) bool { return self.cpu_arch == null and (self.cpu_model == .native or self.cpu_model == .determined_by_cpu_arch) and self.cpu_features_sub.isEmpty() and self.cpu_features_add.isEmpty(); } -pub fn isNativeOs(self: CrossTarget) bool { +pub fn isNativeOs(self: Query) bool { return self.os_tag == null and self.os_version_min == null and self.os_version_max == null and self.dynamic_linker.get() == null and self.glibc_version == null; } -pub fn isNativeAbi(self: CrossTarget) bool { +pub fn isNativeAbi(self: Query) bool { return self.os_tag == null and self.abi == null; } -pub fn isNative(self: CrossTarget) bool { +pub fn isNative(self: Query) bool { return self.isNativeCpu() and self.isNativeOs() and self.isNativeAbi(); } @@ -576,7 +425,7 @@ fn formatVersion(version: SemanticVersion, writer: anytype) !void { } } -pub fn zigTriple(self: CrossTarget, allocator: mem.Allocator) error{OutOfMemory}![]u8 { +pub fn zigTriple(self: Query, allocator: Allocator) Allocator.Error![]u8 { if (self.isNative()) { return allocator.dupe(u8, "native"); } @@ -591,14 +440,16 @@ pub fn zigTriple(self: CrossTarget, allocator: mem.Allocator) error{OutOfMemory} // The zig target syntax does not allow specifying a max os version with no min, so // if either are present, we need the min. - if (self.os_version_min != null or self.os_version_max != null) { - switch (self.getOsVersionMin()) { + if (self.os_version_min) |min| { + switch (min) { .none => {}, .semver => |v| { try result.writer().writeAll("."); try formatVersion(v, result.writer()); }, - .windows => |v| try result.writer().print("{s}", .{v}), + .windows => |v| { + try result.writer().print("{s}", .{v}); + }, } } if (self.os_version_max) |max| { @@ -608,92 +459,93 @@ pub fn zigTriple(self: CrossTarget, allocator: mem.Allocator) error{OutOfMemory} try result.writer().writeAll("..."); try formatVersion(v, result.writer()); }, - .windows => |v| try result.writer().print("..{s}", .{v}), + .windows => |v| { + // This is counting on a custom format() function defined on `WindowsVersion` + // to add a prefix '.' and make there be a total of three dots. + try result.writer().print("..{s}", .{v}); + }, } } if (self.glibc_version) |v| { - try result.writer().print("-{s}.", .{@tagName(self.getAbi())}); + const name = @tagName(self.abi orelse builtin.target.abi); + try result.ensureUnusedCapacity(name.len + 2); + result.appendAssumeCapacity('-'); + result.appendSliceAssumeCapacity(name); + result.appendAssumeCapacity('.'); try formatVersion(v, result.writer()); } else if (self.abi) |abi| { - try result.writer().print("-{s}", .{@tagName(abi)}); + const name = @tagName(abi); + try result.ensureUnusedCapacity(name.len + 1); + result.appendAssumeCapacity('-'); + result.appendSliceAssumeCapacity(name); } return result.toOwnedSlice(); } -pub fn allocDescription(self: CrossTarget, allocator: mem.Allocator) ![]u8 { - // TODO is there anything else worthy of the description that is not - // already captured in the triple? - return self.zigTriple(allocator); -} +/// Renders the query into a textual representation that can be parsed via the +/// `-mcpu` flag passed to the Zig compiler. +/// Appends the result to `buffer`. +pub fn serializeCpu(q: Query, buffer: *std.ArrayList(u8)) Allocator.Error!void { + try buffer.ensureUnusedCapacity(8); + switch (q.cpu_model) { + .native => { + buffer.appendSliceAssumeCapacity("native"); + }, + .baseline => { + buffer.appendSliceAssumeCapacity("baseline"); + }, + .determined_by_cpu_arch => { + if (q.cpu_arch == null) { + buffer.appendSliceAssumeCapacity("native"); + } else { + buffer.appendSliceAssumeCapacity("baseline"); + } + }, + .explicit => |model| { + try buffer.appendSlice(model.name); + }, + } -pub fn linuxTriple(self: CrossTarget, allocator: mem.Allocator) ![]u8 { - return Target.linuxTripleSimple(allocator, self.getCpuArch(), self.getOsTag(), self.getAbi()); -} + if (q.cpu_features_add.isEmpty() and q.cpu_features_sub.isEmpty()) { + // The CPU name alone is sufficient. + return; + } -pub fn wantSharedLibSymLinks(self: CrossTarget) bool { - return self.getOsTag() != .windows; + const cpu_arch = q.cpu_arch orelse builtin.cpu.arch; + const all_features = cpu_arch.allFeaturesList(); + + for (all_features, 0..) |feature, i_usize| { + const i: Target.Cpu.Feature.Set.Index = @intCast(i_usize); + try buffer.ensureUnusedCapacity(feature.name.len + 1); + if (q.cpu_features_sub.isEnabled(i)) { + buffer.appendAssumeCapacity('-'); + buffer.appendSliceAssumeCapacity(feature.name); + } else if (q.cpu_features_add.isEnabled(i)) { + buffer.appendAssumeCapacity('+'); + buffer.appendSliceAssumeCapacity(feature.name); + } + } } -pub const VcpkgLinkage = std.builtin.LinkMode; - -/// Returned slice must be freed by the caller. -pub fn vcpkgTriplet(self: CrossTarget, allocator: mem.Allocator, linkage: VcpkgLinkage) ![]u8 { - const arch = switch (self.getCpuArch()) { - .x86 => "x86", - .x86_64 => "x64", - - .arm, - .armeb, - .thumb, - .thumbeb, - .aarch64_32, - => "arm", - - .aarch64, - .aarch64_be, - => "arm64", - - else => return error.UnsupportedVcpkgArchitecture, - }; - - const os = switch (self.getOsTag()) { - .windows => "windows", - .linux => "linux", - .macos => "macos", - else => return error.UnsupportedVcpkgOperatingSystem, - }; - - const static_suffix = switch (linkage) { - .Static => "-static", - .Dynamic => "", - }; - - return std.fmt.allocPrint(allocator, "{s}-{s}{s}", .{ arch, os, static_suffix }); +pub fn serializeCpuAlloc(q: Query, ally: Allocator) Allocator.Error![]u8 { + var buffer = std.ArrayList(u8).init(ally); + try serializeCpu(q, &buffer); + return buffer.toOwnedSlice(); } -pub fn isGnuLibC(self: CrossTarget) bool { - return Target.isGnuLibC_os_tag_abi(self.getOsTag(), self.getAbi()); +pub fn allocDescription(self: Query, allocator: Allocator) ![]u8 { + // TODO is there anything else worthy of the description that is not + // already captured in the triple? + return self.zigTriple(allocator); } -pub fn setGnuLibCVersion(self: *CrossTarget, major: u32, minor: u32, patch: u32) void { - assert(self.isGnuLibC()); +pub fn setGnuLibCVersion(self: *Query, major: u32, minor: u32, patch: u32) void { self.glibc_version = SemanticVersion{ .major = major, .minor = minor, .patch = patch }; } -pub fn getObjectFormat(self: CrossTarget) Target.ObjectFormat { - return self.ofmt orelse Target.ObjectFormat.default(self.getOsTag(), self.getCpuArch()); -} - -pub fn updateCpuFeatures(self: CrossTarget, set: *Target.Cpu.Feature.Set) void { - set.removeFeatureSet(self.cpu_features_sub); - set.addFeatureSet(self.cpu_features_add); - set.populateDependencies(self.getCpuArch().allFeaturesList()); - set.removeFeatureSet(self.cpu_features_sub); -} - -fn parseOs(result: *CrossTarget, diags: *ParseOptions.Diagnostics, text: []const u8) !void { +fn parseOs(result: *Query, diags: *ParseOptions.Diagnostics, text: []const u8) !void { var it = mem.splitScalar(u8, text, '.'); const os_name = it.first(); diags.os_name = os_name; @@ -702,7 +554,7 @@ fn parseOs(result: *CrossTarget, diags: *ParseOptions.Diagnostics, text: []const result.os_tag = std.meta.stringToEnum(Target.Os.Tag, os_name) orelse return error.UnknownOperatingSystem; } - const tag = result.getOsTag(); + const tag = result.os_tag orelse builtin.os.tag; diags.os_tag = tag; const version_text = it.rest(); @@ -790,48 +642,78 @@ fn parseOs(result: *CrossTarget, diags: *ParseOptions.Diagnostics, text: []const } } -test "CrossTarget.parse" { +pub fn eql(a: Query, b: Query) bool { + if (a.cpu_arch != b.cpu_arch) return false; + if (!a.cpu_model.eql(b.cpu_model)) return false; + if (!a.cpu_features_add.eql(b.cpu_features_add)) return false; + if (!a.cpu_features_sub.eql(b.cpu_features_sub)) return false; + if (a.os_tag != b.os_tag) return false; + if (!OsVersion.eqlOpt(a.os_version_min, b.os_version_min)) return false; + if (!OsVersion.eqlOpt(a.os_version_max, b.os_version_max)) return false; + if (!versionEqualOpt(a.glibc_version, b.glibc_version)) return false; + if (a.abi != b.abi) return false; + if (!a.dynamic_linker.eql(b.dynamic_linker)) return false; + if (a.ofmt != b.ofmt) return false; + + return true; +} + +fn versionEqualOpt(a: ?SemanticVersion, b: ?SemanticVersion) bool { + if (a == null and b == null) return true; + if (a == null or b == null) return false; + return SemanticVersion.order(a.?, b.?) == .eq; +} + +const Query = @This(); +const std = @import("../std.zig"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const Target = std.Target; +const mem = std.mem; +const Allocator = std.mem.Allocator; + +test parse { if (builtin.target.isGnuLibC()) { - var cross_target = try CrossTarget.parse(.{}); - cross_target.setGnuLibCVersion(2, 1, 1); + var query = try Query.parse(.{}); + query.setGnuLibCVersion(2, 1, 1); - const text = try cross_target.zigTriple(std.testing.allocator); + const text = try query.zigTriple(std.testing.allocator); defer std.testing.allocator.free(text); var buf: [256]u8 = undefined; const triple = std.fmt.bufPrint( buf[0..], "native-native-{s}.2.1.1", - .{@tagName(builtin.abi)}, + .{@tagName(builtin.target.abi)}, ) catch unreachable; try std.testing.expectEqualSlices(u8, triple, text); } { - const cross_target = try CrossTarget.parse(.{ + const query = try Query.parse(.{ .arch_os_abi = "aarch64-linux", .cpu_features = "native", }); - try std.testing.expect(cross_target.cpu_arch.? == .aarch64); - try std.testing.expect(cross_target.cpu_model == .native); + try std.testing.expect(query.cpu_arch.? == .aarch64); + try std.testing.expect(query.cpu_model == .native); } { - const cross_target = try CrossTarget.parse(.{ .arch_os_abi = "native" }); + const query = try Query.parse(.{ .arch_os_abi = "native" }); - try std.testing.expect(cross_target.cpu_arch == null); - try std.testing.expect(cross_target.isNative()); + try std.testing.expect(query.cpu_arch == null); + try std.testing.expect(query.isNative()); - const text = try cross_target.zigTriple(std.testing.allocator); + const text = try query.zigTriple(std.testing.allocator); defer std.testing.allocator.free(text); try std.testing.expectEqualSlices(u8, "native", text); } { - const cross_target = try CrossTarget.parse(.{ + const query = try Query.parse(.{ .arch_os_abi = "x86_64-linux-gnu", .cpu_features = "x86_64-sse-sse2-avx-cx8", }); - const target = cross_target.toTarget(); + const target = try std.zig.system.resolveTargetQuery(query); try std.testing.expect(target.os.tag == .linux); try std.testing.expect(target.abi == .gnu); @@ -847,16 +729,16 @@ test "CrossTarget.parse" { try std.testing.expect(Target.x86.featureSetHasAll(target.cpu.features, .{ .mmx, .x87 })); try std.testing.expect(!Target.x86.featureSetHasAll(target.cpu.features, .{ .mmx, .x87, .sse })); - const text = try cross_target.zigTriple(std.testing.allocator); + const text = try query.zigTriple(std.testing.allocator); defer std.testing.allocator.free(text); try std.testing.expectEqualSlices(u8, "x86_64-linux-gnu", text); } { - const cross_target = try CrossTarget.parse(.{ + const query = try Query.parse(.{ .arch_os_abi = "arm-linux-musleabihf", .cpu_features = "generic+v8a", }); - const target = cross_target.toTarget(); + const target = try std.zig.system.resolveTargetQuery(query); try std.testing.expect(target.os.tag == .linux); try std.testing.expect(target.abi == .musleabihf); @@ -864,16 +746,16 @@ test "CrossTarget.parse" { try std.testing.expect(target.cpu.model == &Target.arm.cpu.generic); try std.testing.expect(Target.arm.featureSetHas(target.cpu.features, .v8a)); - const text = try cross_target.zigTriple(std.testing.allocator); + const text = try query.zigTriple(std.testing.allocator); defer std.testing.allocator.free(text); try std.testing.expectEqualSlices(u8, "arm-linux-musleabihf", text); } { - const cross_target = try CrossTarget.parse(.{ + const query = try Query.parse(.{ .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27", .cpu_features = "generic+v8a", }); - const target = cross_target.toTarget(); + const target = try std.zig.system.resolveTargetQuery(query); try std.testing.expect(target.cpu.arch == .aarch64); try std.testing.expect(target.os.tag == .linux); @@ -888,7 +770,7 @@ test "CrossTarget.parse" { try std.testing.expect(target.os.version_range.linux.glibc.patch == 0); try std.testing.expect(target.abi == .gnu); - const text = try cross_target.zigTriple(std.testing.allocator); + const text = try query.zigTriple(std.testing.allocator); defer std.testing.allocator.free(text); try std.testing.expectEqualSlices(u8, "aarch64-linux.3.10...4.4.1-gnu.2.27", text); } diff --git a/lib/std/target/aarch64.zig b/lib/std/Target/aarch64.zig similarity index 100% rename from lib/std/target/aarch64.zig rename to lib/std/Target/aarch64.zig diff --git a/lib/std/target/amdgpu.zig b/lib/std/Target/amdgpu.zig similarity index 100% rename from lib/std/target/amdgpu.zig rename to lib/std/Target/amdgpu.zig diff --git a/lib/std/target/arc.zig b/lib/std/Target/arc.zig similarity index 100% rename from lib/std/target/arc.zig rename to lib/std/Target/arc.zig diff --git a/lib/std/target/arm.zig b/lib/std/Target/arm.zig similarity index 100% rename from lib/std/target/arm.zig rename to lib/std/Target/arm.zig diff --git a/lib/std/target/avr.zig b/lib/std/Target/avr.zig similarity index 100% rename from lib/std/target/avr.zig rename to lib/std/Target/avr.zig diff --git a/lib/std/target/bpf.zig b/lib/std/Target/bpf.zig similarity index 100% rename from lib/std/target/bpf.zig rename to lib/std/Target/bpf.zig diff --git a/lib/std/target/csky.zig b/lib/std/Target/csky.zig similarity index 100% rename from lib/std/target/csky.zig rename to lib/std/Target/csky.zig diff --git a/lib/std/target/hexagon.zig b/lib/std/Target/hexagon.zig similarity index 100% rename from lib/std/target/hexagon.zig rename to lib/std/Target/hexagon.zig diff --git a/lib/std/target/loongarch.zig b/lib/std/Target/loongarch.zig similarity index 100% rename from lib/std/target/loongarch.zig rename to lib/std/Target/loongarch.zig diff --git a/lib/std/target/m68k.zig b/lib/std/Target/m68k.zig similarity index 100% rename from lib/std/target/m68k.zig rename to lib/std/Target/m68k.zig diff --git a/lib/std/target/mips.zig b/lib/std/Target/mips.zig similarity index 100% rename from lib/std/target/mips.zig rename to lib/std/Target/mips.zig diff --git a/lib/std/target/msp430.zig b/lib/std/Target/msp430.zig similarity index 100% rename from lib/std/target/msp430.zig rename to lib/std/Target/msp430.zig diff --git a/lib/std/target/nvptx.zig b/lib/std/Target/nvptx.zig similarity index 100% rename from lib/std/target/nvptx.zig rename to lib/std/Target/nvptx.zig diff --git a/lib/std/target/powerpc.zig b/lib/std/Target/powerpc.zig similarity index 100% rename from lib/std/target/powerpc.zig rename to lib/std/Target/powerpc.zig diff --git a/lib/std/target/riscv.zig b/lib/std/Target/riscv.zig similarity index 100% rename from lib/std/target/riscv.zig rename to lib/std/Target/riscv.zig diff --git a/lib/std/target/s390x.zig b/lib/std/Target/s390x.zig similarity index 100% rename from lib/std/target/s390x.zig rename to lib/std/Target/s390x.zig diff --git a/lib/std/target/sparc.zig b/lib/std/Target/sparc.zig similarity index 100% rename from lib/std/target/sparc.zig rename to lib/std/Target/sparc.zig diff --git a/lib/std/target/spirv.zig b/lib/std/Target/spirv.zig similarity index 100% rename from lib/std/target/spirv.zig rename to lib/std/Target/spirv.zig diff --git a/lib/std/target/ve.zig b/lib/std/Target/ve.zig similarity index 100% rename from lib/std/target/ve.zig rename to lib/std/Target/ve.zig diff --git a/lib/std/target/wasm.zig b/lib/std/Target/wasm.zig similarity index 100% rename from lib/std/target/wasm.zig rename to lib/std/Target/wasm.zig diff --git a/lib/std/target/x86.zig b/lib/std/Target/x86.zig similarity index 100% rename from lib/std/target/x86.zig rename to lib/std/Target/x86.zig diff --git a/lib/std/target/xtensa.zig b/lib/std/Target/xtensa.zig similarity index 100% rename from lib/std/target/xtensa.zig rename to lib/std/Target/xtensa.zig diff --git a/lib/std/Uri.zig b/lib/std/Uri.zig index a88a102bd426..c79007b87a5b 100644 --- a/lib/std/Uri.zig +++ b/lib/std/Uri.zig @@ -6,13 +6,13 @@ const std = @import("std.zig"); const testing = std.testing; scheme: []const u8, -user: ?[]const u8, -password: ?[]const u8, -host: ?[]const u8, -port: ?u16, +user: ?[]const u8 = null, +password: ?[]const u8 = null, +host: ?[]const u8 = null, +port: ?u16 = null, path: []const u8, -query: ?[]const u8, -fragment: ?[]const u8, +query: ?[]const u8 = null, +fragment: ?[]const u8 = null, /// Applies URI encoding and replaces all reserved characters with their respective %XX code. pub fn escapeString(allocator: std.mem.Allocator, input: []const u8) error{OutOfMemory}![]u8 { diff --git a/lib/std/fs/AtomicFile.zig b/lib/std/fs/AtomicFile.zig index b090bc95828e..c95ae9bcf2bb 100644 --- a/lib/std/fs/AtomicFile.zig +++ b/lib/std/fs/AtomicFile.zig @@ -65,6 +65,10 @@ pub fn deinit(self: *AtomicFile) void { pub const FinishError = posix.RenameError; +/// On Windows, this function introduces a period of time where some file +/// system operations on the destination file will result in +/// `error.AccessDenied`, including rename operations (such as the one used in +/// this function). pub fn finish(self: *AtomicFile) FinishError!void { assert(self.file_exists); if (self.file_open) { diff --git a/lib/std/fs/Dir.zig b/lib/std/fs/Dir.zig index b637e664f1ee..4a45975ccc18 100644 --- a/lib/std/fs/Dir.zig +++ b/lib/std/fs/Dir.zig @@ -2416,15 +2416,21 @@ fn copy_file(fd_in: posix.fd_t, fd_out: posix.fd_t, maybe_size: ?u64) CopyFileRa pub const AtomicFileOptions = struct { mode: File.Mode = File.default_mode, + make_path: bool = false, }; -/// Directly access the `.file` field, and then call `AtomicFile.finish` -/// to atomically replace `dest_path` with contents. -/// Always call `AtomicFile.deinit` to clean up, regardless of whether `AtomicFile.finish` succeeded. -/// `dest_path` must remain valid until `AtomicFile.deinit` is called. +/// Directly access the `.file` field, and then call `AtomicFile.finish` to +/// atomically replace `dest_path` with contents. +/// Always call `AtomicFile.deinit` to clean up, regardless of whether +/// `AtomicFile.finish` succeeded. `dest_path` must remain valid until +/// `AtomicFile.deinit` is called. pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions) !AtomicFile { if (fs.path.dirname(dest_path)) |dirname| { - const dir = try self.openDir(dirname, .{}); + const dir = if (options.make_path) + try self.makeOpenPath(dirname, .{}) + else + try self.openDir(dirname, .{}); + return AtomicFile.init(fs.path.basename(dest_path), options.mode, dir, true); } else { return AtomicFile.init(dest_path, options.mode, self, false); diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 40e0991e5fc8..490e12350624 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -752,6 +752,11 @@ test "fsync" { } test "getrlimit and setrlimit" { + if (builtin.target.os.tag == .macos) { + // https://github.com/ziglang/zig/issues/18395 + return error.SkipZigTest; + } + if (!@hasDecl(os.system, "rlimit")) { return error.SkipZigTest; } diff --git a/lib/std/std.zig b/lib/std/std.zig index 7342cadbefdb..66e30c4b4ee1 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -47,7 +47,7 @@ pub const StringArrayHashMap = array_hash_map.StringArrayHashMap; pub const StringArrayHashMapUnmanaged = array_hash_map.StringArrayHashMapUnmanaged; /// deprecated: use `DoublyLinkedList`. pub const TailQueue = DoublyLinkedList; -pub const Target = @import("target.zig").Target; +pub const Target = @import("Target.zig"); pub const Thread = @import("Thread.zig"); pub const Treap = @import("treap.zig").Treap; pub const Tz = tz.Tz; @@ -194,9 +194,6 @@ pub const zig = @import("zig.zig"); pub const start = @import("start.zig"); -/// deprecated: use `Build`. -pub const build = Build; - const root = @import("root"); const options_override = if (@hasDecl(root, "std_options")) root.std_options else struct {}; diff --git a/lib/std/target.zig b/lib/std/target.zig deleted file mode 100644 index bb03c8e0cd93..000000000000 --- a/lib/std/target.zig +++ /dev/null @@ -1,2608 +0,0 @@ -const std = @import("std.zig"); -const builtin = @import("builtin"); -const mem = std.mem; -const Version = std.SemanticVersion; - -pub const Target = struct { - cpu: Cpu, - os: Os, - abi: Abi, - ofmt: ObjectFormat, - - pub const Os = struct { - tag: Tag, - version_range: VersionRange, - - pub const Tag = enum { - freestanding, - ananas, - cloudabi, - dragonfly, - freebsd, - fuchsia, - ios, - kfreebsd, - linux, - lv2, - macos, - netbsd, - openbsd, - solaris, - uefi, - windows, - zos, - haiku, - minix, - rtems, - nacl, - aix, - cuda, - nvcl, - amdhsa, - ps4, - ps5, - elfiamcu, - tvos, - watchos, - driverkit, - mesa3d, - contiki, - amdpal, - hermit, - hurd, - wasi, - emscripten, - shadermodel, - liteos, - opencl, - glsl450, - vulkan, - plan9, - illumos, - other, - - pub inline fn isDarwin(tag: Tag) bool { - return switch (tag) { - .ios, .macos, .watchos, .tvos => true, - else => false, - }; - } - - pub inline fn isBSD(tag: Tag) bool { - return tag.isDarwin() or switch (tag) { - .kfreebsd, .freebsd, .openbsd, .netbsd, .dragonfly => true, - else => false, - }; - } - - pub inline fn isSolarish(tag: Tag) bool { - return tag == .solaris or tag == .illumos; - } - - pub fn dynamicLibSuffix(tag: Tag) [:0]const u8 { - if (tag.isDarwin()) { - return ".dylib"; - } - switch (tag) { - .windows => return ".dll", - else => return ".so", - } - } - - pub fn defaultVersionRange(tag: Tag, arch: Cpu.Arch) Os { - return .{ - .tag = tag, - .version_range = VersionRange.default(tag, arch), - }; - } - }; - - /// Based on NTDDI version constants from - /// https://docs.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt - pub const WindowsVersion = enum(u32) { - nt4 = 0x04000000, - win2k = 0x05000000, - xp = 0x05010000, - ws2003 = 0x05020000, - vista = 0x06000000, - win7 = 0x06010000, - win8 = 0x06020000, - win8_1 = 0x06030000, - win10 = 0x0A000000, //aka win10_th1 - win10_th2 = 0x0A000001, - win10_rs1 = 0x0A000002, - win10_rs2 = 0x0A000003, - win10_rs3 = 0x0A000004, - win10_rs4 = 0x0A000005, - win10_rs5 = 0x0A000006, - win10_19h1 = 0x0A000007, - win10_vb = 0x0A000008, //aka win10_19h2 - win10_mn = 0x0A000009, //aka win10_20h1 - win10_fe = 0x0A00000A, //aka win10_20h2 - _, - - /// Latest Windows version that the Zig Standard Library is aware of - pub const latest = WindowsVersion.win10_fe; - - /// Compared against build numbers reported by the runtime to distinguish win10 versions, - /// where 0x0A000000 + index corresponds to the WindowsVersion u32 value. - pub const known_win10_build_numbers = [_]u32{ - 10240, //win10 aka win10_th1 - 10586, //win10_th2 - 14393, //win10_rs1 - 15063, //win10_rs2 - 16299, //win10_rs3 - 17134, //win10_rs4 - 17763, //win10_rs5 - 18362, //win10_19h1 - 18363, //win10_vb aka win10_19h2 - 19041, //win10_mn aka win10_20h1 - 19042, //win10_fe aka win10_20h2 - }; - - /// Returns whether the first version `self` is newer (greater) than or equal to the second version `ver`. - pub inline fn isAtLeast(self: WindowsVersion, ver: WindowsVersion) bool { - return @intFromEnum(self) >= @intFromEnum(ver); - } - - pub const Range = struct { - min: WindowsVersion, - max: WindowsVersion, - - pub inline fn includesVersion(self: Range, ver: WindowsVersion) bool { - return @intFromEnum(ver) >= @intFromEnum(self.min) and @intFromEnum(ver) <= @intFromEnum(self.max); - } - - /// Checks if system is guaranteed to be at least `version` or older than `version`. - /// Returns `null` if a runtime check is required. - pub inline fn isAtLeast(self: Range, ver: WindowsVersion) ?bool { - if (@intFromEnum(self.min) >= @intFromEnum(ver)) return true; - if (@intFromEnum(self.max) < @intFromEnum(ver)) return false; - return null; - } - }; - - /// This function is defined to serialize a Zig source code representation of this - /// type, that, when parsed, will deserialize into the same data. - pub fn format( - self: WindowsVersion, - comptime fmt: []const u8, - _: std.fmt.FormatOptions, - out_stream: anytype, - ) !void { - if (comptime std.mem.eql(u8, fmt, "s")) { - if (@intFromEnum(self) >= @intFromEnum(WindowsVersion.nt4) and @intFromEnum(self) <= @intFromEnum(WindowsVersion.latest)) { - try std.fmt.format(out_stream, ".{s}", .{@tagName(self)}); - } else { - // TODO this code path breaks zig triples, but it is used in `builtin` - try std.fmt.format(out_stream, "@enumFromInt(Target.Os.WindowsVersion, 0x{X:0>8})", .{@intFromEnum(self)}); - } - } else if (fmt.len == 0) { - if (@intFromEnum(self) >= @intFromEnum(WindowsVersion.nt4) and @intFromEnum(self) <= @intFromEnum(WindowsVersion.latest)) { - try std.fmt.format(out_stream, "WindowsVersion.{s}", .{@tagName(self)}); - } else { - try std.fmt.format(out_stream, "WindowsVersion(0x{X:0>8})", .{@intFromEnum(self)}); - } - } else { - std.fmt.invalidFmtError(fmt, self); - } - } - }; - - pub const LinuxVersionRange = struct { - range: Version.Range, - glibc: Version, - - pub inline fn includesVersion(self: LinuxVersionRange, ver: Version) bool { - return self.range.includesVersion(ver); - } - - /// Checks if system is guaranteed to be at least `version` or older than `version`. - /// Returns `null` if a runtime check is required. - pub inline fn isAtLeast(self: LinuxVersionRange, ver: Version) ?bool { - return self.range.isAtLeast(ver); - } - }; - - /// The version ranges here represent the minimum OS version to be supported - /// and the maximum OS version to be supported. The default values represent - /// the range that the Zig Standard Library bases its abstractions on. - /// - /// The minimum version of the range is the main setting to tweak for a target. - /// Usually, the maximum target OS version will remain the default, which is - /// the latest released version of the OS. - /// - /// To test at compile time if the target is guaranteed to support a given OS feature, - /// one should check that the minimum version of the range is greater than or equal to - /// the version the feature was introduced in. - /// - /// To test at compile time if the target certainly will not support a given OS feature, - /// one should check that the maximum version of the range is less than the version the - /// feature was introduced in. - /// - /// If neither of these cases apply, a runtime check should be used to determine if the - /// target supports a given OS feature. - /// - /// Binaries built with a given maximum version will continue to function on newer - /// operating system versions. However, such a binary may not take full advantage of the - /// newer operating system APIs. - /// - /// See `Os.isAtLeast`. - pub const VersionRange = union { - none: void, - semver: Version.Range, - linux: LinuxVersionRange, - windows: WindowsVersion.Range, - - /// The default `VersionRange` represents the range that the Zig Standard Library - /// bases its abstractions on. - pub fn default(tag: Tag, arch: Cpu.Arch) VersionRange { - switch (tag) { - .freestanding, - .ananas, - .cloudabi, - .fuchsia, - .kfreebsd, - .lv2, - .zos, - .haiku, - .minix, - .rtems, - .nacl, - .aix, - .cuda, - .nvcl, - .amdhsa, - .ps4, - .ps5, - .elfiamcu, - .mesa3d, - .contiki, - .amdpal, - .hermit, - .hurd, - .wasi, - .emscripten, - .driverkit, - .shadermodel, - .liteos, - .uefi, - .opencl, // TODO: OpenCL versions - .glsl450, // TODO: GLSL versions - .vulkan, - .plan9, - .illumos, - .other, - => return .{ .none = {} }, - - .freebsd => return .{ - .semver = Version.Range{ - .min = .{ .major = 12, .minor = 0, .patch = 0 }, - .max = .{ .major = 14, .minor = 0, .patch = 0 }, - }, - }, - .macos => return switch (arch) { - .aarch64 => VersionRange{ - .semver = .{ - .min = .{ .major = 11, .minor = 7, .patch = 1 }, - .max = .{ .major = 14, .minor = 1, .patch = 0 }, - }, - }, - .x86_64 => VersionRange{ - .semver = .{ - .min = .{ .major = 11, .minor = 7, .patch = 1 }, - .max = .{ .major = 14, .minor = 1, .patch = 0 }, - }, - }, - else => unreachable, - }, - .ios => return .{ - .semver = .{ - .min = .{ .major = 12, .minor = 0, .patch = 0 }, - .max = .{ .major = 17, .minor = 1, .patch = 0 }, - }, - }, - .watchos => return .{ - .semver = .{ - .min = .{ .major = 6, .minor = 0, .patch = 0 }, - .max = .{ .major = 10, .minor = 1, .patch = 0 }, - }, - }, - .tvos => return .{ - .semver = .{ - .min = .{ .major = 13, .minor = 0, .patch = 0 }, - .max = .{ .major = 17, .minor = 1, .patch = 0 }, - }, - }, - .netbsd => return .{ - .semver = .{ - .min = .{ .major = 8, .minor = 0, .patch = 0 }, - .max = .{ .major = 10, .minor = 0, .patch = 0 }, - }, - }, - .openbsd => return .{ - .semver = .{ - .min = .{ .major = 6, .minor = 8, .patch = 0 }, - .max = .{ .major = 7, .minor = 4, .patch = 0 }, - }, - }, - .dragonfly => return .{ - .semver = .{ - .min = .{ .major = 5, .minor = 8, .patch = 0 }, - .max = .{ .major = 6, .minor = 4, .patch = 0 }, - }, - }, - .solaris => return .{ - .semver = .{ - .min = .{ .major = 5, .minor = 11, .patch = 0 }, - .max = .{ .major = 5, .minor = 11, .patch = 0 }, - }, - }, - - .linux => return .{ - .linux = .{ - .range = .{ - .min = .{ .major = 4, .minor = 19, .patch = 0 }, - .max = .{ .major = 6, .minor = 5, .patch = 7 }, - }, - .glibc = .{ .major = 2, .minor = 28, .patch = 0 }, - }, - }, - - .windows => return .{ - .windows = .{ - .min = .win8_1, - .max = WindowsVersion.latest, - }, - }, - } - } - }; - - pub const TaggedVersionRange = union(enum) { - none: void, - semver: Version.Range, - linux: LinuxVersionRange, - windows: WindowsVersion.Range, - }; - - /// Provides a tagged union. `Target` does not store the tag because it is - /// redundant with the OS tag; this function abstracts that part away. - pub inline fn getVersionRange(self: Os) TaggedVersionRange { - switch (self.tag) { - .linux => return TaggedVersionRange{ .linux = self.version_range.linux }, - .windows => return TaggedVersionRange{ .windows = self.version_range.windows }, - - .freebsd, - .macos, - .ios, - .tvos, - .watchos, - .netbsd, - .openbsd, - .dragonfly, - .solaris, - => return TaggedVersionRange{ .semver = self.version_range.semver }, - - else => return .none, - } - } - - /// Checks if system is guaranteed to be at least `version` or older than `version`. - /// Returns `null` if a runtime check is required. - pub inline fn isAtLeast(self: Os, comptime tag: Tag, version: anytype) ?bool { - if (self.tag != tag) return false; - - return switch (tag) { - .linux => self.version_range.linux.isAtLeast(version), - .windows => self.version_range.windows.isAtLeast(version), - else => self.version_range.semver.isAtLeast(version), - }; - } - - /// On Darwin, we always link libSystem which contains libc. - /// Similarly on FreeBSD and NetBSD we always link system libc - /// since this is the stable syscall interface. - pub fn requiresLibC(os: Os) bool { - return switch (os.tag) { - .freebsd, - .netbsd, - .macos, - .ios, - .tvos, - .watchos, - .dragonfly, - .openbsd, - .haiku, - .solaris, - .illumos, - => true, - - .linux, - .windows, - .freestanding, - .ananas, - .cloudabi, - .fuchsia, - .kfreebsd, - .lv2, - .zos, - .minix, - .rtems, - .nacl, - .aix, - .cuda, - .nvcl, - .amdhsa, - .ps4, - .ps5, - .elfiamcu, - .mesa3d, - .contiki, - .amdpal, - .hermit, - .hurd, - .wasi, - .emscripten, - .driverkit, - .shadermodel, - .liteos, - .uefi, - .opencl, - .glsl450, - .vulkan, - .plan9, - .other, - => false, - }; - } - }; - - pub const aarch64 = @import("target/aarch64.zig"); - pub const arc = @import("target/arc.zig"); - pub const amdgpu = @import("target/amdgpu.zig"); - pub const arm = @import("target/arm.zig"); - pub const avr = @import("target/avr.zig"); - pub const bpf = @import("target/bpf.zig"); - pub const csky = @import("target/csky.zig"); - pub const hexagon = @import("target/hexagon.zig"); - pub const loongarch = @import("target/loongarch.zig"); - pub const m68k = @import("target/m68k.zig"); - pub const mips = @import("target/mips.zig"); - pub const msp430 = @import("target/msp430.zig"); - pub const nvptx = @import("target/nvptx.zig"); - pub const powerpc = @import("target/powerpc.zig"); - pub const riscv = @import("target/riscv.zig"); - pub const sparc = @import("target/sparc.zig"); - pub const spirv = @import("target/spirv.zig"); - pub const s390x = @import("target/s390x.zig"); - pub const ve = @import("target/ve.zig"); - pub const wasm = @import("target/wasm.zig"); - pub const x86 = @import("target/x86.zig"); - pub const xtensa = @import("target/xtensa.zig"); - - pub const Abi = enum { - none, - gnu, - gnuabin32, - gnuabi64, - gnueabi, - gnueabihf, - gnuf32, - gnuf64, - gnusf, - gnux32, - gnuilp32, - code16, - eabi, - eabihf, - android, - musl, - musleabi, - musleabihf, - muslx32, - msvc, - itanium, - cygnus, - coreclr, - simulator, - macabi, - pixel, - vertex, - geometry, - hull, - domain, - compute, - library, - raygeneration, - intersection, - anyhit, - closesthit, - miss, - callable, - mesh, - amplification, - - pub fn default(arch: Cpu.Arch, target_os: Os) Abi { - if (arch.isWasm()) { - return .musl; - } - switch (target_os.tag) { - .freestanding, - .ananas, - .cloudabi, - .dragonfly, - .lv2, - .zos, - .minix, - .rtems, - .nacl, - .aix, - .cuda, - .nvcl, - .amdhsa, - .ps4, - .ps5, - .elfiamcu, - .mesa3d, - .contiki, - .amdpal, - .hermit, - .other, - => return .eabi, - .openbsd, - .freebsd, - .fuchsia, - .kfreebsd, - .netbsd, - .hurd, - .haiku, - .windows, - => return .gnu, - .uefi => return .msvc, - .linux, - .wasi, - .emscripten, - => return .musl, - .opencl, // TODO: SPIR-V ABIs with Linkage capability - .glsl450, - .vulkan, - .plan9, // TODO specify abi - .macos, - .ios, - .tvos, - .watchos, - .driverkit, - .shadermodel, - .liteos, // TODO: audit this - .solaris, - .illumos, - => return .none, - } - } - - pub inline fn isGnu(abi: Abi) bool { - return switch (abi) { - .gnu, .gnuabin32, .gnuabi64, .gnueabi, .gnueabihf, .gnux32 => true, - else => false, - }; - } - - pub inline fn isMusl(abi: Abi) bool { - return switch (abi) { - .musl, .musleabi, .musleabihf => true, - else => false, - }; - } - - pub inline fn floatAbi(abi: Abi) FloatAbi { - return switch (abi) { - .gnueabihf, - .eabihf, - .musleabihf, - => .hard, - else => .soft, - }; - } - }; - - pub const ObjectFormat = enum { - /// Common Object File Format (Windows) - coff, - /// DirectX Container - dxcontainer, - /// Executable and Linking Format - elf, - /// macOS relocatables - macho, - /// Standard, Portable Intermediate Representation V - spirv, - /// WebAssembly - wasm, - /// C source code - c, - /// Intel IHEX - hex, - /// Machine code with no metadata. - raw, - /// Plan 9 from Bell Labs - plan9, - /// Nvidia PTX format - nvptx, - - pub fn fileExt(of: ObjectFormat, cpu_arch: Cpu.Arch) [:0]const u8 { - return switch (of) { - .coff => ".obj", - .elf, .macho, .wasm => ".o", - .c => ".c", - .spirv => ".spv", - .hex => ".ihex", - .raw => ".bin", - .plan9 => plan9Ext(cpu_arch), - .nvptx => ".ptx", - .dxcontainer => ".dxil", - }; - } - - pub fn default(os_tag: Os.Tag, cpu_arch: Cpu.Arch) ObjectFormat { - return switch (os_tag) { - .windows, .uefi => .coff, - .ios, .macos, .watchos, .tvos => .macho, - .plan9 => .plan9, - else => return switch (cpu_arch) { - .wasm32, .wasm64 => .wasm, - .spirv32, .spirv64 => .spirv, - .nvptx, .nvptx64 => .nvptx, - else => .elf, - }, - }; - } - }; - - pub const SubSystem = enum { - Console, - Windows, - Posix, - Native, - EfiApplication, - EfiBootServiceDriver, - EfiRom, - EfiRuntimeDriver, - }; - - pub const Cpu = struct { - /// Architecture - arch: Arch, - - /// The CPU model to target. It has a set of features - /// which are overridden with the `features` field. - model: *const Model, - - /// An explicit list of the entire CPU feature set. It may differ from the specific CPU model's features. - features: Feature.Set, - - pub const Feature = struct { - /// The bit index into `Set`. Has a default value of `undefined` because the canonical - /// structures are populated via comptime logic. - index: Set.Index = undefined, - - /// Has a default value of `undefined` because the canonical - /// structures are populated via comptime logic. - name: []const u8 = undefined, - - /// If this corresponds to an LLVM-recognized feature, this will be populated; - /// otherwise null. - llvm_name: ?[:0]const u8, - - /// Human-friendly UTF-8 text. - description: []const u8, - - /// Sparse `Set` of features this depends on. - dependencies: Set, - - /// A bit set of all the features. - pub const Set = struct { - ints: [usize_count]usize, - - pub const needed_bit_count = 288; - pub const byte_count = (needed_bit_count + 7) / 8; - pub const usize_count = (byte_count + (@sizeOf(usize) - 1)) / @sizeOf(usize); - pub const Index = std.math.Log2Int(std.meta.Int(.unsigned, usize_count * @bitSizeOf(usize))); - pub const ShiftInt = std.math.Log2Int(usize); - - pub const empty = Set{ .ints = [1]usize{0} ** usize_count }; - - pub fn isEmpty(set: Set) bool { - return for (set.ints) |x| { - if (x != 0) break false; - } else true; - } - - pub fn isEnabled(set: Set, arch_feature_index: Index) bool { - const usize_index = arch_feature_index / @bitSizeOf(usize); - const bit_index = @as(ShiftInt, @intCast(arch_feature_index % @bitSizeOf(usize))); - return (set.ints[usize_index] & (@as(usize, 1) << bit_index)) != 0; - } - - /// Adds the specified feature but not its dependencies. - pub fn addFeature(set: *Set, arch_feature_index: Index) void { - const usize_index = arch_feature_index / @bitSizeOf(usize); - const bit_index = @as(ShiftInt, @intCast(arch_feature_index % @bitSizeOf(usize))); - set.ints[usize_index] |= @as(usize, 1) << bit_index; - } - - /// Adds the specified feature set but not its dependencies. - pub fn addFeatureSet(set: *Set, other_set: Set) void { - switch (builtin.zig_backend) { - .stage2_x86_64 => { - for (&set.ints, other_set.ints) |*set_int, other_set_int| set_int.* |= other_set_int; - }, - else => { - set.ints = @as(@Vector(usize_count, usize), set.ints) | @as(@Vector(usize_count, usize), other_set.ints); - }, - } - } - - /// Removes the specified feature but not its dependents. - pub fn removeFeature(set: *Set, arch_feature_index: Index) void { - const usize_index = arch_feature_index / @bitSizeOf(usize); - const bit_index = @as(ShiftInt, @intCast(arch_feature_index % @bitSizeOf(usize))); - set.ints[usize_index] &= ~(@as(usize, 1) << bit_index); - } - - /// Removes the specified feature but not its dependents. - pub fn removeFeatureSet(set: *Set, other_set: Set) void { - switch (builtin.zig_backend) { - .stage2_x86_64 => { - for (&set.ints, other_set.ints) |*set_int, other_set_int| set_int.* &= ~other_set_int; - }, - else => { - set.ints = @as(@Vector(usize_count, usize), set.ints) & ~@as(@Vector(usize_count, usize), other_set.ints); - }, - } - } - - pub fn populateDependencies(set: *Set, all_features_list: []const Cpu.Feature) void { - @setEvalBranchQuota(1000000); - - var old = set.ints; - while (true) { - for (all_features_list, 0..) |feature, index_usize| { - const index = @as(Index, @intCast(index_usize)); - if (set.isEnabled(index)) { - set.addFeatureSet(feature.dependencies); - } - } - const nothing_changed = mem.eql(usize, &old, &set.ints); - if (nothing_changed) return; - old = set.ints; - } - } - - pub fn asBytes(set: *const Set) *const [byte_count]u8 { - return @as(*const [byte_count]u8, @ptrCast(&set.ints)); - } - - pub fn eql(set: Set, other_set: Set) bool { - return mem.eql(usize, &set.ints, &other_set.ints); - } - - pub fn isSuperSetOf(set: Set, other_set: Set) bool { - switch (builtin.zig_backend) { - .stage2_x86_64 => { - var result = true; - for (&set.ints, other_set.ints) |*set_int, other_set_int| - result = result and (set_int.* & other_set_int) == other_set_int; - return result; - }, - else => { - const V = @Vector(usize_count, usize); - const set_v: V = set.ints; - const other_v: V = other_set.ints; - return @reduce(.And, (set_v & other_v) == other_v); - }, - } - } - }; - - pub fn feature_set_fns(comptime F: type) type { - return struct { - /// Populates only the feature bits specified. - pub fn featureSet(features: []const F) Set { - var x = Set.empty; - for (features) |feature| { - x.addFeature(@intFromEnum(feature)); - } - return x; - } - - /// Returns true if the specified feature is enabled. - pub fn featureSetHas(set: Set, feature: F) bool { - return set.isEnabled(@intFromEnum(feature)); - } - - /// Returns true if any specified feature is enabled. - pub fn featureSetHasAny(set: Set, features: anytype) bool { - inline for (features) |feature| { - if (set.isEnabled(@intFromEnum(@as(F, feature)))) return true; - } - return false; - } - - /// Returns true if every specified feature is enabled. - pub fn featureSetHasAll(set: Set, features: anytype) bool { - inline for (features) |feature| { - if (!set.isEnabled(@intFromEnum(@as(F, feature)))) return false; - } - return true; - } - }; - } - }; - - pub const Arch = enum { - arm, - armeb, - aarch64, - aarch64_be, - aarch64_32, - arc, - avr, - bpfel, - bpfeb, - csky, - dxil, - hexagon, - loongarch32, - loongarch64, - m68k, - mips, - mipsel, - mips64, - mips64el, - msp430, - powerpc, - powerpcle, - powerpc64, - powerpc64le, - r600, - amdgcn, - riscv32, - riscv64, - sparc, - sparc64, - sparcel, - s390x, - tce, - tcele, - thumb, - thumbeb, - x86, - x86_64, - xcore, - xtensa, - nvptx, - nvptx64, - le32, - le64, - amdil, - amdil64, - hsail, - hsail64, - spir, - spir64, - spirv32, - spirv64, - kalimba, - shave, - lanai, - wasm32, - wasm64, - renderscript32, - renderscript64, - ve, - // Stage1 currently assumes that architectures above this comment - // map one-to-one with the ZigLLVM_ArchType enum. - spu_2, - - pub inline fn isX86(arch: Arch) bool { - return switch (arch) { - .x86, .x86_64 => true, - else => false, - }; - } - - pub inline fn isARM(arch: Arch) bool { - return switch (arch) { - .arm, .armeb => true, - else => false, - }; - } - - pub inline fn isAARCH64(arch: Arch) bool { - return switch (arch) { - .aarch64, .aarch64_be, .aarch64_32 => true, - else => false, - }; - } - - pub inline fn isThumb(arch: Arch) bool { - return switch (arch) { - .thumb, .thumbeb => true, - else => false, - }; - } - - pub inline fn isArmOrThumb(arch: Arch) bool { - return arch.isARM() or arch.isThumb(); - } - - pub inline fn isWasm(arch: Arch) bool { - return switch (arch) { - .wasm32, .wasm64 => true, - else => false, - }; - } - - pub inline fn isRISCV(arch: Arch) bool { - return switch (arch) { - .riscv32, .riscv64 => true, - else => false, - }; - } - - pub inline fn isMIPS(arch: Arch) bool { - return switch (arch) { - .mips, .mipsel, .mips64, .mips64el => true, - else => false, - }; - } - - pub inline fn isPPC(arch: Arch) bool { - return switch (arch) { - .powerpc, .powerpcle => true, - else => false, - }; - } - - pub inline fn isPPC64(arch: Arch) bool { - return switch (arch) { - .powerpc64, .powerpc64le => true, - else => false, - }; - } - - pub inline fn isSPARC(arch: Arch) bool { - return switch (arch) { - .sparc, .sparcel, .sparc64 => true, - else => false, - }; - } - - pub inline fn isSpirV(arch: Arch) bool { - return switch (arch) { - .spirv32, .spirv64 => true, - else => false, - }; - } - - pub inline fn isBpf(arch: Arch) bool { - return switch (arch) { - .bpfel, .bpfeb => true, - else => false, - }; - } - - pub inline fn isNvptx(arch: Arch) bool { - return switch (arch) { - .nvptx, .nvptx64 => true, - else => false, - }; - } - - pub fn parseCpuModel(arch: Arch, cpu_name: []const u8) !*const Cpu.Model { - for (arch.allCpuModels()) |cpu| { - if (mem.eql(u8, cpu_name, cpu.name)) { - return cpu; - } - } - return error.UnknownCpuModel; - } - - pub fn toElfMachine(arch: Arch) std.elf.EM { - return switch (arch) { - .avr => .AVR, - .msp430 => .MSP430, - .arc => .ARC, - .arm => .ARM, - .armeb => .ARM, - .hexagon => .HEXAGON, - .dxil => .NONE, - .m68k => .@"68K", - .le32 => .NONE, - .mips => .MIPS, - .mipsel => .MIPS_RS3_LE, - .powerpc, .powerpcle => .PPC, - .r600 => .NONE, - .riscv32 => .RISCV, - .sparc => .SPARC, - .sparcel => .SPARC, - .tce => .NONE, - .tcele => .NONE, - .thumb => .ARM, - .thumbeb => .ARM, - .x86 => .@"386", - .xcore => .XCORE, - .xtensa => .XTENSA, - .nvptx => .NONE, - .amdil => .NONE, - .hsail => .NONE, - .spir => .NONE, - .kalimba => .CSR_KALIMBA, - .shave => .NONE, - .lanai => .LANAI, - .wasm32 => .NONE, - .renderscript32 => .NONE, - .aarch64_32 => .AARCH64, - .aarch64 => .AARCH64, - .aarch64_be => .AARCH64, - .mips64 => .MIPS, - .mips64el => .MIPS_RS3_LE, - .powerpc64 => .PPC64, - .powerpc64le => .PPC64, - .riscv64 => .RISCV, - .x86_64 => .X86_64, - .nvptx64 => .NONE, - .le64 => .NONE, - .amdil64 => .NONE, - .hsail64 => .NONE, - .spir64 => .NONE, - .wasm64 => .NONE, - .renderscript64 => .NONE, - .amdgcn => .AMDGPU, - .bpfel => .BPF, - .bpfeb => .BPF, - .csky => .CSKY, - .sparc64 => .SPARCV9, - .s390x => .S390, - .ve => .NONE, - .spu_2 => .SPU_2, - .spirv32 => .NONE, - .spirv64 => .NONE, - .loongarch32 => .NONE, - .loongarch64 => .NONE, - }; - } - - pub fn toCoffMachine(arch: Arch) std.coff.MachineType { - return switch (arch) { - .avr => .Unknown, - .msp430 => .Unknown, - .arc => .Unknown, - .arm => .ARM, - .armeb => .Unknown, - .dxil => .Unknown, - .hexagon => .Unknown, - .m68k => .Unknown, - .le32 => .Unknown, - .mips => .Unknown, - .mipsel => .Unknown, - .powerpc, .powerpcle => .POWERPC, - .r600 => .Unknown, - .riscv32 => .RISCV32, - .sparc => .Unknown, - .sparcel => .Unknown, - .tce => .Unknown, - .tcele => .Unknown, - .thumb => .Thumb, - .thumbeb => .Thumb, - .x86 => .I386, - .xcore => .Unknown, - .xtensa => .Unknown, - .nvptx => .Unknown, - .amdil => .Unknown, - .hsail => .Unknown, - .spir => .Unknown, - .kalimba => .Unknown, - .shave => .Unknown, - .lanai => .Unknown, - .wasm32 => .Unknown, - .renderscript32 => .Unknown, - .aarch64_32 => .ARM64, - .aarch64 => .ARM64, - .aarch64_be => .ARM64, - .mips64 => .Unknown, - .mips64el => .Unknown, - .powerpc64 => .Unknown, - .powerpc64le => .Unknown, - .riscv64 => .RISCV64, - .x86_64 => .X64, - .nvptx64 => .Unknown, - .le64 => .Unknown, - .amdil64 => .Unknown, - .hsail64 => .Unknown, - .spir64 => .Unknown, - .wasm64 => .Unknown, - .renderscript64 => .Unknown, - .amdgcn => .Unknown, - .bpfel => .Unknown, - .bpfeb => .Unknown, - .csky => .Unknown, - .sparc64 => .Unknown, - .s390x => .Unknown, - .ve => .Unknown, - .spu_2 => .Unknown, - .spirv32 => .Unknown, - .spirv64 => .Unknown, - .loongarch32 => .Unknown, - .loongarch64 => .Unknown, - }; - } - - pub fn endian(arch: Arch) std.builtin.Endian { - return switch (arch) { - .avr, - .arm, - .aarch64_32, - .aarch64, - .amdgcn, - .amdil, - .amdil64, - .bpfel, - .csky, - .xtensa, - .hexagon, - .hsail, - .hsail64, - .kalimba, - .le32, - .le64, - .mipsel, - .mips64el, - .msp430, - .nvptx, - .nvptx64, - .sparcel, - .tcele, - .powerpcle, - .powerpc64le, - .r600, - .riscv32, - .riscv64, - .x86, - .x86_64, - .wasm32, - .wasm64, - .xcore, - .thumb, - .spir, - .spir64, - .renderscript32, - .renderscript64, - .shave, - .ve, - .spu_2, - // GPU bitness is opaque. For now, assume little endian. - .spirv32, - .spirv64, - .dxil, - .loongarch32, - .loongarch64, - .arc, - => .little, - - .armeb, - .aarch64_be, - .bpfeb, - .m68k, - .mips, - .mips64, - .powerpc, - .powerpc64, - .thumbeb, - .sparc, - .sparc64, - .tce, - .lanai, - .s390x, - => .big, - }; - } - - /// Returns whether this architecture supports the address space - pub fn supportsAddressSpace(arch: Arch, address_space: std.builtin.AddressSpace) bool { - const is_nvptx = arch == .nvptx or arch == .nvptx64; - const is_spirv = arch == .spirv32 or arch == .spirv64; - const is_gpu = is_nvptx or is_spirv or arch == .amdgcn; - return switch (address_space) { - .generic => true, - .fs, .gs, .ss => arch == .x86_64 or arch == .x86, - .global, .constant, .local, .shared => is_gpu, - .param => is_nvptx, - // TODO this should also check how many flash banks the cpu has - .flash, .flash1, .flash2, .flash3, .flash4, .flash5 => arch == .avr, - }; - } - - /// Returns a name that matches the lib/std/target/* source file name. - pub fn genericName(arch: Arch) []const u8 { - return switch (arch) { - .arm, .armeb, .thumb, .thumbeb => "arm", - .aarch64, .aarch64_be, .aarch64_32 => "aarch64", - .bpfel, .bpfeb => "bpf", - .loongarch32, .loongarch64 => "loongarch", - .mips, .mipsel, .mips64, .mips64el => "mips", - .powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc", - .amdgcn => "amdgpu", - .riscv32, .riscv64 => "riscv", - .sparc, .sparc64, .sparcel => "sparc", - .s390x => "s390x", - .x86, .x86_64 => "x86", - .nvptx, .nvptx64 => "nvptx", - .wasm32, .wasm64 => "wasm", - .spirv32, .spirv64 => "spirv", - else => @tagName(arch), - }; - } - - /// All CPU features Zig is aware of, sorted lexicographically by name. - pub fn allFeaturesList(arch: Arch) []const Cpu.Feature { - return switch (arch) { - .arm, .armeb, .thumb, .thumbeb => &arm.all_features, - .aarch64, .aarch64_be, .aarch64_32 => &aarch64.all_features, - .arc => &arc.all_features, - .avr => &avr.all_features, - .bpfel, .bpfeb => &bpf.all_features, - .csky => &csky.all_features, - .hexagon => &hexagon.all_features, - .loongarch32, .loongarch64 => &loongarch.all_features, - .m68k => &m68k.all_features, - .mips, .mipsel, .mips64, .mips64el => &mips.all_features, - .msp430 => &msp430.all_features, - .powerpc, .powerpcle, .powerpc64, .powerpc64le => &powerpc.all_features, - .amdgcn => &amdgpu.all_features, - .riscv32, .riscv64 => &riscv.all_features, - .sparc, .sparc64, .sparcel => &sparc.all_features, - .spirv32, .spirv64 => &spirv.all_features, - .s390x => &s390x.all_features, - .x86, .x86_64 => &x86.all_features, - .xtensa => &xtensa.all_features, - .nvptx, .nvptx64 => &nvptx.all_features, - .ve => &ve.all_features, - .wasm32, .wasm64 => &wasm.all_features, - - else => &[0]Cpu.Feature{}, - }; - } - - /// All processors Zig is aware of, sorted lexicographically by name. - pub fn allCpuModels(arch: Arch) []const *const Cpu.Model { - return switch (arch) { - .arc => comptime allCpusFromDecls(arc.cpu), - .arm, .armeb, .thumb, .thumbeb => comptime allCpusFromDecls(arm.cpu), - .aarch64, .aarch64_be, .aarch64_32 => comptime allCpusFromDecls(aarch64.cpu), - .avr => comptime allCpusFromDecls(avr.cpu), - .bpfel, .bpfeb => comptime allCpusFromDecls(bpf.cpu), - .csky => comptime allCpusFromDecls(csky.cpu), - .hexagon => comptime allCpusFromDecls(hexagon.cpu), - .loongarch32, .loongarch64 => comptime allCpusFromDecls(loongarch.cpu), - .m68k => comptime allCpusFromDecls(m68k.cpu), - .mips, .mipsel, .mips64, .mips64el => comptime allCpusFromDecls(mips.cpu), - .msp430 => comptime allCpusFromDecls(msp430.cpu), - .powerpc, .powerpcle, .powerpc64, .powerpc64le => comptime allCpusFromDecls(powerpc.cpu), - .amdgcn => comptime allCpusFromDecls(amdgpu.cpu), - .riscv32, .riscv64 => comptime allCpusFromDecls(riscv.cpu), - .sparc, .sparc64, .sparcel => comptime allCpusFromDecls(sparc.cpu), - .spirv32, .spirv64 => comptime allCpusFromDecls(spirv.cpu), - .s390x => comptime allCpusFromDecls(s390x.cpu), - .x86, .x86_64 => comptime allCpusFromDecls(x86.cpu), - .xtensa => comptime allCpusFromDecls(xtensa.cpu), - .nvptx, .nvptx64 => comptime allCpusFromDecls(nvptx.cpu), - .ve => comptime allCpusFromDecls(ve.cpu), - .wasm32, .wasm64 => comptime allCpusFromDecls(wasm.cpu), - - else => &[0]*const Model{}, - }; - } - - fn allCpusFromDecls(comptime cpus: type) []const *const Cpu.Model { - const decls = @typeInfo(cpus).Struct.decls; - var array: [decls.len]*const Cpu.Model = undefined; - for (decls, 0..) |decl, i| { - array[i] = &@field(cpus, decl.name); - } - return &array; - } - }; - - pub const Model = struct { - name: []const u8, - llvm_name: ?[:0]const u8, - features: Feature.Set, - - pub fn toCpu(model: *const Model, arch: Arch) Cpu { - var features = model.features; - features.populateDependencies(arch.allFeaturesList()); - return .{ - .arch = arch, - .model = model, - .features = features, - }; - } - - pub fn generic(arch: Arch) *const Model { - const S = struct { - const generic_model = Model{ - .name = "generic", - .llvm_name = null, - .features = Cpu.Feature.Set.empty, - }; - }; - return switch (arch) { - .arm, .armeb, .thumb, .thumbeb => &arm.cpu.generic, - .aarch64, .aarch64_be, .aarch64_32 => &aarch64.cpu.generic, - .avr => &avr.cpu.avr2, - .bpfel, .bpfeb => &bpf.cpu.generic, - .hexagon => &hexagon.cpu.generic, - .loongarch32 => &loongarch.cpu.generic_la32, - .loongarch64 => &loongarch.cpu.generic_la64, - .m68k => &m68k.cpu.generic, - .mips, .mipsel => &mips.cpu.mips32, - .mips64, .mips64el => &mips.cpu.mips64, - .msp430 => &msp430.cpu.generic, - .powerpc => &powerpc.cpu.ppc, - .powerpcle => &powerpc.cpu.ppc, - .powerpc64 => &powerpc.cpu.ppc64, - .powerpc64le => &powerpc.cpu.ppc64le, - .amdgcn => &amdgpu.cpu.generic, - .riscv32 => &riscv.cpu.generic_rv32, - .riscv64 => &riscv.cpu.generic_rv64, - .spirv32, .spirv64 => &spirv.cpu.generic, - .sparc, .sparcel => &sparc.cpu.generic, - .sparc64 => &sparc.cpu.v9, // 64-bit SPARC needs v9 as the baseline - .s390x => &s390x.cpu.generic, - .x86 => &x86.cpu.i386, - .x86_64 => &x86.cpu.x86_64, - .nvptx, .nvptx64 => &nvptx.cpu.sm_20, - .ve => &ve.cpu.generic, - .wasm32, .wasm64 => &wasm.cpu.generic, - - else => &S.generic_model, - }; - } - - pub fn baseline(arch: Arch) *const Model { - return switch (arch) { - .arm, .armeb, .thumb, .thumbeb => &arm.cpu.baseline, - .riscv32 => &riscv.cpu.baseline_rv32, - .riscv64 => &riscv.cpu.baseline_rv64, - .x86 => &x86.cpu.pentium4, - .nvptx, .nvptx64 => &nvptx.cpu.sm_20, - .sparc, .sparcel => &sparc.cpu.v8, - - else => generic(arch), - }; - } - }; - - /// The "default" set of CPU features for cross-compiling. A conservative set - /// of features that is expected to be supported on most available hardware. - pub fn baseline(arch: Arch) Cpu { - return Model.baseline(arch).toCpu(arch); - } - }; - - pub fn zigTriple(self: Target, allocator: mem.Allocator) ![]u8 { - return std.zig.CrossTarget.fromTarget(self).zigTriple(allocator); - } - - pub fn linuxTripleSimple(allocator: mem.Allocator, cpu_arch: Cpu.Arch, os_tag: Os.Tag, abi: Abi) ![]u8 { - return std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{ @tagName(cpu_arch), @tagName(os_tag), @tagName(abi) }); - } - - pub fn linuxTriple(self: Target, allocator: mem.Allocator) ![]u8 { - return linuxTripleSimple(allocator, self.cpu.arch, self.os.tag, self.abi); - } - - pub fn exeFileExtSimple(cpu_arch: Cpu.Arch, os_tag: Os.Tag) [:0]const u8 { - return switch (os_tag) { - .windows => ".exe", - .uefi => ".efi", - .plan9 => plan9Ext(cpu_arch), - else => switch (cpu_arch) { - .wasm32, .wasm64 => ".wasm", - else => "", - }, - }; - } - - pub fn exeFileExt(self: Target) [:0]const u8 { - return exeFileExtSimple(self.cpu.arch, self.os.tag); - } - - pub fn staticLibSuffix_os_abi(os_tag: Os.Tag, abi: Abi) [:0]const u8 { - if (abi == .msvc) { - return ".lib"; - } - switch (os_tag) { - .windows, .uefi => return ".lib", - else => return ".a", - } - } - - pub fn staticLibSuffix(self: Target) [:0]const u8 { - return staticLibSuffix_os_abi(self.os.tag, self.abi); - } - - pub fn dynamicLibSuffix(self: Target) [:0]const u8 { - return self.os.tag.dynamicLibSuffix(); - } - - pub fn libPrefix_os_abi(os_tag: Os.Tag, abi: Abi) [:0]const u8 { - if (abi == .msvc) { - return ""; - } - switch (os_tag) { - .windows, .uefi => return "", - else => return "lib", - } - } - - pub fn libPrefix(self: Target) [:0]const u8 { - return libPrefix_os_abi(self.os.tag, self.abi); - } - - pub inline fn isMinGW(self: Target) bool { - return self.os.tag == .windows and self.isGnu(); - } - - pub inline fn isGnu(self: Target) bool { - return self.abi.isGnu(); - } - - pub inline fn isMusl(self: Target) bool { - return self.abi.isMusl(); - } - - pub inline fn isAndroid(self: Target) bool { - return self.abi == .android; - } - - pub inline fn isWasm(self: Target) bool { - return self.cpu.arch.isWasm(); - } - - pub inline fn isDarwin(self: Target) bool { - return self.os.tag.isDarwin(); - } - - pub inline fn isBSD(self: Target) bool { - return self.os.tag.isBSD(); - } - - pub inline fn isBpfFreestanding(self: Target) bool { - return self.cpu.arch.isBpf() and self.os.tag == .freestanding; - } - - pub inline fn isGnuLibC_os_tag_abi(os_tag: Os.Tag, abi: Abi) bool { - return os_tag == .linux and abi.isGnu(); - } - - pub inline fn isGnuLibC(self: Target) bool { - return isGnuLibC_os_tag_abi(self.os.tag, self.abi); - } - - pub inline fn supportsNewStackCall(self: Target) bool { - return !self.cpu.arch.isWasm(); - } - - pub inline fn isSpirV(self: Target) bool { - return self.cpu.arch.isSpirV(); - } - - pub const FloatAbi = enum { - hard, - soft, - }; - - pub inline fn getFloatAbi(self: Target) FloatAbi { - return self.abi.floatAbi(); - } - - pub inline fn hasDynamicLinker(self: Target) bool { - if (self.cpu.arch.isWasm()) { - return false; - } - switch (self.os.tag) { - .freestanding, - .ios, - .tvos, - .watchos, - .macos, - .uefi, - .windows, - .emscripten, - .opencl, - .glsl450, - .vulkan, - .plan9, - .other, - => return false, - else => return true, - } - } - - pub const DynamicLinker = struct { - /// Contains the memory used to store the dynamic linker path. This field should - /// not be used directly. See `get` and `set`. This field exists so that this API requires no allocator. - buffer: [255]u8 = undefined, - - /// Used to construct the dynamic linker path. This field should not be used - /// directly. See `get` and `set`. - max_byte: ?u8 = null, - - /// Asserts that the length is less than or equal to 255 bytes. - pub fn init(dl_or_null: ?[]const u8) DynamicLinker { - var result: DynamicLinker = undefined; - result.set(dl_or_null); - return result; - } - - /// The returned memory has the same lifetime as the `DynamicLinker`. - pub fn get(self: *const DynamicLinker) ?[]const u8 { - const m: usize = self.max_byte orelse return null; - return self.buffer[0 .. m + 1]; - } - - /// Asserts that the length is less than or equal to 255 bytes. - pub fn set(self: *DynamicLinker, dl_or_null: ?[]const u8) void { - if (dl_or_null) |dl| { - @memcpy(self.buffer[0..dl.len], dl); - self.max_byte = @as(u8, @intCast(dl.len - 1)); - } else { - self.max_byte = null; - } - } - }; - - pub fn standardDynamicLinkerPath(self: Target) DynamicLinker { - var result: DynamicLinker = .{}; - const S = struct { - fn print(r: *DynamicLinker, comptime fmt: []const u8, args: anytype) DynamicLinker { - r.max_byte = @as(u8, @intCast((std.fmt.bufPrint(&r.buffer, fmt, args) catch unreachable).len - 1)); - return r.*; - } - fn copy(r: *DynamicLinker, s: []const u8) DynamicLinker { - @memcpy(r.buffer[0..s.len], s); - r.max_byte = @as(u8, @intCast(s.len - 1)); - return r.*; - } - }; - const print = S.print; - const copy = S.copy; - - if (self.abi == .android) { - const suffix = if (self.ptrBitWidth() == 64) "64" else ""; - return print(&result, "/system/bin/linker{s}", .{suffix}); - } - - if (self.abi.isMusl()) { - const is_arm = switch (self.cpu.arch) { - .arm, .armeb, .thumb, .thumbeb => true, - else => false, - }; - const arch_part = switch (self.cpu.arch) { - .arm, .thumb => "arm", - .armeb, .thumbeb => "armeb", - else => |arch| @tagName(arch), - }; - const arch_suffix = if (is_arm and self.abi.floatAbi() == .hard) "hf" else ""; - return print(&result, "/lib/ld-musl-{s}{s}.so.1", .{ arch_part, arch_suffix }); - } - - switch (self.os.tag) { - .freebsd => return copy(&result, "/libexec/ld-elf.so.1"), - .netbsd => return copy(&result, "/libexec/ld.elf_so"), - .openbsd => return copy(&result, "/usr/libexec/ld.so"), - .dragonfly => return copy(&result, "/libexec/ld-elf.so.2"), - .solaris, .illumos => return copy(&result, "/lib/64/ld.so.1"), - .linux => switch (self.cpu.arch) { - .x86, - .sparc, - .sparcel, - => return copy(&result, "/lib/ld-linux.so.2"), - - .aarch64 => return copy(&result, "/lib/ld-linux-aarch64.so.1"), - .aarch64_be => return copy(&result, "/lib/ld-linux-aarch64_be.so.1"), - .aarch64_32 => return copy(&result, "/lib/ld-linux-aarch64_32.so.1"), - - .arm, - .armeb, - .thumb, - .thumbeb, - => return copy(&result, switch (self.abi.floatAbi()) { - .hard => "/lib/ld-linux-armhf.so.3", - else => "/lib/ld-linux.so.3", - }), - - .mips, - .mipsel, - .mips64, - .mips64el, - => { - const lib_suffix = switch (self.abi) { - .gnuabin32, .gnux32 => "32", - .gnuabi64 => "64", - else => "", - }; - const is_nan_2008 = mips.featureSetHas(self.cpu.features, .nan2008); - const loader = if (is_nan_2008) "ld-linux-mipsn8.so.1" else "ld.so.1"; - return print(&result, "/lib{s}/{s}", .{ lib_suffix, loader }); - }, - - .powerpc, .powerpcle => return copy(&result, "/lib/ld.so.1"), - .powerpc64, .powerpc64le => return copy(&result, "/lib64/ld64.so.2"), - .s390x => return copy(&result, "/lib64/ld64.so.1"), - .sparc64 => return copy(&result, "/lib64/ld-linux.so.2"), - .x86_64 => return copy(&result, switch (self.abi) { - .gnux32 => "/libx32/ld-linux-x32.so.2", - else => "/lib64/ld-linux-x86-64.so.2", - }), - - .riscv32 => return copy(&result, "/lib/ld-linux-riscv32-ilp32.so.1"), - .riscv64 => return copy(&result, "/lib/ld-linux-riscv64-lp64.so.1"), - - // Architectures in this list have been verified as not having a standard - // dynamic linker path. - .wasm32, - .wasm64, - .bpfel, - .bpfeb, - .nvptx, - .nvptx64, - .spu_2, - .avr, - .spirv32, - .spirv64, - => return result, - - // TODO go over each item in this list and either move it to the above list, or - // implement the standard dynamic linker path code for it. - .arc, - .csky, - .hexagon, - .m68k, - .msp430, - .r600, - .amdgcn, - .tce, - .tcele, - .xcore, - .le32, - .le64, - .amdil, - .amdil64, - .hsail, - .hsail64, - .spir, - .spir64, - .kalimba, - .shave, - .lanai, - .renderscript32, - .renderscript64, - .ve, - .dxil, - .loongarch32, - .loongarch64, - .xtensa, - => return result, - }, - - .ios, - .tvos, - .watchos, - .macos, - => return copy(&result, "/usr/lib/dyld"), - - // Operating systems in this list have been verified as not having a standard - // dynamic linker path. - .freestanding, - .uefi, - .windows, - .emscripten, - .wasi, - .opencl, - .glsl450, - .vulkan, - .other, - .plan9, - => return result, - - // TODO revisit when multi-arch for Haiku is available - .haiku => return copy(&result, "/system/runtime_loader"), - - // TODO go over each item in this list and either move it to the above list, or - // implement the standard dynamic linker path code for it. - .ananas, - .cloudabi, - .fuchsia, - .kfreebsd, - .lv2, - .zos, - .minix, - .rtems, - .nacl, - .aix, - .cuda, - .nvcl, - .amdhsa, - .ps4, - .ps5, - .elfiamcu, - .mesa3d, - .contiki, - .amdpal, - .hermit, - .hurd, - .driverkit, - .shadermodel, - .liteos, - => return result, - } - } - - /// 0c spim little-endian MIPS 3000 family - /// 1c 68000 Motorola MC68000 - /// 2c 68020 Motorola MC68020 - /// 5c arm little-endian ARM - /// 6c amd64 AMD64 and compatibles (e.g., Intel EM64T) - /// 7c arm64 ARM64 (ARMv8) - /// 8c 386 Intel x86, i486, Pentium, etc. - /// kc sparc Sun SPARC - /// qc power Power PC - /// vc mips big-endian MIPS 3000 family - pub fn plan9Ext(cpu_arch: Cpu.Arch) [:0]const u8 { - return switch (cpu_arch) { - .arm => ".5", - .x86_64 => ".6", - .aarch64 => ".7", - .x86 => ".8", - .sparc => ".k", - .powerpc, .powerpcle => ".q", - .mips, .mipsel => ".v", - // ISAs without designated characters get 'X' for lack of a better option. - else => ".X", - }; - } - - pub fn maxIntAlignment(target: Target) u16 { - return switch (target.cpu.arch) { - .avr => 1, - .msp430 => 2, - .xcore => 4, - - .arm, - .armeb, - .thumb, - .thumbeb, - .hexagon, - .mips, - .mipsel, - .powerpc, - .powerpcle, - .r600, - .amdgcn, - .riscv32, - .sparc, - .sparcel, - .s390x, - .lanai, - .wasm32, - .wasm64, - => 8, - - .x86 => if (target.ofmt == .c) 16 else return switch (target.os.tag) { - .windows, .uefi => 8, - else => 4, - }, - - // For these, LLVMABIAlignmentOfType(i128) reports 8. Note that 16 - // is a relevant number in three cases: - // 1. Different machine code instruction when loading into SIMD register. - // 2. The C ABI wants 16 for extern structs. - // 3. 16-byte cmpxchg needs 16-byte alignment. - // Same logic for powerpc64, mips64, sparc64. - .x86_64, - .powerpc64, - .powerpc64le, - .mips64, - .mips64el, - .sparc64, - => return switch (target.ofmt) { - .c => 16, - else => 8, - }, - - // Even LLVMABIAlignmentOfType(i128) agrees on these targets. - .aarch64, - .aarch64_be, - .aarch64_32, - .riscv64, - .bpfel, - .bpfeb, - .nvptx, - .nvptx64, - => 16, - - // Below this comment are unverified but based on the fact that C requires - // int128_t to be 16 bytes aligned, it's a safe default. - .spu_2, - .csky, - .arc, - .m68k, - .tce, - .tcele, - .le32, - .amdil, - .hsail, - .spir, - .kalimba, - .renderscript32, - .spirv32, - .shave, - .le64, - .amdil64, - .hsail64, - .spir64, - .renderscript64, - .ve, - .spirv64, - .dxil, - .loongarch32, - .loongarch64, - .xtensa, - => 16, - }; - } - - pub fn ptrBitWidth(target: Target) u16 { - switch (target.abi) { - .gnux32, .muslx32, .gnuabin32, .gnuilp32 => return 32, - .gnuabi64 => return 64, - else => {}, - } - switch (target.cpu.arch) { - .avr, - .msp430, - .spu_2, - => return 16, - - .arc, - .arm, - .armeb, - .csky, - .hexagon, - .m68k, - .le32, - .mips, - .mipsel, - .powerpc, - .powerpcle, - .r600, - .riscv32, - .sparcel, - .tce, - .tcele, - .thumb, - .thumbeb, - .x86, - .xcore, - .nvptx, - .amdil, - .hsail, - .spir, - .kalimba, - .shave, - .lanai, - .wasm32, - .renderscript32, - .aarch64_32, - .spirv32, - .loongarch32, - .dxil, - .xtensa, - => return 32, - - .aarch64, - .aarch64_be, - .mips64, - .mips64el, - .powerpc64, - .powerpc64le, - .riscv64, - .x86_64, - .nvptx64, - .le64, - .amdil64, - .hsail64, - .spir64, - .wasm64, - .renderscript64, - .amdgcn, - .bpfel, - .bpfeb, - .sparc64, - .s390x, - .ve, - .spirv64, - .loongarch64, - => return 64, - - .sparc => return if (std.Target.sparc.featureSetHas(target.cpu.features, .v9)) 64 else 32, - } - } - - pub fn stackAlignment(target: Target) u16 { - return switch (target.cpu.arch) { - .m68k => 2, - .amdgcn => 4, - .x86 => switch (target.os.tag) { - .windows, .uefi => 4, - else => 16, - }, - .arm, - .armeb, - .thumb, - .thumbeb, - .mips, - .mipsel, - .sparc, - .sparcel, - => 8, - .aarch64, - .aarch64_be, - .aarch64_32, - .bpfeb, - .bpfel, - .mips64, - .mips64el, - .riscv32, - .riscv64, - .sparc64, - .x86_64, - .ve, - .wasm32, - .wasm64, - => 16, - .powerpc64, - .powerpc64le, - => switch (target.os.tag) { - else => 8, - .linux => 16, - }, - else => @divExact(target.ptrBitWidth(), 8), - }; - } - - /// Default signedness of `char` for the native C compiler for this target - /// Note that char signedness is implementation-defined and many compilers provide - /// an option to override the default signedness e.g. GCC's -funsigned-char / -fsigned-char - pub fn charSignedness(target: Target) std.builtin.Signedness { - switch (target.cpu.arch) { - .aarch64, - .aarch64_32, - .aarch64_be, - .arm, - .armeb, - .thumb, - .thumbeb, - => return if (target.os.tag.isDarwin() or target.os.tag == .windows) .signed else .unsigned, - .powerpc, .powerpc64 => return if (target.os.tag.isDarwin()) .signed else .unsigned, - .powerpcle, - .powerpc64le, - .s390x, - .xcore, - .arc, - .msp430, - .riscv32, - .riscv64, - => return .unsigned, - else => return .signed, - } - } - - pub const CType = enum { - char, - short, - ushort, - int, - uint, - long, - ulong, - longlong, - ulonglong, - float, - double, - longdouble, - }; - - pub fn c_type_byte_size(t: Target, c_type: CType) u16 { - return switch (c_type) { - .char, - .short, - .ushort, - .int, - .uint, - .long, - .ulong, - .longlong, - .ulonglong, - .float, - .double, - => @divExact(c_type_bit_size(t, c_type), 8), - - .longdouble => switch (c_type_bit_size(t, c_type)) { - 16 => 2, - 32 => 4, - 64 => 8, - 80 => @as(u16, @intCast(mem.alignForward(usize, 10, c_type_alignment(t, .longdouble)))), - 128 => 16, - else => unreachable, - }, - }; - } - - pub fn c_type_bit_size(target: Target, c_type: CType) u16 { - switch (target.os.tag) { - .freestanding, .other => switch (target.cpu.arch) { - .msp430 => switch (c_type) { - .char => return 8, - .short, .ushort, .int, .uint => return 16, - .float, .long, .ulong => return 32, - .longlong, .ulonglong, .double, .longdouble => return 64, - }, - .avr => switch (c_type) { - .char => return 8, - .short, .ushort, .int, .uint => return 16, - .long, .ulong, .float, .double, .longdouble => return 32, - .longlong, .ulonglong => return 64, - }, - .tce, .tcele => switch (c_type) { - .char => return 8, - .short, .ushort => return 16, - .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32, - .float, .double, .longdouble => return 32, - }, - .mips64, .mips64el => switch (c_type) { - .char => return 8, - .short, .ushort => return 16, - .int, .uint, .float => return 32, - .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32, - .longlong, .ulonglong, .double => return 64, - .longdouble => return 128, - }, - .x86_64 => switch (c_type) { - .char => return 8, - .short, .ushort => return 16, - .int, .uint, .float => return 32, - .long, .ulong => switch (target.abi) { - .gnux32, .muslx32 => return 32, - else => return 64, - }, - .longlong, .ulonglong, .double => return 64, - .longdouble => return 80, - }, - else => switch (c_type) { - .char => return 8, - .short, .ushort => return 16, - .int, .uint, .float => return 32, - .long, .ulong => return target.ptrBitWidth(), - .longlong, .ulonglong, .double => return 64, - .longdouble => switch (target.cpu.arch) { - .x86 => switch (target.abi) { - .android => return 64, - else => return 80, - }, - - .powerpc, - .powerpcle, - .powerpc64, - .powerpc64le, - => switch (target.abi) { - .musl, - .musleabi, - .musleabihf, - .muslx32, - => return 64, - else => return 128, - }, - - .riscv32, - .riscv64, - .aarch64, - .aarch64_be, - .aarch64_32, - .s390x, - .sparc, - .sparc64, - .sparcel, - .wasm32, - .wasm64, - => return 128, - - else => return 64, - }, - }, - }, - - .linux, - .freebsd, - .netbsd, - .dragonfly, - .openbsd, - .wasi, - .emscripten, - .plan9, - .solaris, - .illumos, - .haiku, - .ananas, - .fuchsia, - .minix, - => switch (target.cpu.arch) { - .msp430 => switch (c_type) { - .char => return 8, - .short, .ushort, .int, .uint => return 16, - .long, .ulong, .float => return 32, - .longlong, .ulonglong, .double, .longdouble => return 64, - }, - .avr => switch (c_type) { - .char => return 8, - .short, .ushort, .int, .uint => return 16, - .long, .ulong, .float, .double, .longdouble => return 32, - .longlong, .ulonglong => return 64, - }, - .tce, .tcele => switch (c_type) { - .char => return 8, - .short, .ushort => return 16, - .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32, - .float, .double, .longdouble => return 32, - }, - .mips64, .mips64el => switch (c_type) { - .char => return 8, - .short, .ushort => return 16, - .int, .uint, .float => return 32, - .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32, - .longlong, .ulonglong, .double => return 64, - .longdouble => if (target.os.tag == .freebsd) return 64 else return 128, - }, - .x86_64 => switch (c_type) { - .char => return 8, - .short, .ushort => return 16, - .int, .uint, .float => return 32, - .long, .ulong => switch (target.abi) { - .gnux32, .muslx32 => return 32, - else => return 64, - }, - .longlong, .ulonglong, .double => return 64, - .longdouble => return 80, - }, - else => switch (c_type) { - .char => return 8, - .short, .ushort => return 16, - .int, .uint, .float => return 32, - .long, .ulong => return target.ptrBitWidth(), - .longlong, .ulonglong, .double => return 64, - .longdouble => switch (target.cpu.arch) { - .x86 => switch (target.abi) { - .android => return 64, - else => return 80, - }, - - .powerpc, - .powerpcle, - => switch (target.abi) { - .musl, - .musleabi, - .musleabihf, - .muslx32, - => return 64, - else => switch (target.os.tag) { - .freebsd, .netbsd, .openbsd => return 64, - else => return 128, - }, - }, - - .powerpc64, - .powerpc64le, - => switch (target.abi) { - .musl, - .musleabi, - .musleabihf, - .muslx32, - => return 64, - else => switch (target.os.tag) { - .freebsd, .openbsd => return 64, - else => return 128, - }, - }, - - .riscv32, - .riscv64, - .aarch64, - .aarch64_be, - .aarch64_32, - .s390x, - .mips64, - .mips64el, - .sparc, - .sparc64, - .sparcel, - .wasm32, - .wasm64, - => return 128, - - else => return 64, - }, - }, - }, - - .windows, .uefi => switch (target.cpu.arch) { - .x86 => switch (c_type) { - .char => return 8, - .short, .ushort => return 16, - .int, .uint, .float => return 32, - .long, .ulong => return 32, - .longlong, .ulonglong, .double => return 64, - .longdouble => switch (target.abi) { - .gnu, .gnuilp32, .cygnus => return 80, - else => return 64, - }, - }, - .x86_64 => switch (c_type) { - .char => return 8, - .short, .ushort => return 16, - .int, .uint, .float => return 32, - .long, .ulong => switch (target.abi) { - .cygnus => return 64, - else => return 32, - }, - .longlong, .ulonglong, .double => return 64, - .longdouble => switch (target.abi) { - .gnu, .gnuilp32, .cygnus => return 80, - else => return 64, - }, - }, - else => switch (c_type) { - .char => return 8, - .short, .ushort => return 16, - .int, .uint, .float => return 32, - .long, .ulong => return 32, - .longlong, .ulonglong, .double => return 64, - .longdouble => return 64, - }, - }, - - .macos, .ios, .tvos, .watchos => switch (c_type) { - .char => return 8, - .short, .ushort => return 16, - .int, .uint, .float => return 32, - .long, .ulong => switch (target.cpu.arch) { - .x86, .arm, .aarch64_32 => return 32, - .x86_64 => switch (target.abi) { - .gnux32, .muslx32 => return 32, - else => return 64, - }, - else => return 64, - }, - .longlong, .ulonglong, .double => return 64, - .longdouble => switch (target.cpu.arch) { - .x86 => switch (target.abi) { - .android => return 64, - else => return 80, - }, - .x86_64 => return 80, - else => return 64, - }, - }, - - .nvcl, .cuda => switch (c_type) { - .char => return 8, - .short, .ushort => return 16, - .int, .uint, .float => return 32, - .long, .ulong => switch (target.cpu.arch) { - .nvptx => return 32, - .nvptx64 => return 64, - else => return 64, - }, - .longlong, .ulonglong, .double => return 64, - .longdouble => return 64, - }, - - .amdhsa, .amdpal => switch (c_type) { - .char => return 8, - .short, .ushort => return 16, - .int, .uint, .float => return 32, - .long, .ulong, .longlong, .ulonglong, .double => return 64, - .longdouble => return 128, - }, - - .opencl => switch (c_type) { - .char => return 8, - .short, .ushort => return 16, - .int, .uint, .float => return 32, - .long, .ulong, .double => return 64, - .longlong, .ulonglong => return 128, - // Note: The OpenCL specification does not guarantee a particular size for long double, - // but clang uses 128 bits. - .longdouble => return 128, - }, - - .ps4, .ps5 => switch (c_type) { - .char => return 8, - .short, .ushort => return 16, - .int, .uint, .float => return 32, - .long, .ulong => return 64, - .longlong, .ulonglong, .double => return 64, - .longdouble => return 80, - }, - - .cloudabi, - .kfreebsd, - .lv2, - .zos, - .rtems, - .nacl, - .aix, - .elfiamcu, - .mesa3d, - .contiki, - .hermit, - .hurd, - .glsl450, - .vulkan, - .driverkit, - .shadermodel, - .liteos, - => @panic("TODO specify the C integer and float type sizes for this OS"), - } - } - - pub fn c_type_alignment(target: Target, c_type: CType) u16 { - // Overrides for unusual alignments - switch (target.cpu.arch) { - .avr => return 1, - .x86 => switch (target.os.tag) { - .windows, .uefi => switch (c_type) { - .longlong, .ulonglong, .double => return 8, - .longdouble => switch (target.abi) { - .gnu, .gnuilp32, .cygnus => return 4, - else => return 8, - }, - else => {}, - }, - else => {}, - }, - else => {}, - } - - // Next-power-of-two-aligned, up to a maximum. - return @min( - std.math.ceilPowerOfTwoAssert(u16, (c_type_bit_size(target, c_type) + 7) / 8), - switch (target.cpu.arch) { - .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) { - .netbsd => switch (target.abi) { - .gnueabi, - .gnueabihf, - .eabi, - .eabihf, - .android, - .musleabi, - .musleabihf, - => 8, - - else => @as(u16, 4), - }, - .ios, .tvos, .watchos => 4, - else => 8, - }, - - .msp430, - .avr, - => 2, - - .arc, - .csky, - .x86, - .xcore, - .dxil, - .loongarch32, - .tce, - .tcele, - .le32, - .amdil, - .hsail, - .spir, - .spirv32, - .kalimba, - .shave, - .renderscript32, - .ve, - .spu_2, - .xtensa, - => 4, - - .aarch64_32, - .amdgcn, - .amdil64, - .bpfel, - .bpfeb, - .hexagon, - .hsail64, - .loongarch64, - .m68k, - .mips, - .mipsel, - .sparc, - .sparcel, - .sparc64, - .lanai, - .le64, - .nvptx, - .nvptx64, - .r600, - .s390x, - .spir64, - .spirv64, - .renderscript64, - => 8, - - .aarch64, - .aarch64_be, - .mips64, - .mips64el, - .powerpc, - .powerpcle, - .powerpc64, - .powerpc64le, - .riscv32, - .riscv64, - .x86_64, - .wasm32, - .wasm64, - => 16, - }, - ); - } - - pub fn c_type_preferred_alignment(target: Target, c_type: CType) u16 { - // Overrides for unusual alignments - switch (target.cpu.arch) { - .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) { - .netbsd => switch (target.abi) { - .gnueabi, - .gnueabihf, - .eabi, - .eabihf, - .android, - .musleabi, - .musleabihf, - => {}, - - else => switch (c_type) { - .longdouble => return 4, - else => {}, - }, - }, - .ios, .tvos, .watchos => switch (c_type) { - .longdouble => return 4, - else => {}, - }, - else => {}, - }, - .arc => switch (c_type) { - .longdouble => return 4, - else => {}, - }, - .avr => switch (c_type) { - .char, .int, .uint, .long, .ulong, .float, .longdouble => return 1, - .short, .ushort => return 2, - .double => return 4, - .longlong, .ulonglong => return 8, - }, - .x86 => switch (target.os.tag) { - .windows, .uefi => switch (c_type) { - .longdouble => switch (target.abi) { - .gnu, .gnuilp32, .cygnus => return 4, - else => return 8, - }, - else => {}, - }, - else => switch (c_type) { - .longdouble => return 4, - else => {}, - }, - }, - else => {}, - } - - // Next-power-of-two-aligned, up to a maximum. - return @min( - std.math.ceilPowerOfTwoAssert(u16, (c_type_bit_size(target, c_type) + 7) / 8), - switch (target.cpu.arch) { - .msp430 => @as(u16, 2), - - .csky, - .xcore, - .dxil, - .loongarch32, - .tce, - .tcele, - .le32, - .amdil, - .hsail, - .spir, - .spirv32, - .kalimba, - .shave, - .renderscript32, - .ve, - .spu_2, - .xtensa, - => 4, - - .arc, - .arm, - .armeb, - .avr, - .thumb, - .thumbeb, - .aarch64_32, - .amdgcn, - .amdil64, - .bpfel, - .bpfeb, - .hexagon, - .hsail64, - .x86, - .loongarch64, - .m68k, - .mips, - .mipsel, - .sparc, - .sparcel, - .sparc64, - .lanai, - .le64, - .nvptx, - .nvptx64, - .r600, - .s390x, - .spir64, - .spirv64, - .renderscript64, - => 8, - - .aarch64, - .aarch64_be, - .mips64, - .mips64el, - .powerpc, - .powerpcle, - .powerpc64, - .powerpc64le, - .riscv32, - .riscv64, - .x86_64, - .wasm32, - .wasm64, - => 16, - }, - ); - } -}; - -test { - std.testing.refAllDecls(Target.Cpu.Arch); -} diff --git a/lib/std/zig.zig b/lib/std/zig.zig index 2d2aaf17ba40..84feb2cf0a4b 100644 --- a/lib/std/zig.zig +++ b/lib/std/zig.zig @@ -1,7 +1,4 @@ -const std = @import("std.zig"); -const tokenizer = @import("zig/tokenizer.zig"); pub const fmt = @import("zig/fmt.zig"); -const assert = std.debug.assert; pub const ErrorBundle = @import("zig/ErrorBundle.zig"); pub const Server = @import("zig/Server.zig"); @@ -16,7 +13,8 @@ pub const number_literal = @import("zig/number_literal.zig"); pub const primitives = @import("zig/primitives.zig"); pub const Ast = @import("zig/Ast.zig"); pub const system = @import("zig/system.zig"); -pub const CrossTarget = @import("zig/CrossTarget.zig"); +/// Deprecated: use `std.Target.Query`. +pub const CrossTarget = std.Target.Query; pub const BuiltinFn = @import("zig/BuiltinFn.zig"); pub const AstRlAnnotate = @import("zig/AstRlAnnotate.zig"); @@ -114,7 +112,7 @@ pub const BinNameOptions = struct { }; /// Returns the standard file system basename of a binary generated by the Zig compiler. -pub fn binNameAlloc(allocator: std.mem.Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 { +pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 { const root_name = options.root_name; const target = options.target; switch (target.ofmt) { @@ -203,6 +201,124 @@ pub fn binNameAlloc(allocator: std.mem.Allocator, options: BinNameOptions) error } } +pub const BuildId = union(enum) { + none, + fast, + uuid, + sha1, + md5, + hexstring: HexString, + + pub fn eql(a: BuildId, b: BuildId) bool { + const Tag = @typeInfo(BuildId).Union.tag_type.?; + const a_tag: Tag = a; + const b_tag: Tag = b; + if (a_tag != b_tag) return false; + return switch (a) { + .none, .fast, .uuid, .sha1, .md5 => true, + .hexstring => |a_hexstring| std.mem.eql(u8, a_hexstring.toSlice(), b.hexstring.toSlice()), + }; + } + + pub const HexString = struct { + bytes: [32]u8, + len: u8, + + /// Result is byte values, *not* hex-encoded. + pub fn toSlice(hs: *const HexString) []const u8 { + return hs.bytes[0..hs.len]; + } + }; + + /// Input is byte values, *not* hex-encoded. + /// Asserts `bytes` fits inside `HexString` + pub fn initHexString(bytes: []const u8) BuildId { + var result: BuildId = .{ .hexstring = .{ + .bytes = undefined, + .len = @intCast(bytes.len), + } }; + @memcpy(result.hexstring.bytes[0..bytes.len], bytes); + return result; + } + + /// Converts UTF-8 text to a `BuildId`. + pub fn parse(text: []const u8) !BuildId { + if (std.mem.eql(u8, text, "none")) { + return .none; + } else if (std.mem.eql(u8, text, "fast")) { + return .fast; + } else if (std.mem.eql(u8, text, "uuid")) { + return .uuid; + } else if (std.mem.eql(u8, text, "sha1") or std.mem.eql(u8, text, "tree")) { + return .sha1; + } else if (std.mem.eql(u8, text, "md5")) { + return .md5; + } else if (std.mem.startsWith(u8, text, "0x")) { + var result: BuildId = .{ .hexstring = undefined }; + const slice = try std.fmt.hexToBytes(&result.hexstring.bytes, text[2..]); + result.hexstring.len = @as(u8, @intCast(slice.len)); + return result; + } + return error.InvalidBuildIdStyle; + } + + test parse { + try std.testing.expectEqual(BuildId.md5, try parse("md5")); + try std.testing.expectEqual(BuildId.none, try parse("none")); + try std.testing.expectEqual(BuildId.fast, try parse("fast")); + try std.testing.expectEqual(BuildId.uuid, try parse("uuid")); + try std.testing.expectEqual(BuildId.sha1, try parse("sha1")); + try std.testing.expectEqual(BuildId.sha1, try parse("tree")); + + try std.testing.expect(BuildId.initHexString("").eql(try parse("0x"))); + try std.testing.expect(BuildId.initHexString("\x12\x34\x56").eql(try parse("0x123456"))); + try std.testing.expectError(error.InvalidLength, parse("0x12-34")); + try std.testing.expectError(error.InvalidCharacter, parse("0xfoobbb")); + try std.testing.expectError(error.InvalidBuildIdStyle, parse("yaddaxxx")); + } +}; + +/// Renders a `std.Target.Cpu` value into a textual representation that can be parsed +/// via the `-mcpu` flag passed to the Zig compiler. +/// Appends the result to `buffer`. +pub fn serializeCpu(buffer: *std.ArrayList(u8), cpu: std.Target.Cpu) Allocator.Error!void { + const all_features = cpu.arch.allFeaturesList(); + var populated_cpu_features = cpu.model.features; + populated_cpu_features.populateDependencies(all_features); + + try buffer.appendSlice(cpu.model.name); + + if (populated_cpu_features.eql(cpu.features)) { + // The CPU name alone is sufficient. + return; + } + + for (all_features, 0..) |feature, i_usize| { + const i: std.Target.Cpu.Feature.Set.Index = @intCast(i_usize); + const in_cpu_set = populated_cpu_features.isEnabled(i); + const in_actual_set = cpu.features.isEnabled(i); + try buffer.ensureUnusedCapacity(feature.name.len + 1); + if (in_cpu_set and !in_actual_set) { + buffer.appendAssumeCapacity('-'); + buffer.appendSliceAssumeCapacity(feature.name); + } else if (!in_cpu_set and in_actual_set) { + buffer.appendAssumeCapacity('+'); + buffer.appendSliceAssumeCapacity(feature.name); + } + } +} + +pub fn serializeCpuAlloc(ally: Allocator, cpu: std.Target.Cpu) Allocator.Error![]u8 { + var buffer = std.ArrayList(u8).init(ally); + try serializeCpu(&buffer, cpu); + return buffer.toOwnedSlice(); +} + +const std = @import("std.zig"); +const tokenizer = @import("zig/tokenizer.zig"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; + test { @import("std").testing.refAllDecls(@This()); } diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index 3ec92c8274e0..c2a9fa4f9fe3 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -1,13 +1,1144 @@ pub const NativePaths = @import("system/NativePaths.zig"); -pub const NativeTargetInfo = @import("system/NativeTargetInfo.zig"); pub const windows = @import("system/windows.zig"); pub const darwin = @import("system/darwin.zig"); pub const linux = @import("system/linux.zig"); +pub const Executor = union(enum) { + native, + rosetta, + qemu: []const u8, + wine: []const u8, + wasmtime: []const u8, + darling: []const u8, + bad_dl: []const u8, + bad_os_or_cpu, +}; + +pub const GetExternalExecutorOptions = struct { + allow_darling: bool = true, + allow_qemu: bool = true, + allow_rosetta: bool = true, + allow_wasmtime: bool = true, + allow_wine: bool = true, + qemu_fixes_dl: bool = false, + link_libc: bool = false, +}; + +/// Return whether or not the given host is capable of running executables of +/// the other target. +pub fn getExternalExecutor( + host: std.Target, + candidate: *const std.Target, + options: GetExternalExecutorOptions, +) Executor { + const os_match = host.os.tag == candidate.os.tag; + const cpu_ok = cpu_ok: { + if (host.cpu.arch == candidate.cpu.arch) + break :cpu_ok true; + + if (host.cpu.arch == .x86_64 and candidate.cpu.arch == .x86) + break :cpu_ok true; + + if (host.cpu.arch == .aarch64 and candidate.cpu.arch == .arm) + break :cpu_ok true; + + if (host.cpu.arch == .aarch64_be and candidate.cpu.arch == .armeb) + break :cpu_ok true; + + // TODO additionally detect incompatible CPU features. + // Note that in some cases the OS kernel will emulate missing CPU features + // when an illegal instruction is encountered. + + break :cpu_ok false; + }; + + var bad_result: Executor = .bad_os_or_cpu; + + if (os_match and cpu_ok) native: { + if (options.link_libc) { + if (candidate.dynamic_linker.get()) |candidate_dl| { + fs.cwd().access(candidate_dl, .{}) catch { + bad_result = .{ .bad_dl = candidate_dl }; + break :native; + }; + } + } + return .native; + } + + // If the OS match and OS is macOS and CPU is arm64, we can use Rosetta 2 + // to emulate the foreign architecture. + if (options.allow_rosetta and os_match and + host.os.tag == .macos and host.cpu.arch == .aarch64) + { + switch (candidate.cpu.arch) { + .x86_64 => return .rosetta, + else => return bad_result, + } + } + + // If the OS matches, we can use QEMU to emulate a foreign architecture. + if (options.allow_qemu and os_match and (!cpu_ok or options.qemu_fixes_dl)) { + return switch (candidate.cpu.arch) { + .aarch64 => Executor{ .qemu = "qemu-aarch64" }, + .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" }, + .arm => Executor{ .qemu = "qemu-arm" }, + .armeb => Executor{ .qemu = "qemu-armeb" }, + .hexagon => Executor{ .qemu = "qemu-hexagon" }, + .x86 => Executor{ .qemu = "qemu-i386" }, + .m68k => Executor{ .qemu = "qemu-m68k" }, + .mips => Executor{ .qemu = "qemu-mips" }, + .mipsel => Executor{ .qemu = "qemu-mipsel" }, + .mips64 => Executor{ .qemu = "qemu-mips64" }, + .mips64el => Executor{ .qemu = "qemu-mips64el" }, + .powerpc => Executor{ .qemu = "qemu-ppc" }, + .powerpc64 => Executor{ .qemu = "qemu-ppc64" }, + .powerpc64le => Executor{ .qemu = "qemu-ppc64le" }, + .riscv32 => Executor{ .qemu = "qemu-riscv32" }, + .riscv64 => Executor{ .qemu = "qemu-riscv64" }, + .s390x => Executor{ .qemu = "qemu-s390x" }, + .sparc => Executor{ .qemu = "qemu-sparc" }, + .sparc64 => Executor{ .qemu = "qemu-sparc64" }, + .x86_64 => Executor{ .qemu = "qemu-x86_64" }, + else => return bad_result, + }; + } + + switch (candidate.os.tag) { + .windows => { + if (options.allow_wine) { + // x86_64 wine does not support emulating aarch64-windows and + // vice versa. + if (candidate.cpu.arch != builtin.cpu.arch) { + return bad_result; + } + switch (candidate.ptrBitWidth()) { + 32 => return Executor{ .wine = "wine" }, + 64 => return Executor{ .wine = "wine64" }, + else => return bad_result, + } + } + return bad_result; + }, + .wasi => { + if (options.allow_wasmtime) { + switch (candidate.ptrBitWidth()) { + 32 => return Executor{ .wasmtime = "wasmtime" }, + else => return bad_result, + } + } + return bad_result; + }, + .macos => { + if (options.allow_darling) { + // This check can be loosened once darling adds a QEMU-based emulation + // layer for non-host architectures: + // https://github.com/darlinghq/darling/issues/863 + if (candidate.cpu.arch != builtin.cpu.arch) { + return bad_result; + } + return Executor{ .darling = "darling" }; + } + return bad_result; + }, + else => return bad_result, + } +} + +pub const DetectError = error{ + FileSystem, + SystemResources, + SymLinkLoop, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + DeviceBusy, + OSVersionDetectionFail, + Unexpected, +}; + +/// Given a `Target.Query`, which specifies in detail which parts of the +/// target should be detected natively, which should be standard or default, +/// and which are provided explicitly, this function resolves the native +/// components by detecting the native system, and then resolves +/// standard/default parts relative to that. +pub fn resolveTargetQuery(query: Target.Query) DetectError!Target { + const query_os_tag = query.os_tag orelse builtin.os.tag; + var os = query_os_tag.defaultVersionRange(query.cpu_arch orelse builtin.cpu.arch); + if (query.os_tag == null) { + switch (builtin.target.os.tag) { + .linux => { + const uts = std.os.uname(); + const release = mem.sliceTo(&uts.release, 0); + // The release field sometimes has a weird format, + // `Version.parse` will attempt to find some meaningful interpretation. + if (std.SemanticVersion.parse(release)) |ver| { + os.version_range.linux.range.min = ver; + os.version_range.linux.range.max = ver; + } else |err| switch (err) { + error.Overflow => {}, + error.InvalidVersion => {}, + } + }, + .solaris, .illumos => { + const uts = std.os.uname(); + const release = mem.sliceTo(&uts.release, 0); + if (std.SemanticVersion.parse(release)) |ver| { + os.version_range.semver.min = ver; + os.version_range.semver.max = ver; + } else |err| switch (err) { + error.Overflow => {}, + error.InvalidVersion => {}, + } + }, + .windows => { + const detected_version = windows.detectRuntimeVersion(); + os.version_range.windows.min = detected_version; + os.version_range.windows.max = detected_version; + }, + .macos => try darwin.macos.detect(&os), + .freebsd, .netbsd, .dragonfly => { + const key = switch (builtin.target.os.tag) { + .freebsd => "kern.osreldate", + .netbsd, .dragonfly => "kern.osrevision", + else => unreachable, + }; + var value: u32 = undefined; + var len: usize = @sizeOf(@TypeOf(value)); + + std.os.sysctlbynameZ(key, &value, &len, null, 0) catch |err| switch (err) { + error.NameTooLong => unreachable, // constant, known good value + error.PermissionDenied => unreachable, // only when setting values, + error.SystemResources => unreachable, // memory already on the stack + error.UnknownName => unreachable, // constant, known good value + error.Unexpected => return error.OSVersionDetectionFail, + }; + + switch (builtin.target.os.tag) { + .freebsd => { + // https://www.freebsd.org/doc/en_US.ISO8859-1/books/porters-handbook/versions.html + // Major * 100,000 has been convention since FreeBSD 2.2 (1997) + // Minor * 1(0),000 summed has been convention since FreeBSD 2.2 (1997) + // e.g. 492101 = 4.11-STABLE = 4.(9+2) + const major = value / 100_000; + const minor1 = value % 100_000 / 10_000; // usually 0 since 5.1 + const minor2 = value % 10_000 / 1_000; // 0 before 5.1, minor version since + const patch = value % 1_000; + os.version_range.semver.min = .{ .major = major, .minor = minor1 + minor2, .patch = patch }; + os.version_range.semver.max = os.version_range.semver.min; + }, + .netbsd => { + // #define __NetBSD_Version__ MMmmrrpp00 + // + // M = major version + // m = minor version; a minor number of 99 indicates current. + // r = 0 (*) + // p = patchlevel + const major = value / 100_000_000; + const minor = value % 100_000_000 / 1_000_000; + const patch = value % 10_000 / 100; + os.version_range.semver.min = .{ .major = major, .minor = minor, .patch = patch }; + os.version_range.semver.max = os.version_range.semver.min; + }, + .dragonfly => { + // https://github.com/DragonFlyBSD/DragonFlyBSD/blob/cb2cde83771754aeef9bb3251ee48959138dec87/Makefile.inc1#L15-L17 + // flat base10 format: Mmmmpp + // M = major + // m = minor; odd-numbers indicate current dev branch + // p = patch + const major = value / 100_000; + const minor = value % 100_000 / 100; + const patch = value % 100; + os.version_range.semver.min = .{ .major = major, .minor = minor, .patch = patch }; + os.version_range.semver.max = os.version_range.semver.min; + }, + else => unreachable, + } + }, + .openbsd => { + const mib: [2]c_int = [_]c_int{ + std.os.CTL.KERN, + std.os.KERN.OSRELEASE, + }; + var buf: [64]u8 = undefined; + // consider that sysctl result includes null-termination + // reserve 1 byte to ensure we never overflow when appending ".0" + var len: usize = buf.len - 1; + + std.os.sysctl(&mib, &buf, &len, null, 0) catch |err| switch (err) { + error.NameTooLong => unreachable, // constant, known good value + error.PermissionDenied => unreachable, // only when setting values, + error.SystemResources => unreachable, // memory already on the stack + error.UnknownName => unreachable, // constant, known good value + error.Unexpected => return error.OSVersionDetectionFail, + }; + + // append ".0" to satisfy semver + buf[len - 1] = '.'; + buf[len] = '0'; + len += 1; + + if (std.SemanticVersion.parse(buf[0..len])) |ver| { + os.version_range.semver.min = ver; + os.version_range.semver.max = ver; + } else |_| { + return error.OSVersionDetectionFail; + } + }, + else => { + // Unimplemented, fall back to default version range. + }, + } + } + + if (query.os_version_min) |min| switch (min) { + .none => {}, + .semver => |semver| switch (os.tag) { + .linux => os.version_range.linux.range.min = semver, + else => os.version_range.semver.min = semver, + }, + .windows => |win_ver| os.version_range.windows.min = win_ver, + }; + + if (query.os_version_max) |max| switch (max) { + .none => {}, + .semver => |semver| switch (os.tag) { + .linux => os.version_range.linux.range.max = semver, + else => os.version_range.semver.max = semver, + }, + .windows => |win_ver| os.version_range.windows.max = win_ver, + }; + + if (query.glibc_version) |glibc| { + os.version_range.linux.glibc = glibc; + } + + // Until https://github.com/ziglang/zig/issues/4592 is implemented (support detecting the + // native CPU architecture as being different than the current target), we use this: + const cpu_arch = query.cpu_arch orelse builtin.cpu.arch; + + const cpu = switch (query.cpu_model) { + .native => detectNativeCpuAndFeatures(cpu_arch, os, query), + .baseline => Target.Cpu.baseline(cpu_arch), + .determined_by_cpu_arch => if (query.cpu_arch == null) + detectNativeCpuAndFeatures(cpu_arch, os, query) + else + Target.Cpu.baseline(cpu_arch), + .explicit => |model| model.toCpu(cpu_arch), + } orelse backup_cpu_detection: { + break :backup_cpu_detection Target.Cpu.baseline(cpu_arch); + }; + var result = try detectAbiAndDynamicLinker(cpu, os, query); + // For x86, we need to populate some CPU feature flags depending on architecture + // and mode: + // * 16bit_mode => if the abi is code16 + // * 32bit_mode => if the arch is x86 + // However, the "mode" flags can be used as overrides, so if the user explicitly + // sets one of them, that takes precedence. + switch (cpu_arch) { + .x86 => { + if (!Target.x86.featureSetHasAny(query.cpu_features_add, .{ + .@"16bit_mode", .@"32bit_mode", + })) { + switch (result.abi) { + .code16 => result.cpu.features.addFeature( + @intFromEnum(Target.x86.Feature.@"16bit_mode"), + ), + else => result.cpu.features.addFeature( + @intFromEnum(Target.x86.Feature.@"32bit_mode"), + ), + } + } + }, + .arm, .armeb => { + // XXX What do we do if the target has the noarm feature? + // What do we do if the user specifies +thumb_mode? + }, + .thumb, .thumbeb => { + result.cpu.features.addFeature( + @intFromEnum(Target.arm.Feature.thumb_mode), + ); + }, + else => {}, + } + updateCpuFeatures( + &result.cpu.features, + cpu_arch.allFeaturesList(), + query.cpu_features_add, + query.cpu_features_sub, + ); + return result; +} + +fn updateCpuFeatures( + set: *Target.Cpu.Feature.Set, + all_features_list: []const Target.Cpu.Feature, + add_set: Target.Cpu.Feature.Set, + sub_set: Target.Cpu.Feature.Set, +) void { + set.removeFeatureSet(sub_set); + set.addFeatureSet(add_set); + set.populateDependencies(all_features_list); + set.removeFeatureSet(sub_set); +} + +fn detectNativeCpuAndFeatures(cpu_arch: Target.Cpu.Arch, os: Target.Os, query: Target.Query) ?Target.Cpu { + // Here we switch on a comptime value rather than `cpu_arch`. This is valid because `cpu_arch`, + // although it is a runtime value, is guaranteed to be one of the architectures in the set + // of the respective switch prong. + switch (builtin.cpu.arch) { + .x86_64, .x86 => { + return @import("system/x86.zig").detectNativeCpuAndFeatures(cpu_arch, os, query); + }, + else => {}, + } + + switch (builtin.os.tag) { + .linux => return linux.detectNativeCpuAndFeatures(), + .macos => return darwin.macos.detectNativeCpuAndFeatures(), + .windows => return windows.detectNativeCpuAndFeatures(), + else => {}, + } + + // This architecture does not have CPU model & feature detection yet. + // See https://github.com/ziglang/zig/issues/4591 + return null; +} + +pub const AbiAndDynamicLinkerFromFileError = error{ + FileSystem, + SystemResources, + SymLinkLoop, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + UnableToReadElfFile, + InvalidElfClass, + InvalidElfVersion, + InvalidElfEndian, + InvalidElfFile, + InvalidElfMagic, + Unexpected, + UnexpectedEndOfFile, + NameTooLong, +}; + +pub fn abiAndDynamicLinkerFromFile( + file: fs.File, + cpu: Target.Cpu, + os: Target.Os, + ld_info_list: []const LdInfo, + query: Target.Query, +) AbiAndDynamicLinkerFromFileError!Target { + var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined; + _ = try preadMin(file, &hdr_buf, 0, hdr_buf.len); + const hdr32 = @as(*elf.Elf32_Ehdr, @ptrCast(&hdr_buf)); + const hdr64 = @as(*elf.Elf64_Ehdr, @ptrCast(&hdr_buf)); + if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic; + const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) { + elf.ELFDATA2LSB => .little, + elf.ELFDATA2MSB => .big, + else => return error.InvalidElfEndian, + }; + const need_bswap = elf_endian != native_endian; + if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion; + + const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) { + elf.ELFCLASS32 => false, + elf.ELFCLASS64 => true, + else => return error.InvalidElfClass, + }; + var phoff = elfInt(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff); + const phentsize = elfInt(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize); + const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum); + + var result: Target = .{ + .cpu = cpu, + .os = os, + .abi = query.abi orelse Target.Abi.default(cpu.arch, os), + .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch), + .dynamic_linker = query.dynamic_linker, + }; + var rpath_offset: ?u64 = null; // Found inside PT_DYNAMIC + const look_for_ld = query.dynamic_linker.get() == null; + + var ph_buf: [16 * @sizeOf(elf.Elf64_Phdr)]u8 align(@alignOf(elf.Elf64_Phdr)) = undefined; + if (phentsize > @sizeOf(elf.Elf64_Phdr)) return error.InvalidElfFile; + + var ph_i: u16 = 0; + while (ph_i < phnum) { + // Reserve some bytes so that we can deref the 64-bit struct fields + // even when the ELF file is 32-bits. + const ph_reserve: usize = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr); + const ph_read_byte_len = try preadMin(file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize); + var ph_buf_i: usize = 0; + while (ph_buf_i < ph_read_byte_len and ph_i < phnum) : ({ + ph_i += 1; + phoff += phentsize; + ph_buf_i += phentsize; + }) { + const ph32: *elf.Elf32_Phdr = @ptrCast(@alignCast(&ph_buf[ph_buf_i])); + const ph64: *elf.Elf64_Phdr = @ptrCast(@alignCast(&ph_buf[ph_buf_i])); + const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type); + switch (p_type) { + elf.PT_INTERP => if (look_for_ld) { + const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset); + const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz); + if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong; + const filesz = @as(usize, @intCast(p_filesz)); + _ = try preadMin(file, result.dynamic_linker.buffer[0..filesz], p_offset, filesz); + // PT_INTERP includes a null byte in filesz. + const len = filesz - 1; + // dynamic_linker.max_byte is "max", not "len". + // We know it will fit in u8 because we check against dynamic_linker.buffer.len above. + result.dynamic_linker.max_byte = @as(u8, @intCast(len - 1)); + + // Use it to determine ABI. + const full_ld_path = result.dynamic_linker.buffer[0..len]; + for (ld_info_list) |ld_info| { + const standard_ld_basename = fs.path.basename(ld_info.ld.get().?); + if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) { + result.abi = ld_info.abi; + break; + } + } + }, + // We only need this for detecting glibc version. + elf.PT_DYNAMIC => if (builtin.target.os.tag == .linux and result.isGnuLibC() and + query.glibc_version == null) + { + var dyn_off = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset); + const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz); + const dyn_size: usize = if (is_64) @sizeOf(elf.Elf64_Dyn) else @sizeOf(elf.Elf32_Dyn); + const dyn_num = p_filesz / dyn_size; + var dyn_buf: [16 * @sizeOf(elf.Elf64_Dyn)]u8 align(@alignOf(elf.Elf64_Dyn)) = undefined; + var dyn_i: usize = 0; + dyn: while (dyn_i < dyn_num) { + // Reserve some bytes so that we can deref the 64-bit struct fields + // even when the ELF file is 32-bits. + const dyn_reserve: usize = @sizeOf(elf.Elf64_Dyn) - @sizeOf(elf.Elf32_Dyn); + const dyn_read_byte_len = try preadMin( + file, + dyn_buf[0 .. dyn_buf.len - dyn_reserve], + dyn_off, + dyn_size, + ); + var dyn_buf_i: usize = 0; + while (dyn_buf_i < dyn_read_byte_len and dyn_i < dyn_num) : ({ + dyn_i += 1; + dyn_off += dyn_size; + dyn_buf_i += dyn_size; + }) { + const dyn32: *elf.Elf32_Dyn = @ptrCast(@alignCast(&dyn_buf[dyn_buf_i])); + const dyn64: *elf.Elf64_Dyn = @ptrCast(@alignCast(&dyn_buf[dyn_buf_i])); + const tag = elfInt(is_64, need_bswap, dyn32.d_tag, dyn64.d_tag); + const val = elfInt(is_64, need_bswap, dyn32.d_val, dyn64.d_val); + if (tag == elf.DT_RUNPATH) { + rpath_offset = val; + break :dyn; + } + } + } + }, + else => continue, + } + } + } + + if (builtin.target.os.tag == .linux and result.isGnuLibC() and + query.glibc_version == null) + { + const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); + + var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff); + const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize); + const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx); + + var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined; + if (sh_buf.len < shentsize) return error.InvalidElfFile; + + _ = try preadMin(file, &sh_buf, str_section_off, shentsize); + const shstr32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf)); + const shstr64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf)); + const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset); + const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size); + var strtab_buf: [4096:0]u8 = undefined; + const shstrtab_len = @min(shstrtab_size, strtab_buf.len); + const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len); + const shstrtab = strtab_buf[0..shstrtab_read_len]; + + const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum); + var sh_i: u16 = 0; + const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) { + // Reserve some bytes so that we can deref the 64-bit struct fields + // even when the ELF file is 32-bits. + const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr); + const sh_read_byte_len = try preadMin( + file, + sh_buf[0 .. sh_buf.len - sh_reserve], + shoff, + shentsize, + ); + var sh_buf_i: usize = 0; + while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({ + sh_i += 1; + shoff += shentsize; + sh_buf_i += shentsize; + }) { + const sh32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i])); + const sh64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i])); + const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name); + const sh_name = mem.sliceTo(shstrtab[sh_name_off..], 0); + if (mem.eql(u8, sh_name, ".dynstr")) { + break :find_dyn_str .{ + .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset), + .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size), + }; + } + } + } else null; + + if (dynstr) |ds| { + if (rpath_offset) |rpoff| { + if (rpoff > ds.size) return error.InvalidElfFile; + const rpoff_file = ds.offset + rpoff; + const rp_max_size = ds.size - rpoff; + + const strtab_len = @min(rp_max_size, strtab_buf.len); + const strtab_read_len = try preadMin(file, &strtab_buf, rpoff_file, strtab_len); + const strtab = strtab_buf[0..strtab_read_len]; + + const rpath_list = mem.sliceTo(strtab, 0); + var it = mem.tokenizeScalar(u8, rpath_list, ':'); + while (it.next()) |rpath| { + if (glibcVerFromRPath(rpath)) |ver| { + result.os.version_range.linux.glibc = ver; + return result; + } else |err| switch (err) { + error.GLibCNotFound => continue, + else => |e| return e, + } + } + } + } + + if (result.dynamic_linker.get()) |dl_path| glibc_ver: { + // There is no DT_RUNPATH so we try to find libc.so.6 inside the same + // directory as the dynamic linker. + if (fs.path.dirname(dl_path)) |rpath| { + if (glibcVerFromRPath(rpath)) |ver| { + result.os.version_range.linux.glibc = ver; + return result; + } else |err| switch (err) { + error.GLibCNotFound => {}, + else => |e| return e, + } + } + + // So far, no luck. Next we try to see if the information is + // present in the symlink data for the dynamic linker path. + var link_buf: [std.os.PATH_MAX]u8 = undefined; + const link_name = std.os.readlink(dl_path, &link_buf) catch |err| switch (err) { + error.NameTooLong => unreachable, + error.InvalidUtf8 => unreachable, // Windows only + error.BadPathName => unreachable, // Windows only + error.UnsupportedReparsePointType => unreachable, // Windows only + error.NetworkNotFound => unreachable, // Windows only + + error.AccessDenied, + error.FileNotFound, + error.NotLink, + error.NotDir, + => break :glibc_ver, + + error.SystemResources, + error.FileSystem, + error.SymLinkLoop, + error.Unexpected, + => |e| return e, + }; + result.os.version_range.linux.glibc = glibcVerFromLinkName( + fs.path.basename(link_name), + "ld-", + ) catch |err| switch (err) { + error.UnrecognizedGnuLibCFileName, + error.InvalidGnuLibCVersion, + => break :glibc_ver, + }; + return result; + } + + // Nothing worked so far. Finally we fall back to hard-coded search paths. + // Some distros such as Debian keep their libc.so.6 in `/lib/$triple/`. + var path_buf: [std.os.PATH_MAX]u8 = undefined; + var index: usize = 0; + const prefix = "/lib/"; + const cpu_arch = @tagName(result.cpu.arch); + const os_tag = @tagName(result.os.tag); + const abi = @tagName(result.abi); + @memcpy(path_buf[index..][0..prefix.len], prefix); + index += prefix.len; + @memcpy(path_buf[index..][0..cpu_arch.len], cpu_arch); + index += cpu_arch.len; + path_buf[index] = '-'; + index += 1; + @memcpy(path_buf[index..][0..os_tag.len], os_tag); + index += os_tag.len; + path_buf[index] = '-'; + index += 1; + @memcpy(path_buf[index..][0..abi.len], abi); + index += abi.len; + const rpath = path_buf[0..index]; + if (glibcVerFromRPath(rpath)) |ver| { + result.os.version_range.linux.glibc = ver; + return result; + } else |err| switch (err) { + error.GLibCNotFound => {}, + else => |e| return e, + } + } + + return result; +} + +fn glibcVerFromLinkName(link_name: []const u8, prefix: []const u8) error{ UnrecognizedGnuLibCFileName, InvalidGnuLibCVersion }!std.SemanticVersion { + // example: "libc-2.3.4.so" + // example: "libc-2.27.so" + // example: "ld-2.33.so" + const suffix = ".so"; + if (!mem.startsWith(u8, link_name, prefix) or !mem.endsWith(u8, link_name, suffix)) { + return error.UnrecognizedGnuLibCFileName; + } + // chop off "libc-" and ".so" + const link_name_chopped = link_name[prefix.len .. link_name.len - suffix.len]; + return Target.Query.parseVersion(link_name_chopped) catch |err| switch (err) { + error.Overflow => return error.InvalidGnuLibCVersion, + error.InvalidVersion => return error.InvalidGnuLibCVersion, + }; +} + +test glibcVerFromLinkName { + try std.testing.expectError(error.UnrecognizedGnuLibCFileName, glibcVerFromLinkName("ld-2.37.so", "this-prefix-does-not-exist")); + try std.testing.expectError(error.UnrecognizedGnuLibCFileName, glibcVerFromLinkName("libc-2.37.so-is-not-end", "libc-")); + + try std.testing.expectError(error.InvalidGnuLibCVersion, glibcVerFromLinkName("ld-2.so", "ld-")); + try std.testing.expectEqual(std.SemanticVersion{ .major = 2, .minor = 37, .patch = 0 }, try glibcVerFromLinkName("ld-2.37.so", "ld-")); + try std.testing.expectEqual(std.SemanticVersion{ .major = 2, .minor = 37, .patch = 0 }, try glibcVerFromLinkName("ld-2.37.0.so", "ld-")); + try std.testing.expectEqual(std.SemanticVersion{ .major = 2, .minor = 37, .patch = 1 }, try glibcVerFromLinkName("ld-2.37.1.so", "ld-")); + try std.testing.expectError(error.InvalidGnuLibCVersion, glibcVerFromLinkName("ld-2.37.4.5.so", "ld-")); +} + +fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion { + var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) { + error.NameTooLong => unreachable, + error.InvalidUtf8 => unreachable, + error.BadPathName => unreachable, + error.DeviceBusy => unreachable, + error.NetworkNotFound => unreachable, // Windows-only + + error.FileNotFound, + error.NotDir, + error.InvalidHandle, + error.AccessDenied, + error.NoDevice, + => return error.GLibCNotFound, + + error.ProcessFdQuotaExceeded, + error.SystemFdQuotaExceeded, + error.SystemResources, + error.SymLinkLoop, + error.Unexpected, + => |e| return e, + }; + defer dir.close(); + + // Now we have a candidate for the path to libc shared object. In + // the past, we used readlink() here because the link name would + // reveal the glibc version. However, in more recent GNU/Linux + // installations, there is no symlink. Thus we instead use a more + // robust check of opening the libc shared object and looking at the + // .dynstr section, and finding the max version number of symbols + // that start with "GLIBC_2.". + const glibc_so_basename = "libc.so.6"; + var f = dir.openFile(glibc_so_basename, .{}) catch |err| switch (err) { + error.NameTooLong => unreachable, + error.InvalidUtf8 => unreachable, // Windows only + error.BadPathName => unreachable, // Windows only + error.PipeBusy => unreachable, // Windows-only + error.SharingViolation => unreachable, // Windows-only + error.NetworkNotFound => unreachable, // Windows-only + error.FileLocksNotSupported => unreachable, // No lock requested. + error.NoSpaceLeft => unreachable, // read-only + error.PathAlreadyExists => unreachable, // read-only + error.DeviceBusy => unreachable, // read-only + error.FileBusy => unreachable, // read-only + error.InvalidHandle => unreachable, // should not be in the error set + error.WouldBlock => unreachable, // not using O_NONBLOCK + error.NoDevice => unreachable, // not asking for a special device + + error.AccessDenied, + error.FileNotFound, + error.NotDir, + error.IsDir, + => return error.GLibCNotFound, + + error.FileTooBig => return error.Unexpected, + + error.ProcessFdQuotaExceeded, + error.SystemFdQuotaExceeded, + error.SystemResources, + error.SymLinkLoop, + error.Unexpected, + => |e| return e, + }; + defer f.close(); + + return glibcVerFromSoFile(f) catch |err| switch (err) { + error.InvalidElfMagic, + error.InvalidElfEndian, + error.InvalidElfClass, + error.InvalidElfFile, + error.InvalidElfVersion, + error.InvalidGnuLibCVersion, + error.UnexpectedEndOfFile, + => return error.GLibCNotFound, + + error.SystemResources, + error.UnableToReadElfFile, + error.Unexpected, + error.FileSystem, + => |e| return e, + }; +} + +fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion { + var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined; + _ = try preadMin(file, &hdr_buf, 0, hdr_buf.len); + const hdr32 = @as(*elf.Elf32_Ehdr, @ptrCast(&hdr_buf)); + const hdr64 = @as(*elf.Elf64_Ehdr, @ptrCast(&hdr_buf)); + if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic; + const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) { + elf.ELFDATA2LSB => .little, + elf.ELFDATA2MSB => .big, + else => return error.InvalidElfEndian, + }; + const need_bswap = elf_endian != native_endian; + if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion; + + const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) { + elf.ELFCLASS32 => false, + elf.ELFCLASS64 => true, + else => return error.InvalidElfClass, + }; + const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); + var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff); + const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize); + const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx); + var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined; + if (sh_buf.len < shentsize) return error.InvalidElfFile; + + _ = try preadMin(file, &sh_buf, str_section_off, shentsize); + const shstr32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf)); + const shstr64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf)); + const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset); + const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size); + var strtab_buf: [4096:0]u8 = undefined; + const shstrtab_len = @min(shstrtab_size, strtab_buf.len); + const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len); + const shstrtab = strtab_buf[0..shstrtab_read_len]; + const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum); + var sh_i: u16 = 0; + const dynstr: struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) { + // Reserve some bytes so that we can deref the 64-bit struct fields + // even when the ELF file is 32-bits. + const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr); + const sh_read_byte_len = try preadMin( + file, + sh_buf[0 .. sh_buf.len - sh_reserve], + shoff, + shentsize, + ); + var sh_buf_i: usize = 0; + while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({ + sh_i += 1; + shoff += shentsize; + sh_buf_i += shentsize; + }) { + const sh32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i])); + const sh64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i])); + const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name); + const sh_name = mem.sliceTo(shstrtab[sh_name_off..], 0); + if (mem.eql(u8, sh_name, ".dynstr")) { + break :find_dyn_str .{ + .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset), + .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size), + }; + } + } + } else return error.InvalidGnuLibCVersion; + + // Here we loop over all the strings in the dynstr string table, assuming that any + // strings that start with "GLIBC_2." indicate the existence of such a glibc version, + // and furthermore, that the system-installed glibc is at minimum that version. + + // Empirically, glibc 2.34 libc.so .dynstr section is 32441 bytes on my system. + // Here I use double this value plus some headroom. This makes it only need + // a single read syscall here. + var buf: [80000]u8 = undefined; + if (buf.len < dynstr.size) return error.InvalidGnuLibCVersion; + + const dynstr_size: usize = @intCast(dynstr.size); + const dynstr_bytes = buf[0..dynstr_size]; + _ = try preadMin(file, dynstr_bytes, dynstr.offset, dynstr_bytes.len); + var it = mem.splitScalar(u8, dynstr_bytes, 0); + var max_ver: std.SemanticVersion = .{ .major = 2, .minor = 2, .patch = 5 }; + while (it.next()) |s| { + if (mem.startsWith(u8, s, "GLIBC_2.")) { + const chopped = s["GLIBC_".len..]; + const ver = Target.Query.parseVersion(chopped) catch |err| switch (err) { + error.Overflow => return error.InvalidGnuLibCVersion, + error.InvalidVersion => return error.InvalidGnuLibCVersion, + }; + switch (ver.order(max_ver)) { + .gt => max_ver = ver, + .lt, .eq => continue, + } + } + } + return max_ver; +} + +/// In the past, this function attempted to use the executable's own binary if it was dynamically +/// linked to answer both the C ABI question and the dynamic linker question. However, this +/// could be problematic on a system that uses a RUNPATH for the compiler binary, locking +/// it to an older glibc version, while system binaries such as /usr/bin/env use a newer glibc +/// version. The problem is that libc.so.6 glibc version will match that of the system while +/// the dynamic linker will match that of the compiler binary. Executables with these versions +/// mismatching will fail to run. +/// +/// Therefore, this function works the same regardless of whether the compiler binary is +/// dynamically or statically linked. It inspects `/usr/bin/env` as an ELF file to find the +/// answer to these questions, or if there is a shebang line, then it chases the referenced +/// file recursively. If that does not provide the answer, then the function falls back to +/// defaults. +fn detectAbiAndDynamicLinker( + cpu: Target.Cpu, + os: Target.Os, + query: Target.Query, +) DetectError!Target { + const native_target_has_ld = comptime builtin.target.hasDynamicLinker(); + const is_linux = builtin.target.os.tag == .linux; + const is_solarish = builtin.target.os.tag.isSolarish(); + const have_all_info = query.dynamic_linker.get() != null and + query.abi != null and (!is_linux or query.abi.?.isGnu()); + const os_is_non_native = query.os_tag != null; + // The Solaris/illumos environment is always the same. + if (!native_target_has_ld or have_all_info or os_is_non_native or is_solarish) { + return defaultAbiAndDynamicLinker(cpu, os, query); + } + if (query.abi) |abi| { + if (abi.isMusl()) { + // musl implies static linking. + return defaultAbiAndDynamicLinker(cpu, os, query); + } + } + // The current target's ABI cannot be relied on for this. For example, we may build the zig + // compiler for target riscv64-linux-musl and provide a tarball for users to download. + // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined + // and supported by Zig. But that means that we must detect the system ABI here rather than + // relying on `builtin.target`. + const all_abis = comptime blk: { + assert(@intFromEnum(Target.Abi.none) == 0); + const fields = std.meta.fields(Target.Abi)[1..]; + var array: [fields.len]Target.Abi = undefined; + for (fields, 0..) |field, i| { + array[i] = @field(Target.Abi, field.name); + } + break :blk array; + }; + var ld_info_list_buffer: [all_abis.len]LdInfo = undefined; + var ld_info_list_len: usize = 0; + const ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch); + + for (all_abis) |abi| { + // This may be a nonsensical parameter. We detect this with + // error.UnknownDynamicLinkerPath and skip adding it to `ld_info_list`. + const target: Target = .{ + .cpu = cpu, + .os = os, + .abi = abi, + .ofmt = ofmt, + }; + const ld = target.standardDynamicLinkerPath(); + if (ld.get() == null) continue; + + ld_info_list_buffer[ld_info_list_len] = .{ + .ld = ld, + .abi = abi, + }; + ld_info_list_len += 1; + } + const ld_info_list = ld_info_list_buffer[0..ld_info_list_len]; + + // Best case scenario: the executable is dynamically linked, and we can iterate + // over our own shared objects and find a dynamic linker. + const elf_file = blk: { + // This block looks for a shebang line in /usr/bin/env, + // if it finds one, then instead of using /usr/bin/env as the ELF file to examine, it uses the file it references instead, + // doing the same logic recursively in case it finds another shebang line. + + // Since /usr/bin/env is hard-coded into the shebang line of many portable scripts, it's a + // reasonably reliable path to start with. + var file_name: []const u8 = "/usr/bin/env"; + // #! (2) + 255 (max length of shebang line since Linux 5.1) + \n (1) + var buffer: [258]u8 = undefined; + while (true) { + const file = fs.openFileAbsolute(file_name, .{}) catch |err| switch (err) { + error.NoSpaceLeft => unreachable, + error.NameTooLong => unreachable, + error.PathAlreadyExists => unreachable, + error.SharingViolation => unreachable, + error.InvalidUtf8 => unreachable, + error.BadPathName => unreachable, + error.PipeBusy => unreachable, + error.FileLocksNotSupported => unreachable, + error.WouldBlock => unreachable, + error.FileBusy => unreachable, // opened without write permissions + + error.IsDir, + error.NotDir, + error.InvalidHandle, + error.AccessDenied, + error.NoDevice, + error.FileNotFound, + error.NetworkNotFound, + error.FileTooBig, + error.Unexpected, + => |e| { + std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.\n", .{@errorName(e)}); + return defaultAbiAndDynamicLinker(cpu, os, query); + }, + + else => |e| return e, + }; + errdefer file.close(); + + const len = preadMin(file, &buffer, 0, buffer.len) catch |err| switch (err) { + error.UnexpectedEndOfFile, + error.UnableToReadElfFile, + => break :blk file, + + else => |e| return e, + }; + const newline = mem.indexOfScalar(u8, buffer[0..len], '\n') orelse break :blk file; + const line = buffer[0..newline]; + if (!mem.startsWith(u8, line, "#!")) break :blk file; + var it = mem.tokenizeScalar(u8, line[2..], ' '); + file_name = it.next() orelse return defaultAbiAndDynamicLinker(cpu, os, query); + file.close(); + } + }; + defer elf_file.close(); + + // If Zig is statically linked, such as via distributed binary static builds, the above + // trick (block self_exe) won't work. The next thing we fall back to is the same thing, but for elf_file. + // TODO: inline this function and combine the buffer we already read above to find + // the possible shebang line with the buffer we use for the ELF header. + return abiAndDynamicLinkerFromFile(elf_file, cpu, os, ld_info_list, query) catch |err| switch (err) { + error.FileSystem, + error.SystemResources, + error.SymLinkLoop, + error.ProcessFdQuotaExceeded, + error.SystemFdQuotaExceeded, + => |e| return e, + + error.UnableToReadElfFile, + error.InvalidElfClass, + error.InvalidElfVersion, + error.InvalidElfEndian, + error.InvalidElfFile, + error.InvalidElfMagic, + error.Unexpected, + error.UnexpectedEndOfFile, + error.NameTooLong, + // Finally, we fall back on the standard path. + => |e| { + std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.\n", .{@errorName(e)}); + return defaultAbiAndDynamicLinker(cpu, os, query); + }, + }; +} + +fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, query: Target.Query) !Target { + const abi = query.abi orelse Target.Abi.default(cpu.arch, os); + return .{ + .cpu = cpu, + .os = os, + .abi = abi, + .ofmt = query.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch), + .dynamic_linker = if (query.dynamic_linker.get() == null) + Target.standardDynamicLinkerPath_cpu_os_abi(cpu, os.tag, abi) + else + query.dynamic_linker, + }; +} + +const LdInfo = struct { + ld: Target.DynamicLinker, + abi: Target.Abi, +}; + +fn preadMin(file: fs.File, buf: []u8, offset: u64, min_read_len: usize) !usize { + var i: usize = 0; + while (i < min_read_len) { + const len = file.pread(buf[i..], offset + i) catch |err| switch (err) { + error.OperationAborted => unreachable, // Windows-only + error.WouldBlock => unreachable, // Did not request blocking mode + error.NotOpenForReading => unreachable, + error.SystemResources => return error.SystemResources, + error.IsDir => return error.UnableToReadElfFile, + error.BrokenPipe => return error.UnableToReadElfFile, + error.Unseekable => return error.UnableToReadElfFile, + error.ConnectionResetByPeer => return error.UnableToReadElfFile, + error.ConnectionTimedOut => return error.UnableToReadElfFile, + error.SocketNotConnected => return error.UnableToReadElfFile, + error.NetNameDeleted => return error.UnableToReadElfFile, + error.Unexpected => return error.Unexpected, + error.InputOutput => return error.FileSystem, + error.AccessDenied => return error.Unexpected, + }; + if (len == 0) return error.UnexpectedEndOfFile; + i += len; + } + return i; +} + +fn elfInt(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) { + if (is_64) { + if (need_bswap) { + return @byteSwap(int_64); + } else { + return int_64; + } + } else { + if (need_bswap) { + return @byteSwap(int_32); + } else { + return int_32; + } + } +} + +const builtin = @import("builtin"); +const std = @import("../std.zig"); +const mem = std.mem; +const elf = std.elf; +const fs = std.fs; +const assert = std.debug.assert; +const Target = std.Target; +const native_endian = builtin.cpu.arch.endian(); + test { _ = NativePaths; - _ = NativeTargetInfo; _ = darwin; _ = linux; diff --git a/lib/std/zig/system/NativePaths.zig b/lib/std/zig/system/NativePaths.zig index 833d698d4285..715c172c39bc 100644 --- a/lib/std/zig/system/NativePaths.zig +++ b/lib/std/zig/system/NativePaths.zig @@ -5,7 +5,6 @@ const process = std.process; const mem = std.mem; const NativePaths = @This(); -const NativeTargetInfo = std.zig.system.NativeTargetInfo; arena: Allocator, include_dirs: std.ArrayListUnmanaged([]const u8) = .{}, @@ -14,8 +13,7 @@ framework_dirs: std.ArrayListUnmanaged([]const u8) = .{}, rpaths: std.ArrayListUnmanaged([]const u8) = .{}, warnings: std.ArrayListUnmanaged([]const u8) = .{}, -pub fn detect(arena: Allocator, native_info: NativeTargetInfo) !NativePaths { - const native_target = native_info.target; +pub fn detect(arena: Allocator, native_target: std.Target) !NativePaths { var self: NativePaths = .{ .arena = arena }; var is_nix = false; if (process.getEnvVarOwned(arena, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { diff --git a/lib/std/zig/system/NativeTargetInfo.zig b/lib/std/zig/system/NativeTargetInfo.zig deleted file mode 100644 index 080f18bf0a21..000000000000 --- a/lib/std/zig/system/NativeTargetInfo.zig +++ /dev/null @@ -1,1130 +0,0 @@ -const std = @import("../../std.zig"); -const builtin = @import("builtin"); -const mem = std.mem; -const assert = std.debug.assert; -const fs = std.fs; -const elf = std.elf; -const native_endian = builtin.cpu.arch.endian(); - -const NativeTargetInfo = @This(); -const Target = std.Target; -const Allocator = std.mem.Allocator; -const CrossTarget = std.zig.CrossTarget; -const windows = std.zig.system.windows; -const darwin = std.zig.system.darwin; -const linux = std.zig.system.linux; - -target: Target, -dynamic_linker: DynamicLinker = DynamicLinker{}, - -pub const DynamicLinker = Target.DynamicLinker; - -pub const DetectError = error{ - FileSystem, - SystemResources, - SymLinkLoop, - ProcessFdQuotaExceeded, - SystemFdQuotaExceeded, - DeviceBusy, - OSVersionDetectionFail, - Unexpected, -}; - -/// Given a `CrossTarget`, which specifies in detail which parts of the target should be detected -/// natively, which should be standard or default, and which are provided explicitly, this function -/// resolves the native components by detecting the native system, and then resolves standard/default parts -/// relative to that. -pub fn detect(cross_target: CrossTarget) DetectError!NativeTargetInfo { - var os = cross_target.getOsTag().defaultVersionRange(cross_target.getCpuArch()); - if (cross_target.os_tag == null) { - switch (builtin.target.os.tag) { - .linux => { - const uts = std.os.uname(); - const release = mem.sliceTo(&uts.release, 0); - // The release field sometimes has a weird format, - // `Version.parse` will attempt to find some meaningful interpretation. - if (std.SemanticVersion.parse(release)) |ver| { - os.version_range.linux.range.min = ver; - os.version_range.linux.range.max = ver; - } else |err| switch (err) { - error.Overflow => {}, - error.InvalidVersion => {}, - } - }, - .solaris, .illumos => { - const uts = std.os.uname(); - const release = mem.sliceTo(&uts.release, 0); - if (std.SemanticVersion.parse(release)) |ver| { - os.version_range.semver.min = ver; - os.version_range.semver.max = ver; - } else |err| switch (err) { - error.Overflow => {}, - error.InvalidVersion => {}, - } - }, - .windows => { - const detected_version = windows.detectRuntimeVersion(); - os.version_range.windows.min = detected_version; - os.version_range.windows.max = detected_version; - }, - .macos => try darwin.macos.detect(&os), - .freebsd, .netbsd, .dragonfly => { - const key = switch (builtin.target.os.tag) { - .freebsd => "kern.osreldate", - .netbsd, .dragonfly => "kern.osrevision", - else => unreachable, - }; - var value: u32 = undefined; - var len: usize = @sizeOf(@TypeOf(value)); - - std.os.sysctlbynameZ(key, &value, &len, null, 0) catch |err| switch (err) { - error.NameTooLong => unreachable, // constant, known good value - error.PermissionDenied => unreachable, // only when setting values, - error.SystemResources => unreachable, // memory already on the stack - error.UnknownName => unreachable, // constant, known good value - error.Unexpected => return error.OSVersionDetectionFail, - }; - - switch (builtin.target.os.tag) { - .freebsd => { - // https://www.freebsd.org/doc/en_US.ISO8859-1/books/porters-handbook/versions.html - // Major * 100,000 has been convention since FreeBSD 2.2 (1997) - // Minor * 1(0),000 summed has been convention since FreeBSD 2.2 (1997) - // e.g. 492101 = 4.11-STABLE = 4.(9+2) - const major = value / 100_000; - const minor1 = value % 100_000 / 10_000; // usually 0 since 5.1 - const minor2 = value % 10_000 / 1_000; // 0 before 5.1, minor version since - const patch = value % 1_000; - os.version_range.semver.min = .{ .major = major, .minor = minor1 + minor2, .patch = patch }; - os.version_range.semver.max = os.version_range.semver.min; - }, - .netbsd => { - // #define __NetBSD_Version__ MMmmrrpp00 - // - // M = major version - // m = minor version; a minor number of 99 indicates current. - // r = 0 (*) - // p = patchlevel - const major = value / 100_000_000; - const minor = value % 100_000_000 / 1_000_000; - const patch = value % 10_000 / 100; - os.version_range.semver.min = .{ .major = major, .minor = minor, .patch = patch }; - os.version_range.semver.max = os.version_range.semver.min; - }, - .dragonfly => { - // https://github.com/DragonFlyBSD/DragonFlyBSD/blob/cb2cde83771754aeef9bb3251ee48959138dec87/Makefile.inc1#L15-L17 - // flat base10 format: Mmmmpp - // M = major - // m = minor; odd-numbers indicate current dev branch - // p = patch - const major = value / 100_000; - const minor = value % 100_000 / 100; - const patch = value % 100; - os.version_range.semver.min = .{ .major = major, .minor = minor, .patch = patch }; - os.version_range.semver.max = os.version_range.semver.min; - }, - else => unreachable, - } - }, - .openbsd => { - const mib: [2]c_int = [_]c_int{ - std.os.CTL.KERN, - std.os.KERN.OSRELEASE, - }; - var buf: [64]u8 = undefined; - // consider that sysctl result includes null-termination - // reserve 1 byte to ensure we never overflow when appending ".0" - var len: usize = buf.len - 1; - - std.os.sysctl(&mib, &buf, &len, null, 0) catch |err| switch (err) { - error.NameTooLong => unreachable, // constant, known good value - error.PermissionDenied => unreachable, // only when setting values, - error.SystemResources => unreachable, // memory already on the stack - error.UnknownName => unreachable, // constant, known good value - error.Unexpected => return error.OSVersionDetectionFail, - }; - - // append ".0" to satisfy semver - buf[len - 1] = '.'; - buf[len] = '0'; - len += 1; - - if (std.SemanticVersion.parse(buf[0..len])) |ver| { - os.version_range.semver.min = ver; - os.version_range.semver.max = ver; - } else |_| { - return error.OSVersionDetectionFail; - } - }, - else => { - // Unimplemented, fall back to default version range. - }, - } - } - - if (cross_target.os_version_min) |min| switch (min) { - .none => {}, - .semver => |semver| switch (cross_target.getOsTag()) { - .linux => os.version_range.linux.range.min = semver, - else => os.version_range.semver.min = semver, - }, - .windows => |win_ver| os.version_range.windows.min = win_ver, - }; - - if (cross_target.os_version_max) |max| switch (max) { - .none => {}, - .semver => |semver| switch (cross_target.getOsTag()) { - .linux => os.version_range.linux.range.max = semver, - else => os.version_range.semver.max = semver, - }, - .windows => |win_ver| os.version_range.windows.max = win_ver, - }; - - if (cross_target.glibc_version) |glibc| { - assert(cross_target.isGnuLibC()); - os.version_range.linux.glibc = glibc; - } - - // Until https://github.com/ziglang/zig/issues/4592 is implemented (support detecting the - // native CPU architecture as being different than the current target), we use this: - const cpu_arch = cross_target.getCpuArch(); - - const cpu = switch (cross_target.cpu_model) { - .native => detectNativeCpuAndFeatures(cpu_arch, os, cross_target), - .baseline => Target.Cpu.baseline(cpu_arch), - .determined_by_cpu_arch => if (cross_target.cpu_arch == null) - detectNativeCpuAndFeatures(cpu_arch, os, cross_target) - else - Target.Cpu.baseline(cpu_arch), - .explicit => |model| model.toCpu(cpu_arch), - } orelse backup_cpu_detection: { - break :backup_cpu_detection Target.Cpu.baseline(cpu_arch); - }; - var result = try detectAbiAndDynamicLinker(cpu, os, cross_target); - // For x86, we need to populate some CPU feature flags depending on architecture - // and mode: - // * 16bit_mode => if the abi is code16 - // * 32bit_mode => if the arch is x86 - // However, the "mode" flags can be used as overrides, so if the user explicitly - // sets one of them, that takes precedence. - switch (cpu_arch) { - .x86 => { - if (!std.Target.x86.featureSetHasAny(cross_target.cpu_features_add, .{ - .@"16bit_mode", .@"32bit_mode", - })) { - switch (result.target.abi) { - .code16 => result.target.cpu.features.addFeature( - @intFromEnum(std.Target.x86.Feature.@"16bit_mode"), - ), - else => result.target.cpu.features.addFeature( - @intFromEnum(std.Target.x86.Feature.@"32bit_mode"), - ), - } - } - }, - .arm, .armeb => { - // XXX What do we do if the target has the noarm feature? - // What do we do if the user specifies +thumb_mode? - }, - .thumb, .thumbeb => { - result.target.cpu.features.addFeature( - @intFromEnum(std.Target.arm.Feature.thumb_mode), - ); - }, - else => {}, - } - cross_target.updateCpuFeatures(&result.target.cpu.features); - return result; -} - -/// In the past, this function attempted to use the executable's own binary if it was dynamically -/// linked to answer both the C ABI question and the dynamic linker question. However, this -/// could be problematic on a system that uses a RUNPATH for the compiler binary, locking -/// it to an older glibc version, while system binaries such as /usr/bin/env use a newer glibc -/// version. The problem is that libc.so.6 glibc version will match that of the system while -/// the dynamic linker will match that of the compiler binary. Executables with these versions -/// mismatching will fail to run. -/// -/// Therefore, this function works the same regardless of whether the compiler binary is -/// dynamically or statically linked. It inspects `/usr/bin/env` as an ELF file to find the -/// answer to these questions, or if there is a shebang line, then it chases the referenced -/// file recursively. If that does not provide the answer, then the function falls back to -/// defaults. -fn detectAbiAndDynamicLinker( - cpu: Target.Cpu, - os: Target.Os, - cross_target: CrossTarget, -) DetectError!NativeTargetInfo { - const native_target_has_ld = comptime builtin.target.hasDynamicLinker(); - const is_linux = builtin.target.os.tag == .linux; - const is_solarish = builtin.target.os.tag.isSolarish(); - const have_all_info = cross_target.dynamic_linker.get() != null and - cross_target.abi != null and (!is_linux or cross_target.abi.?.isGnu()); - const os_is_non_native = cross_target.os_tag != null; - // The Solaris/illumos environment is always the same. - if (!native_target_has_ld or have_all_info or os_is_non_native or is_solarish) { - return defaultAbiAndDynamicLinker(cpu, os, cross_target); - } - if (cross_target.abi) |abi| { - if (abi.isMusl()) { - // musl implies static linking. - return defaultAbiAndDynamicLinker(cpu, os, cross_target); - } - } - // The current target's ABI cannot be relied on for this. For example, we may build the zig - // compiler for target riscv64-linux-musl and provide a tarball for users to download. - // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined - // and supported by Zig. But that means that we must detect the system ABI here rather than - // relying on `builtin.target`. - const all_abis = comptime blk: { - assert(@intFromEnum(Target.Abi.none) == 0); - const fields = std.meta.fields(Target.Abi)[1..]; - var array: [fields.len]Target.Abi = undefined; - for (fields, 0..) |field, i| { - array[i] = @field(Target.Abi, field.name); - } - break :blk array; - }; - var ld_info_list_buffer: [all_abis.len]LdInfo = undefined; - var ld_info_list_len: usize = 0; - const ofmt = cross_target.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch); - - for (all_abis) |abi| { - // This may be a nonsensical parameter. We detect this with - // error.UnknownDynamicLinkerPath and skip adding it to `ld_info_list`. - const target: Target = .{ - .cpu = cpu, - .os = os, - .abi = abi, - .ofmt = ofmt, - }; - const ld = target.standardDynamicLinkerPath(); - if (ld.get() == null) continue; - - ld_info_list_buffer[ld_info_list_len] = .{ - .ld = ld, - .abi = abi, - }; - ld_info_list_len += 1; - } - const ld_info_list = ld_info_list_buffer[0..ld_info_list_len]; - - // Best case scenario: the executable is dynamically linked, and we can iterate - // over our own shared objects and find a dynamic linker. - const elf_file = blk: { - // This block looks for a shebang line in /usr/bin/env, - // if it finds one, then instead of using /usr/bin/env as the ELF file to examine, it uses the file it references instead, - // doing the same logic recursively in case it finds another shebang line. - - // Since /usr/bin/env is hard-coded into the shebang line of many portable scripts, it's a - // reasonably reliable path to start with. - var file_name: []const u8 = "/usr/bin/env"; - // #! (2) + 255 (max length of shebang line since Linux 5.1) + \n (1) - var buffer: [258]u8 = undefined; - while (true) { - const file = fs.openFileAbsolute(file_name, .{}) catch |err| switch (err) { - error.NoSpaceLeft => unreachable, - error.NameTooLong => unreachable, - error.PathAlreadyExists => unreachable, - error.SharingViolation => unreachable, - error.InvalidUtf8 => unreachable, - error.BadPathName => unreachable, - error.PipeBusy => unreachable, - error.FileLocksNotSupported => unreachable, - error.WouldBlock => unreachable, - error.FileBusy => unreachable, // opened without write permissions - - error.IsDir, - error.NotDir, - error.InvalidHandle, - error.AccessDenied, - error.NoDevice, - error.FileNotFound, - error.NetworkNotFound, - error.FileTooBig, - error.Unexpected, - => |e| { - std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.\n", .{@errorName(e)}); - return defaultAbiAndDynamicLinker(cpu, os, cross_target); - }, - - else => |e| return e, - }; - errdefer file.close(); - - const len = preadMin(file, &buffer, 0, buffer.len) catch |err| switch (err) { - error.UnexpectedEndOfFile, - error.UnableToReadElfFile, - => break :blk file, - - else => |e| return e, - }; - const newline = mem.indexOfScalar(u8, buffer[0..len], '\n') orelse break :blk file; - const line = buffer[0..newline]; - if (!mem.startsWith(u8, line, "#!")) break :blk file; - var it = mem.tokenizeScalar(u8, line[2..], ' '); - file_name = it.next() orelse return defaultAbiAndDynamicLinker(cpu, os, cross_target); - file.close(); - } - }; - defer elf_file.close(); - - // If Zig is statically linked, such as via distributed binary static builds, the above - // trick (block self_exe) won't work. The next thing we fall back to is the same thing, but for elf_file. - // TODO: inline this function and combine the buffer we already read above to find - // the possible shebang line with the buffer we use for the ELF header. - return abiAndDynamicLinkerFromFile(elf_file, cpu, os, ld_info_list, cross_target) catch |err| switch (err) { - error.FileSystem, - error.SystemResources, - error.SymLinkLoop, - error.ProcessFdQuotaExceeded, - error.SystemFdQuotaExceeded, - => |e| return e, - - error.UnableToReadElfFile, - error.InvalidElfClass, - error.InvalidElfVersion, - error.InvalidElfEndian, - error.InvalidElfFile, - error.InvalidElfMagic, - error.Unexpected, - error.UnexpectedEndOfFile, - error.NameTooLong, - // Finally, we fall back on the standard path. - => |e| { - std.log.warn("Encountered error: {s}, falling back to default ABI and dynamic linker.\n", .{@errorName(e)}); - return defaultAbiAndDynamicLinker(cpu, os, cross_target); - }, - }; -} - -fn glibcVerFromRPath(rpath: []const u8) !std.SemanticVersion { - var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) { - error.NameTooLong => unreachable, - error.InvalidUtf8 => unreachable, - error.BadPathName => unreachable, - error.DeviceBusy => unreachable, - error.NetworkNotFound => unreachable, // Windows-only - - error.FileNotFound, - error.NotDir, - error.InvalidHandle, - error.AccessDenied, - error.NoDevice, - => return error.GLibCNotFound, - - error.ProcessFdQuotaExceeded, - error.SystemFdQuotaExceeded, - error.SystemResources, - error.SymLinkLoop, - error.Unexpected, - => |e| return e, - }; - defer dir.close(); - - // Now we have a candidate for the path to libc shared object. In - // the past, we used readlink() here because the link name would - // reveal the glibc version. However, in more recent GNU/Linux - // installations, there is no symlink. Thus we instead use a more - // robust check of opening the libc shared object and looking at the - // .dynstr section, and finding the max version number of symbols - // that start with "GLIBC_2.". - const glibc_so_basename = "libc.so.6"; - var f = dir.openFile(glibc_so_basename, .{}) catch |err| switch (err) { - error.NameTooLong => unreachable, - error.InvalidUtf8 => unreachable, // Windows only - error.BadPathName => unreachable, // Windows only - error.PipeBusy => unreachable, // Windows-only - error.SharingViolation => unreachable, // Windows-only - error.NetworkNotFound => unreachable, // Windows-only - error.FileLocksNotSupported => unreachable, // No lock requested. - error.NoSpaceLeft => unreachable, // read-only - error.PathAlreadyExists => unreachable, // read-only - error.DeviceBusy => unreachable, // read-only - error.FileBusy => unreachable, // read-only - error.InvalidHandle => unreachable, // should not be in the error set - error.WouldBlock => unreachable, // not using O_NONBLOCK - error.NoDevice => unreachable, // not asking for a special device - - error.AccessDenied, - error.FileNotFound, - error.NotDir, - error.IsDir, - => return error.GLibCNotFound, - - error.FileTooBig => return error.Unexpected, - - error.ProcessFdQuotaExceeded, - error.SystemFdQuotaExceeded, - error.SystemResources, - error.SymLinkLoop, - error.Unexpected, - => |e| return e, - }; - defer f.close(); - - return glibcVerFromSoFile(f) catch |err| switch (err) { - error.InvalidElfMagic, - error.InvalidElfEndian, - error.InvalidElfClass, - error.InvalidElfFile, - error.InvalidElfVersion, - error.InvalidGnuLibCVersion, - error.UnexpectedEndOfFile, - => return error.GLibCNotFound, - - error.SystemResources, - error.UnableToReadElfFile, - error.Unexpected, - error.FileSystem, - => |e| return e, - }; -} - -fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion { - var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined; - _ = try preadMin(file, &hdr_buf, 0, hdr_buf.len); - const hdr32 = @as(*elf.Elf32_Ehdr, @ptrCast(&hdr_buf)); - const hdr64 = @as(*elf.Elf64_Ehdr, @ptrCast(&hdr_buf)); - if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic; - const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) { - elf.ELFDATA2LSB => .little, - elf.ELFDATA2MSB => .big, - else => return error.InvalidElfEndian, - }; - const need_bswap = elf_endian != native_endian; - if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion; - - const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) { - elf.ELFCLASS32 => false, - elf.ELFCLASS64 => true, - else => return error.InvalidElfClass, - }; - const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); - var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff); - const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize); - const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx); - var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined; - if (sh_buf.len < shentsize) return error.InvalidElfFile; - - _ = try preadMin(file, &sh_buf, str_section_off, shentsize); - const shstr32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf)); - const shstr64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf)); - const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset); - const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size); - var strtab_buf: [4096:0]u8 = undefined; - const shstrtab_len = @min(shstrtab_size, strtab_buf.len); - const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len); - const shstrtab = strtab_buf[0..shstrtab_read_len]; - const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum); - var sh_i: u16 = 0; - const dynstr: struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) { - // Reserve some bytes so that we can deref the 64-bit struct fields - // even when the ELF file is 32-bits. - const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr); - const sh_read_byte_len = try preadMin( - file, - sh_buf[0 .. sh_buf.len - sh_reserve], - shoff, - shentsize, - ); - var sh_buf_i: usize = 0; - while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({ - sh_i += 1; - shoff += shentsize; - sh_buf_i += shentsize; - }) { - const sh32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i])); - const sh64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i])); - const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name); - const sh_name = mem.sliceTo(shstrtab[sh_name_off..], 0); - if (mem.eql(u8, sh_name, ".dynstr")) { - break :find_dyn_str .{ - .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset), - .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size), - }; - } - } - } else return error.InvalidGnuLibCVersion; - - // Here we loop over all the strings in the dynstr string table, assuming that any - // strings that start with "GLIBC_2." indicate the existence of such a glibc version, - // and furthermore, that the system-installed glibc is at minimum that version. - - // Empirically, glibc 2.34 libc.so .dynstr section is 32441 bytes on my system. - // Here I use double this value plus some headroom. This makes it only need - // a single read syscall here. - var buf: [80000]u8 = undefined; - if (buf.len < dynstr.size) return error.InvalidGnuLibCVersion; - - const dynstr_size: usize = @intCast(dynstr.size); - const dynstr_bytes = buf[0..dynstr_size]; - _ = try preadMin(file, dynstr_bytes, dynstr.offset, dynstr_bytes.len); - var it = mem.splitScalar(u8, dynstr_bytes, 0); - var max_ver: std.SemanticVersion = .{ .major = 2, .minor = 2, .patch = 5 }; - while (it.next()) |s| { - if (mem.startsWith(u8, s, "GLIBC_2.")) { - const chopped = s["GLIBC_".len..]; - const ver = CrossTarget.parseVersion(chopped) catch |err| switch (err) { - error.Overflow => return error.InvalidGnuLibCVersion, - error.InvalidVersion => return error.InvalidGnuLibCVersion, - }; - switch (ver.order(max_ver)) { - .gt => max_ver = ver, - .lt, .eq => continue, - } - } - } - return max_ver; -} - -fn glibcVerFromLinkName(link_name: []const u8, prefix: []const u8) error{ UnrecognizedGnuLibCFileName, InvalidGnuLibCVersion }!std.SemanticVersion { - // example: "libc-2.3.4.so" - // example: "libc-2.27.so" - // example: "ld-2.33.so" - const suffix = ".so"; - if (!mem.startsWith(u8, link_name, prefix) or !mem.endsWith(u8, link_name, suffix)) { - return error.UnrecognizedGnuLibCFileName; - } - // chop off "libc-" and ".so" - const link_name_chopped = link_name[prefix.len .. link_name.len - suffix.len]; - return CrossTarget.parseVersion(link_name_chopped) catch |err| switch (err) { - error.Overflow => return error.InvalidGnuLibCVersion, - error.InvalidVersion => return error.InvalidGnuLibCVersion, - }; -} - -test glibcVerFromLinkName { - try std.testing.expectError(error.UnrecognizedGnuLibCFileName, glibcVerFromLinkName("ld-2.37.so", "this-prefix-does-not-exist")); - try std.testing.expectError(error.UnrecognizedGnuLibCFileName, glibcVerFromLinkName("libc-2.37.so-is-not-end", "libc-")); - - try std.testing.expectError(error.InvalidGnuLibCVersion, glibcVerFromLinkName("ld-2.so", "ld-")); - try std.testing.expectEqual(std.SemanticVersion{ .major = 2, .minor = 37, .patch = 0 }, try glibcVerFromLinkName("ld-2.37.so", "ld-")); - try std.testing.expectEqual(std.SemanticVersion{ .major = 2, .minor = 37, .patch = 0 }, try glibcVerFromLinkName("ld-2.37.0.so", "ld-")); - try std.testing.expectEqual(std.SemanticVersion{ .major = 2, .minor = 37, .patch = 1 }, try glibcVerFromLinkName("ld-2.37.1.so", "ld-")); - try std.testing.expectError(error.InvalidGnuLibCVersion, glibcVerFromLinkName("ld-2.37.4.5.so", "ld-")); -} - -pub const AbiAndDynamicLinkerFromFileError = error{ - FileSystem, - SystemResources, - SymLinkLoop, - ProcessFdQuotaExceeded, - SystemFdQuotaExceeded, - UnableToReadElfFile, - InvalidElfClass, - InvalidElfVersion, - InvalidElfEndian, - InvalidElfFile, - InvalidElfMagic, - Unexpected, - UnexpectedEndOfFile, - NameTooLong, -}; - -pub fn abiAndDynamicLinkerFromFile( - file: fs.File, - cpu: Target.Cpu, - os: Target.Os, - ld_info_list: []const LdInfo, - cross_target: CrossTarget, -) AbiAndDynamicLinkerFromFileError!NativeTargetInfo { - var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined; - _ = try preadMin(file, &hdr_buf, 0, hdr_buf.len); - const hdr32 = @as(*elf.Elf32_Ehdr, @ptrCast(&hdr_buf)); - const hdr64 = @as(*elf.Elf64_Ehdr, @ptrCast(&hdr_buf)); - if (!mem.eql(u8, hdr32.e_ident[0..4], elf.MAGIC)) return error.InvalidElfMagic; - const elf_endian: std.builtin.Endian = switch (hdr32.e_ident[elf.EI_DATA]) { - elf.ELFDATA2LSB => .little, - elf.ELFDATA2MSB => .big, - else => return error.InvalidElfEndian, - }; - const need_bswap = elf_endian != native_endian; - if (hdr32.e_ident[elf.EI_VERSION] != 1) return error.InvalidElfVersion; - - const is_64 = switch (hdr32.e_ident[elf.EI_CLASS]) { - elf.ELFCLASS32 => false, - elf.ELFCLASS64 => true, - else => return error.InvalidElfClass, - }; - var phoff = elfInt(is_64, need_bswap, hdr32.e_phoff, hdr64.e_phoff); - const phentsize = elfInt(is_64, need_bswap, hdr32.e_phentsize, hdr64.e_phentsize); - const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum); - - var result: NativeTargetInfo = .{ - .target = .{ - .cpu = cpu, - .os = os, - .abi = cross_target.abi orelse Target.Abi.default(cpu.arch, os), - .ofmt = cross_target.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch), - }, - .dynamic_linker = cross_target.dynamic_linker, - }; - var rpath_offset: ?u64 = null; // Found inside PT_DYNAMIC - const look_for_ld = cross_target.dynamic_linker.get() == null; - - var ph_buf: [16 * @sizeOf(elf.Elf64_Phdr)]u8 align(@alignOf(elf.Elf64_Phdr)) = undefined; - if (phentsize > @sizeOf(elf.Elf64_Phdr)) return error.InvalidElfFile; - - var ph_i: u16 = 0; - while (ph_i < phnum) { - // Reserve some bytes so that we can deref the 64-bit struct fields - // even when the ELF file is 32-bits. - const ph_reserve: usize = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr); - const ph_read_byte_len = try preadMin(file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize); - var ph_buf_i: usize = 0; - while (ph_buf_i < ph_read_byte_len and ph_i < phnum) : ({ - ph_i += 1; - phoff += phentsize; - ph_buf_i += phentsize; - }) { - const ph32: *elf.Elf32_Phdr = @ptrCast(@alignCast(&ph_buf[ph_buf_i])); - const ph64: *elf.Elf64_Phdr = @ptrCast(@alignCast(&ph_buf[ph_buf_i])); - const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type); - switch (p_type) { - elf.PT_INTERP => if (look_for_ld) { - const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset); - const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz); - if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong; - const filesz = @as(usize, @intCast(p_filesz)); - _ = try preadMin(file, result.dynamic_linker.buffer[0..filesz], p_offset, filesz); - // PT_INTERP includes a null byte in filesz. - const len = filesz - 1; - // dynamic_linker.max_byte is "max", not "len". - // We know it will fit in u8 because we check against dynamic_linker.buffer.len above. - result.dynamic_linker.max_byte = @as(u8, @intCast(len - 1)); - - // Use it to determine ABI. - const full_ld_path = result.dynamic_linker.buffer[0..len]; - for (ld_info_list) |ld_info| { - const standard_ld_basename = fs.path.basename(ld_info.ld.get().?); - if (std.mem.endsWith(u8, full_ld_path, standard_ld_basename)) { - result.target.abi = ld_info.abi; - break; - } - } - }, - // We only need this for detecting glibc version. - elf.PT_DYNAMIC => if (builtin.target.os.tag == .linux and result.target.isGnuLibC() and - cross_target.glibc_version == null) - { - var dyn_off = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset); - const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz); - const dyn_size: usize = if (is_64) @sizeOf(elf.Elf64_Dyn) else @sizeOf(elf.Elf32_Dyn); - const dyn_num = p_filesz / dyn_size; - var dyn_buf: [16 * @sizeOf(elf.Elf64_Dyn)]u8 align(@alignOf(elf.Elf64_Dyn)) = undefined; - var dyn_i: usize = 0; - dyn: while (dyn_i < dyn_num) { - // Reserve some bytes so that we can deref the 64-bit struct fields - // even when the ELF file is 32-bits. - const dyn_reserve: usize = @sizeOf(elf.Elf64_Dyn) - @sizeOf(elf.Elf32_Dyn); - const dyn_read_byte_len = try preadMin( - file, - dyn_buf[0 .. dyn_buf.len - dyn_reserve], - dyn_off, - dyn_size, - ); - var dyn_buf_i: usize = 0; - while (dyn_buf_i < dyn_read_byte_len and dyn_i < dyn_num) : ({ - dyn_i += 1; - dyn_off += dyn_size; - dyn_buf_i += dyn_size; - }) { - const dyn32: *elf.Elf32_Dyn = @ptrCast(@alignCast(&dyn_buf[dyn_buf_i])); - const dyn64: *elf.Elf64_Dyn = @ptrCast(@alignCast(&dyn_buf[dyn_buf_i])); - const tag = elfInt(is_64, need_bswap, dyn32.d_tag, dyn64.d_tag); - const val = elfInt(is_64, need_bswap, dyn32.d_val, dyn64.d_val); - if (tag == elf.DT_RUNPATH) { - rpath_offset = val; - break :dyn; - } - } - } - }, - else => continue, - } - } - } - - if (builtin.target.os.tag == .linux and result.target.isGnuLibC() and - cross_target.glibc_version == null) - { - const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx); - - var shoff = elfInt(is_64, need_bswap, hdr32.e_shoff, hdr64.e_shoff); - const shentsize = elfInt(is_64, need_bswap, hdr32.e_shentsize, hdr64.e_shentsize); - const str_section_off = shoff + @as(u64, shentsize) * @as(u64, shstrndx); - - var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined; - if (sh_buf.len < shentsize) return error.InvalidElfFile; - - _ = try preadMin(file, &sh_buf, str_section_off, shentsize); - const shstr32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf)); - const shstr64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf)); - const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset); - const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size); - var strtab_buf: [4096:0]u8 = undefined; - const shstrtab_len = @min(shstrtab_size, strtab_buf.len); - const shstrtab_read_len = try preadMin(file, &strtab_buf, shstrtab_off, shstrtab_len); - const shstrtab = strtab_buf[0..shstrtab_read_len]; - - const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum); - var sh_i: u16 = 0; - const dynstr: ?struct { offset: u64, size: u64 } = find_dyn_str: while (sh_i < shnum) { - // Reserve some bytes so that we can deref the 64-bit struct fields - // even when the ELF file is 32-bits. - const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr); - const sh_read_byte_len = try preadMin( - file, - sh_buf[0 .. sh_buf.len - sh_reserve], - shoff, - shentsize, - ); - var sh_buf_i: usize = 0; - while (sh_buf_i < sh_read_byte_len and sh_i < shnum) : ({ - sh_i += 1; - shoff += shentsize; - sh_buf_i += shentsize; - }) { - const sh32: *elf.Elf32_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i])); - const sh64: *elf.Elf64_Shdr = @ptrCast(@alignCast(&sh_buf[sh_buf_i])); - const sh_name_off = elfInt(is_64, need_bswap, sh32.sh_name, sh64.sh_name); - const sh_name = mem.sliceTo(shstrtab[sh_name_off..], 0); - if (mem.eql(u8, sh_name, ".dynstr")) { - break :find_dyn_str .{ - .offset = elfInt(is_64, need_bswap, sh32.sh_offset, sh64.sh_offset), - .size = elfInt(is_64, need_bswap, sh32.sh_size, sh64.sh_size), - }; - } - } - } else null; - - if (dynstr) |ds| { - if (rpath_offset) |rpoff| { - if (rpoff > ds.size) return error.InvalidElfFile; - const rpoff_file = ds.offset + rpoff; - const rp_max_size = ds.size - rpoff; - - const strtab_len = @min(rp_max_size, strtab_buf.len); - const strtab_read_len = try preadMin(file, &strtab_buf, rpoff_file, strtab_len); - const strtab = strtab_buf[0..strtab_read_len]; - - const rpath_list = mem.sliceTo(strtab, 0); - var it = mem.tokenizeScalar(u8, rpath_list, ':'); - while (it.next()) |rpath| { - if (glibcVerFromRPath(rpath)) |ver| { - result.target.os.version_range.linux.glibc = ver; - return result; - } else |err| switch (err) { - error.GLibCNotFound => continue, - else => |e| return e, - } - } - } - } - - if (result.dynamic_linker.get()) |dl_path| glibc_ver: { - // There is no DT_RUNPATH so we try to find libc.so.6 inside the same - // directory as the dynamic linker. - if (fs.path.dirname(dl_path)) |rpath| { - if (glibcVerFromRPath(rpath)) |ver| { - result.target.os.version_range.linux.glibc = ver; - return result; - } else |err| switch (err) { - error.GLibCNotFound => {}, - else => |e| return e, - } - } - - // So far, no luck. Next we try to see if the information is - // present in the symlink data for the dynamic linker path. - var link_buf: [std.os.PATH_MAX]u8 = undefined; - const link_name = std.os.readlink(dl_path, &link_buf) catch |err| switch (err) { - error.NameTooLong => unreachable, - error.InvalidUtf8 => unreachable, // Windows only - error.BadPathName => unreachable, // Windows only - error.UnsupportedReparsePointType => unreachable, // Windows only - error.NetworkNotFound => unreachable, // Windows only - - error.AccessDenied, - error.FileNotFound, - error.NotLink, - error.NotDir, - => break :glibc_ver, - - error.SystemResources, - error.FileSystem, - error.SymLinkLoop, - error.Unexpected, - => |e| return e, - }; - result.target.os.version_range.linux.glibc = glibcVerFromLinkName( - fs.path.basename(link_name), - "ld-", - ) catch |err| switch (err) { - error.UnrecognizedGnuLibCFileName, - error.InvalidGnuLibCVersion, - => break :glibc_ver, - }; - return result; - } - - // Nothing worked so far. Finally we fall back to hard-coded search paths. - // Some distros such as Debian keep their libc.so.6 in `/lib/$triple/`. - var path_buf: [std.os.PATH_MAX]u8 = undefined; - var index: usize = 0; - const prefix = "/lib/"; - const cpu_arch = @tagName(result.target.cpu.arch); - const os_tag = @tagName(result.target.os.tag); - const abi = @tagName(result.target.abi); - @memcpy(path_buf[index..][0..prefix.len], prefix); - index += prefix.len; - @memcpy(path_buf[index..][0..cpu_arch.len], cpu_arch); - index += cpu_arch.len; - path_buf[index] = '-'; - index += 1; - @memcpy(path_buf[index..][0..os_tag.len], os_tag); - index += os_tag.len; - path_buf[index] = '-'; - index += 1; - @memcpy(path_buf[index..][0..abi.len], abi); - index += abi.len; - const rpath = path_buf[0..index]; - if (glibcVerFromRPath(rpath)) |ver| { - result.target.os.version_range.linux.glibc = ver; - return result; - } else |err| switch (err) { - error.GLibCNotFound => {}, - else => |e| return e, - } - } - - return result; -} - -fn preadMin(file: fs.File, buf: []u8, offset: u64, min_read_len: usize) !usize { - var i: usize = 0; - while (i < min_read_len) { - const len = file.pread(buf[i..], offset + i) catch |err| switch (err) { - error.OperationAborted => unreachable, // Windows-only - error.WouldBlock => unreachable, // Did not request blocking mode - error.NotOpenForReading => unreachable, - error.SystemResources => return error.SystemResources, - error.IsDir => return error.UnableToReadElfFile, - error.BrokenPipe => return error.UnableToReadElfFile, - error.Unseekable => return error.UnableToReadElfFile, - error.ConnectionResetByPeer => return error.UnableToReadElfFile, - error.ConnectionTimedOut => return error.UnableToReadElfFile, - error.SocketNotConnected => return error.UnableToReadElfFile, - error.NetNameDeleted => return error.UnableToReadElfFile, - error.Unexpected => return error.Unexpected, - error.InputOutput => return error.FileSystem, - error.AccessDenied => return error.Unexpected, - }; - if (len == 0) return error.UnexpectedEndOfFile; - i += len; - } - return i; -} - -fn defaultAbiAndDynamicLinker(cpu: Target.Cpu, os: Target.Os, cross_target: CrossTarget) !NativeTargetInfo { - const target: Target = .{ - .cpu = cpu, - .os = os, - .abi = cross_target.abi orelse Target.Abi.default(cpu.arch, os), - .ofmt = cross_target.ofmt orelse Target.ObjectFormat.default(os.tag, cpu.arch), - }; - return NativeTargetInfo{ - .target = target, - .dynamic_linker = if (cross_target.dynamic_linker.get() == null) - target.standardDynamicLinkerPath() - else - cross_target.dynamic_linker, - }; -} - -pub const LdInfo = struct { - ld: DynamicLinker, - abi: Target.Abi, -}; - -pub fn elfInt(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) { - if (is_64) { - if (need_bswap) { - return @byteSwap(int_64); - } else { - return int_64; - } - } else { - if (need_bswap) { - return @byteSwap(int_32); - } else { - return int_32; - } - } -} - -fn detectNativeCpuAndFeatures(cpu_arch: Target.Cpu.Arch, os: Target.Os, cross_target: CrossTarget) ?Target.Cpu { - // Here we switch on a comptime value rather than `cpu_arch`. This is valid because `cpu_arch`, - // although it is a runtime value, is guaranteed to be one of the architectures in the set - // of the respective switch prong. - switch (builtin.cpu.arch) { - .x86_64, .x86 => { - return @import("x86.zig").detectNativeCpuAndFeatures(cpu_arch, os, cross_target); - }, - else => {}, - } - - switch (builtin.os.tag) { - .linux => return linux.detectNativeCpuAndFeatures(), - .macos => return darwin.macos.detectNativeCpuAndFeatures(), - .windows => return windows.detectNativeCpuAndFeatures(), - else => {}, - } - - // This architecture does not have CPU model & feature detection yet. - // See https://github.com/ziglang/zig/issues/4591 - return null; -} - -pub const Executor = union(enum) { - native, - rosetta, - qemu: []const u8, - wine: []const u8, - wasmtime: []const u8, - darling: []const u8, - bad_dl: []const u8, - bad_os_or_cpu, -}; - -pub const GetExternalExecutorOptions = struct { - allow_darling: bool = true, - allow_qemu: bool = true, - allow_rosetta: bool = true, - allow_wasmtime: bool = true, - allow_wine: bool = true, - qemu_fixes_dl: bool = false, - link_libc: bool = false, -}; - -/// Return whether or not the given host target is capable of executing natively executables -/// of the other target. -pub fn getExternalExecutor( - host: NativeTargetInfo, - candidate: *const NativeTargetInfo, - options: GetExternalExecutorOptions, -) Executor { - const os_match = host.target.os.tag == candidate.target.os.tag; - const cpu_ok = cpu_ok: { - if (host.target.cpu.arch == candidate.target.cpu.arch) - break :cpu_ok true; - - if (host.target.cpu.arch == .x86_64 and candidate.target.cpu.arch == .x86) - break :cpu_ok true; - - if (host.target.cpu.arch == .aarch64 and candidate.target.cpu.arch == .arm) - break :cpu_ok true; - - if (host.target.cpu.arch == .aarch64_be and candidate.target.cpu.arch == .armeb) - break :cpu_ok true; - - // TODO additionally detect incompatible CPU features. - // Note that in some cases the OS kernel will emulate missing CPU features - // when an illegal instruction is encountered. - - break :cpu_ok false; - }; - - var bad_result: Executor = .bad_os_or_cpu; - - if (os_match and cpu_ok) native: { - if (options.link_libc) { - if (candidate.dynamic_linker.get()) |candidate_dl| { - fs.cwd().access(candidate_dl, .{}) catch { - bad_result = .{ .bad_dl = candidate_dl }; - break :native; - }; - } - } - return .native; - } - - // If the OS match and OS is macOS and CPU is arm64, we can use Rosetta 2 - // to emulate the foreign architecture. - if (options.allow_rosetta and os_match and - host.target.os.tag == .macos and host.target.cpu.arch == .aarch64) - { - switch (candidate.target.cpu.arch) { - .x86_64 => return .rosetta, - else => return bad_result, - } - } - - // If the OS matches, we can use QEMU to emulate a foreign architecture. - if (options.allow_qemu and os_match and (!cpu_ok or options.qemu_fixes_dl)) { - return switch (candidate.target.cpu.arch) { - .aarch64 => Executor{ .qemu = "qemu-aarch64" }, - .aarch64_be => Executor{ .qemu = "qemu-aarch64_be" }, - .arm => Executor{ .qemu = "qemu-arm" }, - .armeb => Executor{ .qemu = "qemu-armeb" }, - .hexagon => Executor{ .qemu = "qemu-hexagon" }, - .x86 => Executor{ .qemu = "qemu-i386" }, - .m68k => Executor{ .qemu = "qemu-m68k" }, - .mips => Executor{ .qemu = "qemu-mips" }, - .mipsel => Executor{ .qemu = "qemu-mipsel" }, - .mips64 => Executor{ .qemu = "qemu-mips64" }, - .mips64el => Executor{ .qemu = "qemu-mips64el" }, - .powerpc => Executor{ .qemu = "qemu-ppc" }, - .powerpc64 => Executor{ .qemu = "qemu-ppc64" }, - .powerpc64le => Executor{ .qemu = "qemu-ppc64le" }, - .riscv32 => Executor{ .qemu = "qemu-riscv32" }, - .riscv64 => Executor{ .qemu = "qemu-riscv64" }, - .s390x => Executor{ .qemu = "qemu-s390x" }, - .sparc => Executor{ .qemu = "qemu-sparc" }, - .sparc64 => Executor{ .qemu = "qemu-sparc64" }, - .x86_64 => Executor{ .qemu = "qemu-x86_64" }, - else => return bad_result, - }; - } - - switch (candidate.target.os.tag) { - .windows => { - if (options.allow_wine) { - // x86_64 wine does not support emulating aarch64-windows and - // vice versa. - if (candidate.target.cpu.arch != builtin.cpu.arch) { - return bad_result; - } - switch (candidate.target.ptrBitWidth()) { - 32 => return Executor{ .wine = "wine" }, - 64 => return Executor{ .wine = "wine64" }, - else => return bad_result, - } - } - return bad_result; - }, - .wasi => { - if (options.allow_wasmtime) { - switch (candidate.target.ptrBitWidth()) { - 32 => return Executor{ .wasmtime = "wasmtime" }, - else => return bad_result, - } - } - return bad_result; - }, - .macos => { - if (options.allow_darling) { - // This check can be loosened once darling adds a QEMU-based emulation - // layer for non-host architectures: - // https://github.com/darlinghq/darling/issues/863 - if (candidate.target.cpu.arch != builtin.cpu.arch) { - return bad_result; - } - return Executor{ .darling = "darling" }; - } - return bad_result; - }, - else => return bad_result, - } -} diff --git a/lib/std/zig/system/darwin/macos.zig b/lib/std/zig/system/darwin/macos.zig index 2c0b22e96cba..c4feb7a35b26 100644 --- a/lib/std/zig/system/darwin/macos.zig +++ b/lib/std/zig/system/darwin/macos.zig @@ -87,7 +87,7 @@ fn parseSystemVersion(buf: []const u8) !std.SemanticVersion { const ver = try svt.expectContent(); try svt.skipUntilTag(.end, "string"); - return try std.zig.CrossTarget.parseVersion(ver); + return try std.Target.Query.parseVersion(ver); } const SystemVersionTokenizer = struct { diff --git a/lib/std/zig/system/linux.zig b/lib/std/zig/system/linux.zig index f2f89f8317b6..d2d31b407981 100644 --- a/lib/std/zig/system/linux.zig +++ b/lib/std/zig/system/linux.zig @@ -5,10 +5,7 @@ const io = std.io; const fs = std.fs; const fmt = std.fmt; const testing = std.testing; - const Target = std.Target; -const CrossTarget = std.zig.CrossTarget; - const assert = std.debug.assert; const SparcCpuinfoImpl = struct { diff --git a/lib/std/zig/system/x86.zig b/lib/std/zig/system/x86.zig index b99f5cf65faa..edcfcebf29d3 100644 --- a/lib/std/zig/system/x86.zig +++ b/lib/std/zig/system/x86.zig @@ -1,7 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); const Target = std.Target; -const CrossTarget = std.zig.CrossTarget; const XCR0_XMM = 0x02; const XCR0_YMM = 0x04; @@ -23,8 +22,8 @@ inline fn hasMask(input: u32, mask: u32) bool { return (input & mask) == mask; } -pub fn detectNativeCpuAndFeatures(arch: Target.Cpu.Arch, os: Target.Os, cross_target: CrossTarget) Target.Cpu { - _ = cross_target; +pub fn detectNativeCpuAndFeatures(arch: Target.Cpu.Arch, os: Target.Os, query: Target.Query) Target.Cpu { + _ = query; var cpu = Target.Cpu{ .arch = arch, .model = Target.Cpu.Model.generic(arch), diff --git a/src/Builtin.zig b/src/Builtin.zig new file mode 100644 index 000000000000..3211dd3a690b --- /dev/null +++ b/src/Builtin.zig @@ -0,0 +1,314 @@ +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); + try append(opts, &buffer); + return buffer.toOwnedSliceSentinel(0); +} + +pub fn append(opts: @This(), buffer: *std.ArrayList(u8)) Allocator.Error!void { + const target = opts.target; + const generic_arch_name = target.cpu.arch.genericName(); + const zig_backend = opts.zig_backend; + + @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; + \\ + ); + } + } +} + +pub fn populateFile(comp: *Compilation, mod: *Module, file: *File) !void { + assert(file.source_loaded == true); + + if (mod.root.statFile(mod.root_src_path)) |stat| { + if (stat.size != file.source.len) { + std.log.warn( + "the cached file '{}{s}' had the wrong size. Expected {d}, found {d}. " ++ + "Overwriting with correct file contents now", + .{ mod.root, mod.root_src_path, file.source.len, stat.size }, + ); + + try writeFile(file, mod); + } else { + file.stat = .{ + .size = stat.size, + .inode = stat.inode, + .mtime = stat.mtime, + }; + } + } else |err| switch (err) { + error.BadPathName => unreachable, // it's always "builtin.zig" + error.NameTooLong => unreachable, // it's always "builtin.zig" + error.PipeBusy => unreachable, // it's not a pipe + error.WouldBlock => unreachable, // not asking for non-blocking I/O + + error.FileNotFound => try writeFile(file, mod), + + else => |e| return e, + } + + log.debug("parsing and generating '{s}'", .{mod.root_src_path}); + + file.tree = try std.zig.Ast.parse(comp.gpa, file.source, .zig); + assert(file.tree.errors.len == 0); // builtin.zig must parse + file.tree_loaded = true; + + file.zir = try AstGen.generate(comp.gpa, file.tree); + assert(!file.zir.hasCompileErrors()); // builtin.zig must not have astgen errors + file.zir_loaded = true; + file.status = .success_zir; +} + +fn writeFile(file: *File, mod: *Module) !void { + var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + var af = try mod.root.atomicFile(mod.root_src_path, .{ .make_path = true }, &buf); + defer af.deinit(); + try af.file.writeAll(file.source); + af.finish() catch |err| switch (err) { + error.AccessDenied => switch (builtin.os.tag) { + .windows => { + // Very likely happened due to another process or thread + // simultaneously creating the same, correct builtin.zig file. + // This is not a problem; ignore it. + }, + else => return err, + }, + else => return err, + }; + + file.stat = .{ + .size = file.source.len, + .inode = 0, // dummy value + .mtime = 0, // dummy value + }; +} + +const builtin = @import("builtin"); +const std = @import("std"); +const Allocator = std.mem.Allocator; +const build_options = @import("build_options"); +const Module = @import("Package/Module.zig"); +const assert = std.debug.assert; +const AstGen = @import("AstGen.zig"); +const File = @import("Module.zig").File; +const Compilation = @import("Compilation.zig"); +const log = std.log.scoped(.builtin); diff --git a/src/Compilation.zig b/src/Compilation.zig index 296b300f8d8c..6c7f1d0d83cb 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -28,31 +28,79 @@ const libcxx = @import("libcxx.zig"); const wasi_libc = @import("wasi_libc.zig"); const fatal = @import("main.zig").fatal; const clangMain = @import("main.zig").clangMain; -const Module = @import("Module.zig"); +const Zcu = @import("Module.zig"); +/// Deprecated; use `Zcu`. +const Module = Zcu; const InternPool = @import("InternPool.zig"); -const BuildId = std.Build.CompileStep.BuildId; const Cache = std.Build.Cache; const c_codegen = @import("codegen/c.zig"); const libtsan = @import("libtsan.zig"); const Zir = @import("Zir.zig"); const Autodoc = @import("Autodoc.zig"); -const Color = @import("main.zig").Color; const resinator = @import("resinator.zig"); +const Builtin = @import("Builtin.zig"); +const LlvmObject = @import("codegen/llvm.zig").Object; + +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, +/// Arena-allocated memory, mostly used during initialization. However, it can +/// be used for other things requiring the same lifetime as the `Compilation`. +arena: Allocator, +/// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. +/// TODO: rename to zcu: ?*Zcu +module: ?*Module, +/// Contains different state depending on whether the Compilation uses +/// incremental or whole cache mode. +cache_use: CacheUse, +/// 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, + +/// The main output file. +/// In whole cache mode, this is null except for during the body of the update +/// function. In incremental cache mode, this is a long-lived object. +/// In both cases, this is `null` when `-fno-emit-bin` is used. +bin_file: ?*link.File, + +/// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin) +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, +include_compiler_rt: bool, +objects: []Compilation.LinkObject, +/// Needed only for passing -F args to clang. +framework_dirs: []const []const u8, +/// These are *always* dynamically linked. Static libraries will be +/// provided as positional arguments. +system_libs: std.StringArrayHashMapUnmanaged(SystemLib), +version: ?std.SemanticVersion, +libc_installation: ?*const LibCInstallation, +skip_linker_dependencies: bool, +no_builtin: bool, +function_sections: bool, +data_sections: bool, +link_eh_frame_hdr: bool, +native_system_include_paths: []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), + 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 .{}, -/// This is a pointer to a local variable inside `update()`. -whole_cache_manifest: ?*Cache.Manifest = null, -whole_cache_manifest_mutex: std.Thread.Mutex = .{}, link_error_flags: link.File.ErrorFlags = .{}, +link_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .{}, lld_errors: std.ArrayListUnmanaged(LldError) = .{}, work_queue: std.fifo.LinearFifo(Job, .Dynamic), @@ -87,9 +135,6 @@ failed_win32_resources: if (build_options.only_core_functionality) void else std /// Miscellaneous things that can fail. misc_failures: std.AutoArrayHashMapUnmanaged(MiscTask, MiscError) = .{}, -keep_source_files_loaded: bool, -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. @@ -105,33 +150,25 @@ verbose_llvm_ir: ?[]const u8, verbose_llvm_bc: ?[]const u8, verbose_cimport: bool, verbose_llvm_cpu_features: bool, +verbose_link: 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, job_queued_compiler_rt_obj: bool = false, +job_queued_update_builtin_zig: bool, alloc_failure_occurred: bool = false, formatted_panics: bool = false, last_update_was_cache_hit: bool = false, c_source_files: []const CSourceFile, -clang_argv: []const []const u8, rc_source_files: []const RcSourceFile, +global_cc_argv: []const []const u8, cache_parent: *Cache, /// Path to own executable for invoking `zig clang`. self_exe_path: ?[]const u8, -/// null means -fno-emit-bin. -/// This is mutable memory allocated into the Compilation-lifetime arena (`arena`) -/// of exactly the correct size for "o/[digest]/[basename]". -/// The basename is of the outputted binary file in case we don't know the directory yet. -whole_bin_sub_path: ?[]u8, -/// Same as `whole_bin_sub_path` but for implibs. -whole_implib_sub_path: ?[]u8, -whole_docs_sub_path: ?[]u8, zig_lib_directory: Directory, local_cache_directory: Directory, global_cache_directory: Directory, @@ -163,19 +200,13 @@ compiler_rt_lib: ?CRTFile = null, compiler_rt_obj: ?CRTFile = null, glibc_so_files: ?glibc.BuiltSharedObjects = null, +wasi_emulated_libs: []const wasi_libc.CRTFile, /// For example `Scrt1.o` and `libc_nonshared.a`. These are populated after building libc from source, /// The set of needed CRT (C runtime) files differs depending on the target and compilation settings. /// The key is the basename, and the value is the absolute path to the completed build artifact. crt_files: std.StringHashMapUnmanaged(CRTFile) = .{}, -/// Keeping track of this possibly open resource so we can close it later. -owned_link_dir: ?std.fs.Dir, - -/// This is for stage1 and should be deleted upon completion of self-hosting. -/// Don't use this for anything other than stage1 compatibility. -color: Color = .auto, - /// How many lines of reference trace should be included per compile error. /// Null means only show snippet on first error. reference_trace: ?u32 = null, @@ -195,7 +226,31 @@ emit_llvm_bc: ?EmitLoc, work_queue_wait_group: WaitGroup = .{}, astgen_wait_group: WaitGroup = .{}, -pub const default_stack_protector_buffer_size = 4; +llvm_opt_bisect_limit: c_int, + +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: []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 try arena.dupeZ(u8, basename); + } + } +}; + +pub const default_stack_protector_buffer_size = target_util.default_stack_protector_buffer_size; pub const SemaError = Module.SemaError; pub const CRTFile = struct { @@ -209,8 +264,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 }, @@ -227,16 +282,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 = &.{}, }; @@ -289,7 +348,7 @@ const Job = union(enum) { /// one of WASI libc static objects wasi_libc_crt_file: wasi_libc.CRTFile, - /// The value is the index into `link.File.Options.system_libs`. + /// The value is the index into `system_libs`. windows_import_lib: usize, }; @@ -659,6 +718,8 @@ pub const Win32Resource = struct { pub const MiscTask = enum { write_builtin_zig, + rename_results, + check_whole_cache, glibc_crt_file, glibc_shared_objects, musl_crt_file, @@ -743,6 +804,41 @@ pub const EmitLoc = struct { }; pub const cache_helpers = struct { + pub fn addModule(hh: *Cache.HashHelper, mod: *const Package.Module) void { + addResolvedTarget(hh, mod.resolved_target); + hh.add(mod.optimize_mode); + hh.add(mod.code_model); + hh.add(mod.single_threaded); + hh.add(mod.error_tracing); + hh.add(mod.valgrind); + hh.add(mod.pic); + hh.add(mod.strip); + hh.add(mod.omit_frame_pointer); + hh.add(mod.stack_check); + hh.add(mod.red_zone); + hh.add(mod.sanitize_c); + hh.add(mod.sanitize_thread); + hh.add(mod.unwind_tables); + hh.add(mod.structured_cfg); + hh.addListOfBytes(mod.cc_argv); + } + + 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); } @@ -752,7 +848,21 @@ 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 addOptionalDebugFormat(hh: *Cache.HashHelper, x: ?Config.DebugFormat) void { + hh.add(x != null); + addDebugFormat(hh, x orelse return); + } + + pub fn addDebugFormat(hh: *Cache.HashHelper, x: Config.DebugFormat) void { + const tag: @typeInfo(Config.DebugFormat).Union.tag_type.? = x; + hh.add(tag); + switch (x) { + .strip, .code_view => {}, + .dwarf => |f| hh.add(f), + } + } + + 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. @@ -771,8 +881,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 `. @@ -781,9 +889,62 @@ pub const ClangPreprocessorMode = enum { stdout, }; -pub const Framework = link.Framework; +pub const Framework = link.File.MachO.Framework; pub const SystemLib = link.SystemLib; -pub const CacheMode = link.CacheMode; + +pub const CacheMode = enum { incremental, whole }; + +const CacheUse = union(CacheMode) { + incremental: *Incremental, + whole: *Whole, + + const Whole = struct { + /// This is a pointer to a local variable inside `update()`. + cache_manifest: ?*Cache.Manifest = null, + cache_manifest_mutex: std.Thread.Mutex = .{}, + /// null means -fno-emit-bin. + /// This is mutable memory allocated into the Compilation-lifetime arena (`arena`) + /// of exactly the correct size for "o/[digest]/[basename]". + /// The basename is of the outputted binary file in case we don't know the directory yet. + bin_sub_path: ?[]u8, + /// Same as `bin_sub_path` but for implibs. + implib_sub_path: ?[]u8, + docs_sub_path: ?[]u8, + lf_open_opts: link.File.OpenOptions, + tmp_artifact_directory: ?Cache.Directory, + /// Prevents other processes from clobbering files in the output directory. + lock: ?Cache.Lock, + + fn releaseLock(whole: *Whole) void { + if (whole.lock) |*lock| { + lock.release(); + whole.lock = null; + } + } + + fn moveLock(whole: *Whole) Cache.Lock { + const result = whole.lock.?; + whole.lock = null; + return result; + } + }; + + const Incremental = struct { + /// Where build artifacts and incremental compilation metadata serialization go. + artifact_directory: Compilation.Directory, + }; + + fn deinit(cu: CacheUse) void { + switch (cu) { + .incremental => |incremental| { + incremental.artifact_directory.handle.close(); + }, + .whole => |whole| { + whole.releaseLock(); + }, + } + } +}; pub const LinkObject = struct { path: []const u8, @@ -795,16 +956,27 @@ pub const LinkObject = struct { loption: bool = false, }; -pub const InitOptions = struct { +pub const CreateOptions = 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, - dynamic_linker: ?[]const u8 = null, + 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 = null, + /// This is provided so that the API user has a chance to tweak the + /// per-module settings of the standard library. + /// When this is null, a default configuration of the std lib is created + /// based on the settings of root_mod. + std_mod: ?*Package.Module = null, + root_name: []const u8, sysroot: ?[]const u8 = null, /// `null` means to not emit a binary file. emit_bin: ?EmitLoc, @@ -820,8 +992,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 /// operation, allowing Zig to skip linking when the hash would be unchanged. @@ -830,14 +1000,11 @@ 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{}, @@ -850,41 +1017,16 @@ pub const InitOptions = struct { /// * getpid /// * 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, + wasi_emulated_libs: []const wasi_libc.CRTFile = &.{}, /// This means that if the output mode is an executable it will be a /// Position Independent Executable. If the output mode is not an /// executable this field is ignored. - 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, @@ -895,22 +1037,19 @@ 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, linker_print_icf_sections: bool = false, linker_print_map: bool = false, - linker_opt_bisect_limit: i32 = -1, + llvm_opt_bisect_limit: i32 = -1, each_lib_rpath: ?bool = null, - build_id: ?BuildId = null, + build_id: ?std.zig.BuildId = null, disable_c_depfile: bool = false, linker_z_nodelete: bool = false, linker_z_notext: bool = false, @@ -924,12 +1063,11 @@ pub const InitOptions = struct { linker_tsaware: bool = false, linker_nxcompat: bool = false, linker_dynamicbase: bool = true, - linker_optimization: ?u8 = null, - linker_compress_debug_sections: ?link.CompressDebugSections = null, + linker_compress_debug_sections: ?link.File.Elf.CompressDebugSections = null, linker_module_definition_file: ?[]const u8 = null, - linker_sort_section: ?link.SortSection = null, - major_subsystem_version: ?u32 = null, - minor_subsystem_version: ?u32 = null, + linker_sort_section: ?link.File.Elf.SortSection = null, + major_subsystem_version: ?u16 = null, + minor_subsystem_version: ?u16 = null, clang_passthrough_mode: bool = false, verbose_cc: bool = false, verbose_link: bool = false, @@ -940,8 +1078,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 @@ -949,28 +1085,21 @@ pub const InitOptions = struct { /// building such dependencies themselves, this flag must be set to avoid /// infinite recursion. skip_linker_dependencies: bool = false, - hash_style: link.HashStyle = .both, - entry: ?[]const u8 = null, + hash_style: link.File.Elf.HashStyle = .both, + entry: Entry = .default, force_undefined_symbols: std.StringArrayHashMapUnmanaged(void) = .{}, - stack_size_override: ?u64 = null, - image_base_override: ?u64 = null, - self_exe_path: ?[]const u8 = null, + stack_size: ?u64 = null, + image_base: ?u64 = null, version: ?std.SemanticVersion = null, compatibility_version: ?std.SemanticVersion = null, libc_installation: ?*const LibCInstallation = null, - machine_code_model: std.builtin.CodeModel = .default, + native_system_include_paths: []const []const u8 = &.{}, 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 @@ -991,449 +1120,223 @@ 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, - /// (SPIR-V) whether to generate a structured control flow graph or not - want_structured_cfg: ?bool = null, + error_limit: ?Compilation.Module.ErrorInt = null, + global_cc_argv: []const []const u8 = &.{}, + + pub const Entry = link.File.OpenOptions.Entry; }; fn addModuleTableToCacheHash( + gpa: Allocator, + arena: Allocator, hash: *Cache.HashHelper, - arena: *std.heap.ArenaAllocator, - mod_table: Package.Module.Deps, - seen_table: *std.AutoHashMap(*Package.Module, void), + root_mod: *Package.Module, hash_type: union(enum) { path_bytes, files: *Cache.Manifest }, ) (error{OutOfMemory} || std.os.GetCwdError)!void { - const allocator = arena.allocator(); - - const module_indices = try allocator.alloc(u32, mod_table.count()); - // Copy over the hashmap entries to our slice - for (module_indices, 0..) |*module_index, index| module_index.* = @intCast(index); - // Sort the slice by package name - mem.sortUnstable(u32, module_indices, &mod_table, struct { - fn lessThan(deps: *const Package.Module.Deps, lhs: u32, rhs: u32) bool { - const keys = deps.keys(); - return std.mem.lessThan(u8, keys[lhs], keys[rhs]); + var seen_table: std.AutoArrayHashMapUnmanaged(*Package.Module, void) = .{}; + defer seen_table.deinit(gpa); + try seen_table.put(gpa, root_mod, {}); + + const SortByName = struct { + names: []const []const u8, + + pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool { + const lhs_key = ctx.names[lhs_index]; + const rhs_key = ctx.names[rhs_index]; + return mem.lessThan(u8, lhs_key, rhs_key); } - }.lessThan); + }; - for (module_indices) |module_index| { - const module = mod_table.values()[module_index]; - if ((try seen_table.getOrPut(module)).found_existing) continue; + var i: usize = 0; + while (i < seen_table.count()) : (i += 1) { + const mod = seen_table.keys()[i]; + if (mod.isBuiltin()) { + // 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. + continue; + } + + cache_helpers.addModule(hash, mod); - // Finally insert the package name and path to the cache hash. - hash.addBytes(mod_table.keys()[module_index]); switch (hash_type) { .path_bytes => { - hash.addBytes(module.root_src_path); - hash.addOptionalBytes(module.root.root_dir.path); - hash.addBytes(module.root.sub_path); + hash.addBytes(mod.root_src_path); + hash.addOptionalBytes(mod.root.root_dir.path); + hash.addBytes(mod.root.sub_path); }, .files => |man| { - const pkg_zig_file = try module.root.joinString( - allocator, - module.root_src_path, - ); + const pkg_zig_file = try mod.root.joinString(arena, mod.root_src_path); _ = try man.addFile(pkg_zig_file, null); }, } - // Recurse to handle the module's dependencies - try addModuleTableToCacheHash(hash, arena, module.deps, seen_table, hash_type); + + mod.deps.sortUnstable(SortByName{ .names = mod.deps.keys() }); + + hash.addListOfBytes(mod.deps.keys()); + + const deps = mod.deps.values(); + try seen_table.ensureUnusedCapacity(gpa, deps.len); + for (deps) |dep| seen_table.putAssumeCapacity(dep, {}); } } -pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { - const is_dyn_lib = switch (options.output_mode) { +pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compilation { + const output_mode = options.config.output_mode; + const is_dyn_lib = switch (output_mode) { .Obj, .Exe => false, - .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 comp: *Compilation = comp: { - // For allocations that have the same lifetime as Compilation. This arena is used only during this - // initialization and then is freed in deinit(). - var arena_allocator = std.heap.ArenaAllocator.init(gpa); - errdefer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); + const have_zcu = options.config.have_zcu; + const comp: *Compilation = comp: { // We put the `Compilation` itself in the arena. Freeing the arena will free the module. // It's initialized later after we prepare the initialization options. - const 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; - } - } - - // 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) - CacheMode.whole - else - options.cache_mode; - - const tsan = options.want_tsan orelse false; - // TSAN is implemented in C++ so it requires linking libc++. - const link_libcpp = options.link_libcpp or tsan; - const link_libc = link_libcpp or options.link_libc or options.link_libunwind or - target_util.osRequiresLibC(options.target); - - const link_libunwind = options.link_libunwind or - (link_libcpp and target_util.libcNeedsLibUnwind(options.target)); - const unwind_tables = options.want_unwind_tables orelse - (link_libunwind or target_util.needUnwindTables(options.target)); - const link_eh_frame_hdr = options.link_eh_frame_hdr or unwind_tables; - const build_id = options.build_id orelse .none; + const use_llvm = options.config.use_llvm; - // 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; - } + // The "any" values provided by resolved config only account for + // explicitly-provided settings. We now make them additionally account + // for default setting resolution. + const any_unwind_tables = options.config.any_unwind_tables or options.root_mod.unwind_tables; + const any_non_single_threaded = options.config.any_non_single_threaded or !options.root_mod.single_threaded; + const any_sanitize_thread = options.config.any_sanitize_thread or options.root_mod.sanitize_thread; - if (!build_options.have_llvm) - break :blk false; - - if (options.target.ofmt == .c) - break :blk false; - - if (options.want_lto) |lto| { - if (lto) { - break :blk true; - } - } - - // Our linker can't handle objects or most advanced options yet. - if (options.link_objects.len != 0 or - options.c_source_files.len != 0 or - options.frameworks.len != 0 or - options.system_lib_names.len != 0 or - options.link_libc or options.link_libcpp or - link_eh_frame_hdr or - options.link_emit_relocs or - options.output_mode == .Lib or - options.linker_script != null or options.version_script != null or - options.emit_implib != null or - build_id != .none or - options.symbol_wrap_set.count() > 0) - { - break :blk true; - } - - if (use_llvm) { - // If stage1 generates an object file, self-hosted linker is not - // yet sophisticated enough to handle that. - break :blk options.main_mod != null; - } - - break :blk false; - }; - - const lto = blk: { - if (options.want_lto) |want_lto| { - if (want_lto and !use_lld and !options.target.isDarwin()) - return error.LtoUnavailableWithoutLld; - break :blk want_lto; - } else if (!use_lld) { - // zig ld LTO support is tracked by - // https://github.com/ziglang/zig/issues/8680 - break :blk false; - } else if (options.c_source_files.len == 0) { - break :blk false; - } else if (options.target.cpu.arch.isRISCV()) { - // Clang and LLVM currently don't support RISC-V target-abi for LTO. - // Compiling with LTO may fail or produce undesired results. - // See https://reviews.llvm.org/D71387 - // See https://reviews.llvm.org/D102582 - break :blk false; - } else switch (options.output_mode) { - .Lib, .Obj => break :blk false, - .Exe => switch (options.optimize_mode) { - .Debug => break :blk false, - .ReleaseSafe, .ReleaseFast, .ReleaseSmall => break :blk true, - }, - } - }; - - const must_dynamic_link = dl: { - if (target_util.cannotDynamicLink(options.target)) - break :dl false; - if (is_exe_or_dyn_lib and link_libc and - (options.target.isGnuLibC() or target_util.osRequiresLibC(options.target))) - { - break :dl true; - } - const any_dyn_libs: bool = x: { - if (options.system_lib_names.len != 0) - break :x true; - for (options.link_objects) |obj| { - switch (classifyFileExt(obj.path)) { - .shared_library => break :x true, - else => continue, - } - } - break :x false; - }; - if (any_dyn_libs) { - // When creating a executable that links to system libraries, - // we require dynamic linking, but we must not link static libraries - // or object files dynamically! - break :dl (options.output_mode == .Exe); - } - - 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_eh_frame_hdr = options.link_eh_frame_hdr or any_unwind_tables; + const build_id = options.build_id orelse .none; - const dll_export_fns = options.dll_export_fns orelse (is_dyn_lib or options.rdynamic); + const link_libc = options.config.link_libc; 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) { - .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); + const each_lib_rpath = options.each_lib_rpath orelse + options.root_mod.resolved_target.is_native_os; + // We put everything into the cache hash that *cannot be modified // during an incremental update*. For example, one cannot change the // target between updates, but one can change source files, so the @@ -1455,197 +1358,27 @@ 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.addOptional(options.dwarf_format); + cache.hash.add(options.config.link_libcpp); + cache.hash.add(options.config.link_libunwind); + cache.hash.add(output_mode); + cache_helpers.addDebugFormat(&cache.hash, options.config.debug_format); cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_bin); cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_implib); cache_helpers.addOptionalEmitLoc(&cache.hash, options.emit_docs); 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: { - // Options that are specific to zig source files, that cannot be - // modified between incremental updates. - var hash = cache.hash; - - switch (cache_mode) { - .incremental => { - // Here we put the root source file path name, but *not* with addFile. - // We want the hash to be the same regardless of the contents of the - // source file, because incremental compilation will handle it, but we - // 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); - } - }, - .whole => { - // In this case, we postpone adding the input source file until - // we create the cache manifest, in update(), because we want to - // track it and packages as files. - }, - } - - // Synchronize with other matching comments: ZigOnlyHashStuff - hash.add(valgrind); - hash.add(single_threaded); - hash.add(use_llvm); - hash.add(use_lib_llvm); - hash.add(dll_export_fns); - hash.add(options.is_test); - hash.add(options.test_evented_io); - hash.addOptionalBytes(options.test_filter); - hash.addOptionalBytes(options.test_name_prefix); - hash.add(options.skip_linker_dependencies); - hash.add(formatted_panics); - hash.add(options.emit_h != null); - hash.add(error_limit); - hash.addOptional(options.want_structured_cfg); - - // In the case of incremental cache mode, this `zig_cache_artifact_directory` - // is computed based on a hash of non-linker inputs, and it is where all - // build artifacts are stored (even while in-progress). - // - // For whole cache mode, it is still used for builtin.zig so that the file - // path to builtin.zig can remain consistent during a debugging session at - // runtime. However, we don't know where to put outputs from the linker - // until the final cache hash, which is available after the - // compilation is complete. - // - // Therefore, in whole cache mode, we additionally create a temporary cache - // directory for these two kinds of build artifacts, and then rename it - // into place after the final hash is known. However, we don't want - // to create the temporary directory here, because in the case of a cache hit, - // this would have been wasted syscalls to make the directory and then not - // use it (or delete it). - // - // In summary, for whole cache mode, we simulate `-fno-emit-bin` in this - // function, and `zig_cache_artifact_directory` is *wrong* except for builtin.zig, - // and then at the beginning of `update()` when we find out whether we need - // a temporary directory, we patch up all the places that the incorrect - // `zig_cache_artifact_directory` was passed to various components of the compiler. - - const digest = hash.final(); - const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); - var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); - errdefer artifact_dir.close(); - const zig_cache_artifact_directory: Directory = .{ - .handle = artifact_dir, - .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); - } - + const main_mod = options.main_mod orelse options.root_mod; + const comp = try arena.create(Compilation); + const opt_zcu: ?*Module = if (have_zcu) blk: { // 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"; @@ -1664,277 +1397,183 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { }; const emit_h: ?*Module.GlobalEmitH = if (options.emit_h) |loc| eh: { - const eh = try gpa.create(Module.GlobalEmitH); + const eh = try arena.create(Module.GlobalEmitH); eh.* = .{ .loc = loc }; break :eh eh; } else null; - errdefer if (emit_h) |eh| gpa.destroy(eh); - // TODO when we implement serialization and deserialization of incremental - // compilation metadata, this is where we would load it. We have open a handle - // to the directory where the output either already is, or will be. - // 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 std_mod = options.std_mod orelse try Package.Module.create(arena, .{ + .global_cache_directory = options.global_cache_directory, + .paths = .{ + .root = .{ + .root_dir = options.zig_lib_directory, + .sub_path = "std", + }, + .root_src_path = "std.zig", + }, + .fully_qualified_name = "std", + .cc_argv = &.{}, + .inherited = .{}, + .global = options.config, + .parent = options.root_mod, + .builtin_mod = options.root_mod.getBuiltinDependency(), + }); - const module = try arena.create(Module); - errdefer module.deinit(); - module.* = .{ + const zcu = try arena.create(Module); + zcu.* = .{ .gpa = gpa, .comp = comp, .main_mod = main_mod, - .root_mod = root_mod, - .zig_cache_artifact_directory = zig_cache_artifact_directory, + .root_mod = options.root_mod, + .std_mod = std_mod, .global_zir_cache = global_zir_cache, .local_zir_cache = local_zir_cache, .emit_h = emit_h, .tmp_hack_arena = std.heap.ArenaAllocator.init(gpa), .error_limit = error_limit, + .llvm_object = null, }; - try module.init(); - - break :blk module; + try zcu.init(); + 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, - }; - - // 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 emit_bin = options.emit_bin orelse break :blk null; - - if (emit_bin.directory) |directory| { - break :blk link.Emit{ - .directory = directory, - .sub_path = emit_bin.basename, - }; - } - - // In case of whole cache mode, `whole_bin_sub_path` is used to distinguish - // between -femit-bin and -fno-emit-bin. - switch (cache_mode) { - .whole => break :blk null, - .incremental => {}, - } - - if (module) |zm| { - break :blk link.Emit{ - .directory = zm.zig_cache_artifact_directory, - .sub_path = emit_bin.basename, - }; - } - - // We could use the cache hash as is no problem, however, we increase - // the likelihood of cache hits by adding the first C source file - // path name (not contents) to the hash. This way if the user is compiling - // foo.c and bar.c as separate compilations, they get different cache - // directories. - var hash = cache.hash; - if (options.c_source_files.len >= 1) { - hash.addBytes(options.c_source_files[0].src_path); - } else if (options.link_objects.len >= 1) { - hash.addBytes(options.link_objects[0].path); - } - - const digest = hash.final(); - const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); - const artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); - owned_link_dir = artifact_dir; - const link_artifact_directory: Directory = .{ - .handle = artifact_dir, - .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}), - }; - break :blk link.Emit{ - .directory = link_artifact_directory, - .sub_path = emit_bin.basename, - }; - }; - - const implib_emit: ?link.Emit = blk: { - const emit_implib = options.emit_implib orelse break :blk null; - - if (emit_implib.directory) |directory| { - break :blk link.Emit{ - .directory = directory, - .sub_path = emit_implib.basename, - }; - } - - // This is here for the same reason as in `bin_file_emit` above. - switch (cache_mode) { - .whole => break :blk null, - .incremental => {}, - } - - // 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{ - .directory = bin_file_emit.?.directory, - .sub_path = emit_implib.basename, - }; - }; - - const docs_emit: ?link.Emit = blk: { - const emit_docs = options.emit_docs orelse break :blk null; - - if (emit_docs.directory) |directory| { - break :blk .{ - .directory = directory, - .sub_path = emit_docs.basename, - }; - } - - // This is here for the same reason as in `bin_file_emit` above. - switch (cache_mode) { - .whole => break :blk null, - .incremental => {}, - } - - // Use the same directory as the bin, if possible. - if (bin_file_emit) |x| break :blk .{ - .directory = x.directory, - .sub_path = emit_docs.basename, - }; - - break :blk .{ - .directory = module.?.zig_cache_artifact_directory, - .sub_path = emit_docs.basename, - }; - }; + errdefer if (opt_zcu) |zcu| zcu.deinit(); - // This is so that when doing `CacheMode.whole`, the mechanism in update() - // can use it for communicating the result directory via `bin_file.emit`. - // This is used to distinguish between -fno-emit-bin and -femit-bin - // for `CacheMode.whole`. - // This memory will be overwritten with the real digest in update() but - // the basename will be preserved. - const whole_bin_sub_path: ?[]u8 = try prepareWholeEmitSubPath(arena, options.emit_bin); - // Same thing but for implibs. - const whole_implib_sub_path: ?[]u8 = try prepareWholeEmitSubPath(arena, options.emit_implib); - const whole_docs_sub_path: ?[]u8 = try prepareWholeEmitSubPath(arena, options.emit_docs); - - var system_libs: std.StringArrayHashMapUnmanaged(SystemLib) = .{}; + var system_libs = try std.StringArrayHashMapUnmanaged(SystemLib).init( + gpa, + options.system_lib_names, + options.system_lib_infos, + ); errdefer system_libs.deinit(gpa); - try system_libs.ensureTotalCapacity(gpa, options.system_lib_names.len); - for (options.system_lib_names, 0..) |lib_name, i| { - 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, + comp.* = .{ + .gpa = gpa, + .arena = arena, + .module = opt_zcu, + .cache_use = undefined, // populated below + .bin_file = null, // populated below + .implib_emit = null, // handled below + .docs_emit = null, // handled below + .root_mod = options.root_mod, + .config = options.config, + .zig_lib_directory = options.zig_lib_directory, + .local_cache_directory = options.local_cache_directory, + .global_cache_directory = options.global_cache_directory, + .emit_asm = options.emit_asm, + .emit_llvm_ir = options.emit_llvm_ir, + .emit_llvm_bc = options.emit_llvm_bc, + .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa), + .anon_work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa), + .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa), + .win32_resource_work_queue = if (build_options.only_core_functionality) {} else std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa), + .astgen_work_queue = std.fifo.LinearFifo(*Module.File, .Dynamic).init(gpa), + .embed_file_work_queue = std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic).init(gpa), + .c_source_files = options.c_source_files, + .rc_source_files = options.rc_source_files, + .cache_parent = cache, + .self_exe_path = options.self_exe_path, + .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, + .thread_pool = options.thread_pool, + .clang_passthrough_mode = options.clang_passthrough_mode, + .clang_preprocessor_mode = options.clang_preprocessor_mode, + .verbose_cc = options.verbose_cc, + .verbose_air = options.verbose_air, + .verbose_intern_pool = options.verbose_intern_pool, + .verbose_generic_instances = options.verbose_generic_instances, + .verbose_llvm_ir = options.verbose_llvm_ir, + .verbose_llvm_bc = options.verbose_llvm_bc, + .verbose_cimport = options.verbose_cimport, + .verbose_llvm_cpu_features = options.verbose_llvm_cpu_features, + .verbose_link = options.verbose_link, + .disable_c_depfile = options.disable_c_depfile, + .reference_trace = options.reference_trace, + .formatted_panics = formatted_panics, + .time_report = options.time_report, + .stack_report = options.stack_report, + .test_filter = options.test_filter, + .test_name_prefix = options.test_name_prefix, + .debug_compiler_runtime_libs = options.debug_compiler_runtime_libs, + .debug_compile_errors = options.debug_compile_errors, + .libcxx_abi_version = options.libcxx_abi_version, .root_name = root_name, - .module = module, - .target = options.target, - .dynamic_linker = options.dynamic_linker, .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, + .system_libs = system_libs, + .version = options.version, + .libc_installation = libc_dirs.libc_installation, + .include_compiler_rt = include_compiler_rt, .objects = options.link_objects, - .frameworks = options.frameworks, .framework_dirs = options.framework_dirs, - .system_libs = system_libs, + .llvm_opt_bisect_limit = options.llvm_opt_bisect_limit, + .skip_linker_dependencies = options.skip_linker_dependencies, + .no_builtin = options.no_builtin, + .job_queued_update_builtin_zig = have_zcu, + .function_sections = options.function_sections, + .data_sections = options.data_sections, + .native_system_include_paths = options.native_system_include_paths, .wasi_emulated_libs = options.wasi_emulated_libs, + .force_undefined_symbols = options.force_undefined_symbols, + .link_eh_frame_hdr = link_eh_frame_hdr, + .global_cc_argv = options.global_cc_argv, + }; + + // Prevent some footguns by making the "any" fields of config reflect + // the default Module settings. + comp.config.any_unwind_tables = any_unwind_tables; + comp.config.any_non_single_threaded = any_non_single_threaded; + comp.config.any_sanitize_thread = any_sanitize_thread; + + const lf_open_opts: link.File.OpenOptions = .{ + .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, .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, + .stack_size = options.stack_size, + .image_base = options.image_base, .version_script = options.version_script, .gc_sections = options.linker_gc_sections, - .eh_frame_hdr = link_eh_frame_hdr, .emit_relocs = options.link_emit_relocs, - .rdynamic = options.rdynamic, .soname = options.soname, - .version = options.version, .compatibility_version = options.compatibility_version, - .libc_installation = libc_dirs.libc_installation, - .pic = pic, - .pie = pie, - .lto = lto, - .valgrind = valgrind, - .tsan = tsan, - .stack_check = stack_check, - .stack_protector = stack_protector, - .red_zone = red_zone, - .omit_frame_pointer = omit_frame_pointer, - .single_threaded = single_threaded, - .verbose_link = options.verbose_link, - .machine_code_model = options.machine_code_model, - .dll_export_fns = dll_export_fns, - .error_return_tracing = error_return_tracing, - .llvm_cpu_features = llvm_cpu_features, - .skip_linker_dependencies = options.skip_linker_dependencies, - .each_lib_rpath = options.each_lib_rpath orelse options.is_native_os, + .each_lib_rpath = each_lib_rpath, .build_id = build_id, - .cache_mode = cache_mode, - .disable_lld_caching = options.disable_lld_caching or cache_mode == .whole, + .disable_lld_caching = options.disable_lld_caching or options.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, @@ -1943,76 +1582,125 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .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(); - comp.* = .{ - .gpa = gpa, - .arena = arena_allocator, - .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, - .emit_asm = options.emit_asm, - .emit_llvm_ir = options.emit_llvm_ir, - .emit_llvm_bc = options.emit_llvm_bc, - .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa), - .anon_work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa), - .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa), - .win32_resource_work_queue = if (build_options.only_core_functionality) {} else std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa), - .astgen_work_queue = std.fifo.LinearFifo(*Module.File, .Dynamic).init(gpa), - .embed_file_work_queue = std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic).init(gpa), - .keep_source_files_loaded = options.keep_source_files_loaded, - .c_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, - .self_exe_path = options.self_exe_path, - .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, - .verbose_cc = options.verbose_cc, - .verbose_air = options.verbose_air, - .verbose_intern_pool = options.verbose_intern_pool, - .verbose_generic_instances = options.verbose_generic_instances, - .verbose_llvm_ir = options.verbose_llvm_ir, - .verbose_llvm_bc = options.verbose_llvm_bc, - .verbose_cimport = options.verbose_cimport, - .verbose_llvm_cpu_features = options.verbose_llvm_cpu_features, - .disable_c_depfile = options.disable_c_depfile, - .owned_link_dir = owned_link_dir, - .color = options.color, - .reference_trace = options.reference_trace, - .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, + .entry_addr = null, // CLI does not expose this option (yet?) }; + + switch (options.cache_mode) { + .incremental => { + // Options that are specific to zig source files, that cannot be + // modified between incremental updates. + var hash = cache.hash; + + // Synchronize with other matching comments: ZigOnlyHashStuff + hash.add(use_llvm); + hash.add(options.config.use_lib_llvm); + hash.add(options.config.dll_export_fns); + 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); + hash.add(formatted_panics); + hash.add(options.emit_h != null); + hash.add(error_limit); + + // Here we put the root source file path name, but *not* with addFile. + // We want the hash to be the same regardless of the contents of the + // source file, because incremental compilation will handle it, but we + // do want to namespace different source file names because they are + // likely different compilations and therefore this would be likely to + // cause cache hits. + try addModuleTableToCacheHash(gpa, arena, &hash, main_mod, .path_bytes); + + // In the case of incremental cache mode, this `artifact_directory` + // is computed based on a hash of non-linker inputs, and it is where all + // build artifacts are stored (even while in-progress). + const digest = hash.final(); + const artifact_sub_dir = "o" ++ std.fs.path.sep_str ++ digest; + var artifact_dir = try options.local_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); + errdefer artifact_dir.close(); + const artifact_directory: Directory = .{ + .handle = artifact_dir, + .path = try options.local_cache_directory.join(arena, &[_][]const u8{artifact_sub_dir}), + }; + + const incremental = try arena.create(CacheUse.Incremental); + incremental.* = .{ + .artifact_directory = artifact_directory, + }; + comp.cache_use = .{ .incremental = incremental }; + + if (options.emit_bin) |emit_bin| { + const emit: Emit = .{ + .directory = emit_bin.directory orelse artifact_directory, + .sub_path = emit_bin.basename, + }; + comp.bin_file = try link.File.open(arena, comp, emit, lf_open_opts); + } + + if (options.emit_implib) |emit_implib| { + comp.implib_emit = .{ + .directory = emit_implib.directory orelse artifact_directory, + .sub_path = emit_implib.basename, + }; + } + + if (options.emit_docs) |emit_docs| { + comp.docs_emit = .{ + .directory = emit_docs.directory orelse artifact_directory, + .sub_path = emit_docs.basename, + }; + } + }, + .whole => { + // For whole cache mode, we don't know where to put outputs from + // the linker until the final cache hash, which is available after + // the compilation is complete. + // + // Therefore, bin_file is left null until the beginning of update(), + // where it may find a cache hit, or use a temporary directory to + // hold output artifacts. + const whole = try arena.create(CacheUse.Whole); + whole.* = .{ + // This is kept here so that link.File.open can be called later. + .lf_open_opts = lf_open_opts, + // This is so that when doing `CacheMode.whole`, the mechanism in update() + // can use it for communicating the result directory via `bin_file.emit`. + // This is used to distinguish between -fno-emit-bin and -femit-bin + // for `CacheMode.whole`. + // This memory will be overwritten with the real digest in update() but + // the basename will be preserved. + .bin_sub_path = try prepareWholeEmitSubPath(arena, options.emit_bin), + .implib_sub_path = try prepareWholeEmitSubPath(arena, options.emit_implib), + .docs_sub_path = try prepareWholeEmitSubPath(arena, options.emit_docs), + .tmp_artifact_directory = null, + .lock = null, + }; + comp.cache_use = .{ .whole = whole }; + }, + } + + // Handle the case of e.g. -fno-emit-bin -femit-llvm-ir. + if (options.emit_bin == null and (comp.verbose_llvm_ir != null or + comp.verbose_llvm_bc != null or + (use_llvm and comp.emit_asm != null) or + comp.emit_llvm_ir != null or + comp.emit_llvm_bc != null)) + { + if (build_options.only_c) unreachable; + if (opt_zcu) |zcu| zcu.llvm_object = try LlvmObject.create(arena, comp); + } + break :comp comp; }; errdefer comp.destroy(); - const target = comp.getTarget(); + const target = comp.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, comp.config.use_llvm); + const capable_of_building_zig_libc = canBuildZigLibC(target, comp.config.use_llvm); // Add a `CObject` for each `c_source_files`. try comp.c_object_table.ensureTotalCapacity(gpa, options.c_source_files.len); @@ -2052,9 +1740,12 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { } } - const have_bin_emit = comp.bin_file.options.emit != null or comp.whole_bin_sub_path != null; + const have_bin_emit = switch (comp.cache_use) { + .whole => |whole| whole.bin_sub_path != null, + .incremental => comp.bin_file != null, + }; - if (have_bin_emit and !comp.bin_file.options.skip_linker_dependencies and target.ofmt != .c) { + if (have_bin_emit and !comp.skip_linker_dependencies and target.ofmt != .c) { if (target.isDarwin()) { switch (target.abi) { .none, @@ -2095,27 +1786,30 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .{ .musl_crt_file = .crt1_o }, .{ .musl_crt_file = .scrt1_o }, .{ .musl_crt_file = .rcrt1_o }, - switch (comp.bin_file.options.link_mode) { + switch (comp.config.link_mode) { .Static => .{ .musl_crt_file = .libc_a }, .Dynamic => .{ .musl_crt_file = .libc_so }, }, }); } + if (comp.wantBuildWasiLibcFromSource()) { if (!target_util.canBuildLibC(target)) return error.LibCUnavailable; - const wasi_emulated_libs = comp.bin_file.options.wasi_emulated_libs; - try comp.work_queue.ensureUnusedCapacity(wasi_emulated_libs.len + 2); // worst-case we need all components - for (wasi_emulated_libs) |crt_file| { + // worst-case we need all components + try comp.work_queue.ensureUnusedCapacity(comp.wasi_emulated_libs.len + 2); + + for (comp.wasi_emulated_libs) |crt_file| { comp.work_queue.writeItemAssumeCapacity(.{ .wasi_libc_crt_file = crt_file, }); } comp.work_queue.writeAssumeCapacity(&[_]Job{ - .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(wasi_exec_model) }, + .{ .wasi_libc_crt_file = wasi_libc.execModelCrtFile(comp.config.wasi_exec_model) }, .{ .wasi_libc_crt_file = .libc_a }, }); } + if (comp.wantBuildMinGWFromSource()) { if (!target_util.canBuildLibC(target)) return error.LibCUnavailable; @@ -2132,7 +1826,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { // When linking mingw-w64 there are some import libs we always need. for (mingw.always_link_libs) |name| { - try comp.bin_file.options.system_libs.put(comp.gpa, name, .{ + try comp.system_libs.put(comp.gpa, name, .{ .needed = false, .weak = false, .path = null, @@ -2141,7 +1835,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { } // Generate Windows import libs. if (target.os.tag == .windows) { - const count = comp.bin_file.options.system_libs.count(); + const count = comp.system_libs.count(); try comp.work_queue.ensureUnusedCapacity(count); for (0..count) |i| { comp.work_queue.writeItemAssumeCapacity(.{ .windows_import_lib = i }); @@ -2150,31 +1844,31 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { if (comp.wantBuildLibUnwindFromSource()) { try comp.work_queue.writeItem(.{ .libunwind = {} }); } - if (build_options.have_llvm and is_exe_or_dyn_lib and comp.bin_file.options.link_libcpp) { + if (build_options.have_llvm and is_exe_or_dyn_lib and comp.config.link_libcpp) { try comp.work_queue.writeItem(.libcxx); try comp.work_queue.writeItem(.libcxxabi); } - if (build_options.have_llvm and comp.bin_file.options.tsan) { + if (build_options.have_llvm and comp.config.any_sanitize_thread) { try comp.work_queue.writeItem(.libtsan); } - if (comp.getTarget().isMinGW() and !comp.bin_file.options.single_threaded) { + if (target.isMinGW() and comp.config.any_non_single_threaded) { // LLD might drop some symbols as unused during LTO and GCing, therefore, // we force mark them for resolution here. - const tls_index_sym = switch (comp.getTarget().cpu.arch) { + const tls_index_sym = switch (target.cpu.arch) { .x86 => "__tls_index", else => "_tls_index", }; - try comp.bin_file.options.force_undefined_symbols.put(comp.gpa, tls_index_sym, {}); + try comp.force_undefined_symbols.put(comp.gpa, tls_index_sym, {}); } - if (comp.bin_file.options.include_compiler_rt and capable_of_building_compiler_rt) { + if (comp.include_compiler_rt and capable_of_building_compiler_rt) { if (is_exe_or_dyn_lib) { log.debug("queuing a job to build compiler_rt_lib", .{}); comp.job_queued_compiler_rt_lib = true; - } 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. @@ -2182,8 +1876,8 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { } } - if (!comp.bin_file.options.skip_linker_dependencies and is_exe_or_dyn_lib and - !comp.bin_file.options.link_libc and capable_of_building_zig_libc) + if (!comp.skip_linker_dependencies and is_exe_or_dyn_lib and + !comp.config.link_libc and capable_of_building_zig_libc) { try comp.work_queue.writeItem(.{ .zig_libc = {} }); } @@ -2192,88 +1886,87 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { return comp; } -pub fn destroy(self: *Compilation) void { - const optional_module = self.bin_file.options.module; - self.bin_file.destroy(); - if (optional_module) |module| module.deinit(); - - const gpa = self.gpa; - self.work_queue.deinit(); - self.anon_work_queue.deinit(); - self.c_object_work_queue.deinit(); +pub fn destroy(comp: *Compilation) void { + if (comp.bin_file) |lf| lf.destroy(); + if (comp.module) |zcu| zcu.deinit(); + comp.cache_use.deinit(); + comp.work_queue.deinit(); + comp.anon_work_queue.deinit(); + comp.c_object_work_queue.deinit(); if (!build_options.only_core_functionality) { - self.win32_resource_work_queue.deinit(); + comp.win32_resource_work_queue.deinit(); } - self.astgen_work_queue.deinit(); - self.embed_file_work_queue.deinit(); + comp.astgen_work_queue.deinit(); + comp.embed_file_work_queue.deinit(); + + const gpa = comp.gpa; + comp.system_libs.deinit(gpa); { - var it = self.crt_files.iterator(); + var it = comp.crt_files.iterator(); while (it.next()) |entry| { gpa.free(entry.key_ptr.*); entry.value_ptr.deinit(gpa); } - self.crt_files.deinit(gpa); + comp.crt_files.deinit(gpa); } - if (self.libunwind_static_lib) |*crt_file| { + if (comp.libunwind_static_lib) |*crt_file| { crt_file.deinit(gpa); } - if (self.libcxx_static_lib) |*crt_file| { + if (comp.libcxx_static_lib) |*crt_file| { crt_file.deinit(gpa); } - if (self.libcxxabi_static_lib) |*crt_file| { + if (comp.libcxxabi_static_lib) |*crt_file| { crt_file.deinit(gpa); } - if (self.compiler_rt_lib) |*crt_file| { + if (comp.compiler_rt_lib) |*crt_file| { crt_file.deinit(gpa); } - if (self.compiler_rt_obj) |*crt_file| { + if (comp.compiler_rt_obj) |*crt_file| { crt_file.deinit(gpa); } - if (self.libc_static_lib) |*crt_file| { + if (comp.libc_static_lib) |*crt_file| { crt_file.deinit(gpa); } - if (self.glibc_so_files) |*glibc_file| { + if (comp.glibc_so_files) |*glibc_file| { glibc_file.deinit(gpa); } - for (self.c_object_table.keys()) |key| { + for (comp.c_object_table.keys()) |key| { key.destroy(gpa); } - self.c_object_table.deinit(gpa); + comp.c_object_table.deinit(gpa); - for (self.failed_c_objects.values()) |bundle| { + for (comp.failed_c_objects.values()) |bundle| { bundle.destroy(gpa); } - self.failed_c_objects.deinit(gpa); + comp.failed_c_objects.deinit(gpa); if (!build_options.only_core_functionality) { - for (self.win32_resource_table.keys()) |key| { + for (comp.win32_resource_table.keys()) |key| { key.destroy(gpa); } - self.win32_resource_table.deinit(gpa); + comp.win32_resource_table.deinit(gpa); - for (self.failed_win32_resources.values()) |*value| { + for (comp.failed_win32_resources.values()) |*value| { value.deinit(gpa); } - self.failed_win32_resources.deinit(gpa); + comp.failed_win32_resources.deinit(gpa); } - for (self.lld_errors.items) |*lld_error| { + for (comp.link_errors.items) |*item| item.deinit(gpa); + comp.link_errors.deinit(gpa); + + for (comp.lld_errors.items) |*lld_error| { lld_error.deinit(gpa); } - self.lld_errors.deinit(gpa); - - self.clearMiscFailures(); + comp.lld_errors.deinit(gpa); - self.cache_parent.manifest_dir.close(); - if (self.owned_link_dir) |*dir| dir.close(); + comp.clearMiscFailures(); - // This destroys `self`. - var arena_instance = self.arena; - arena_instance.deinit(); + comp.cache_parent.manifest_dir.close(); } pub fn clearMiscFailures(comp: *Compilation) void { @@ -2286,135 +1979,142 @@ 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 { - if (directory.path) |p| comp.gpa.free(p); - - // Restore the Module's previous zig_cache_artifact_directory - // This is only for cleanup purposes; Module.deinit calls close - // on the handle of zig_cache_artifact_directory. - if (comp.bin_file.options.module) |module| { - const builtin_mod = module.main_mod.deps.get("builtin").?; - module.zig_cache_artifact_directory = builtin_mod.root.root_dir; - } +/// Only legal to call when cache mode is incremental and a link file is present. +pub fn hotCodeSwap( + comp: *Compilation, + prog_node: *std.Progress.Node, + pid: std.ChildProcess.Id, +) !void { + const lf = comp.bin_file.?; + lf.child_pid = pid; + try lf.makeWritable(); + try comp.update(prog_node); + try lf.makeExecutable(); } -fn cleanupTmpArtifactDirectory( - comp: *Compilation, - tmp_artifact_directory: *?Directory, - tmp_dir_sub_path: []const u8, -) void { - comp.gpa.free(tmp_dir_sub_path); - if (tmp_artifact_directory.*) |*directory| { - directory.handle.close(); - restorePrevZigCacheArtifactDirectory(comp, directory); +fn cleanupAfterUpdate(comp: *Compilation) void { + switch (comp.cache_use) { + .incremental => return, + .whole => |whole| { + if (whole.cache_manifest) |man| { + man.deinit(); + whole.cache_manifest = null; + } + if (comp.bin_file) |lf| { + lf.destroy(); + comp.bin_file = null; + } + if (whole.tmp_artifact_directory) |*directory| { + directory.handle.close(); + if (directory.path) |p| comp.gpa.free(p); + whole.tmp_artifact_directory = null; + } + }, } } -pub fn hotCodeSwap(comp: *Compilation, prog_node: *std.Progress.Node, pid: std.ChildProcess.Id) !void { - comp.bin_file.child_pid = pid; - try comp.makeBinFileWritable(); - try comp.update(prog_node); - try comp.makeBinFileExecutable(); -} - /// Detect changes to source files, perform semantic analysis, and update the output files. pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void { const tracy_trace = trace(@src()); defer tracy_trace.end(); + // This arena is scoped to this one update. + const gpa = comp.gpa; + var arena_allocator = std.heap.ArenaAllocator.init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + comp.clearMiscFailures(); comp.last_update_was_cache_hit = false; var man: Cache.Manifest = undefined; - defer if (comp.whole_cache_manifest != null) man.deinit(); + defer cleanupAfterUpdate(comp); - var tmp_dir_sub_path: []const u8 = &.{}; - var tmp_artifact_directory: ?Directory = null; - defer cleanupTmpArtifactDirectory(comp, &tmp_artifact_directory, tmp_dir_sub_path); + var tmp_dir_rand_int: u64 = undefined; // If using the whole caching strategy, we check for *everything* up front, including // C source files. - if (comp.bin_file.options.cache_mode == .whole) { - // We are about to obtain this lock, so here we give other processes a chance first. - comp.bin_file.releaseLock(); - - man = comp.cache_parent.obtain(); - comp.whole_cache_manifest = &man; - try comp.addNonIncrementalStuffToCacheManifest(&man); - - const is_hit = man.hit() catch |err| { - // TODO properly bubble these up instead of emitting a warning - const i = man.failed_file_index orelse return err; - const pp = man.files.items[i].prefixed_path orelse return err; - const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; - std.log.warn("{s}: {s}{s}", .{ @errorName(err), prefix, pp.sub_path }); - return err; - }; - if (is_hit) { - comp.last_update_was_cache_hit = true; - log.debug("CacheMode.whole cache hit for {s}", .{comp.bin_file.options.root_name}); - const digest = man.final(); - - comp.wholeCacheModeSetBinFilePath(&digest); + switch (comp.cache_use) { + .whole => |whole| { + assert(comp.bin_file == null); + // We are about to obtain this lock, so here we give other processes a chance first. + whole.releaseLock(); + + man = comp.cache_parent.obtain(); + whole.cache_manifest = &man; + try addNonIncrementalStuffToCacheManifest(comp, arena, &man); + + const is_hit = man.hit() catch |err| { + const i = man.failed_file_index orelse return err; + const pp = man.files.items[i].prefixed_path orelse return err; + const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; + return comp.setMiscFailure( + .check_whole_cache, + "unable to check cache: stat file '{}{s}{s}' failed: {s}", + .{ comp.local_cache_directory, prefix, pp.sub_path, @errorName(err) }, + ); + }; + if (is_hit) { + comp.last_update_was_cache_hit = true; + log.debug("CacheMode.whole cache hit for {s}", .{comp.root_name}); + const digest = man.final(); - assert(comp.bin_file.lock == null); - comp.bin_file.lock = man.toOwnedLock(); - return; - } - log.debug("CacheMode.whole cache miss for {s}", .{comp.bin_file.options.root_name}); + comp.wholeCacheModeSetBinFilePath(whole, &digest); - // Initialize `bin_file.emit` with a temporary Directory so that compilation can - // continue on the same path as incremental, using the temporary Directory. - tmp_artifact_directory = d: { - const s = std.fs.path.sep_str; - const rand_int = std.crypto.random.int(u64); + assert(whole.lock == null); + whole.lock = man.toOwnedLock(); + return; + } + log.debug("CacheMode.whole cache miss for {s}", .{comp.root_name}); - tmp_dir_sub_path = try std.fmt.allocPrint(comp.gpa, "tmp" ++ s ++ "{x}", .{rand_int}); + // Compile the artifacts to a temporary directory. + const tmp_artifact_directory = d: { + const s = std.fs.path.sep_str; + tmp_dir_rand_int = std.crypto.random.int(u64); + const tmp_dir_sub_path = "tmp" ++ s ++ Package.Manifest.hex64(tmp_dir_rand_int); - const path = try comp.local_cache_directory.join(comp.gpa, &.{tmp_dir_sub_path}); - errdefer comp.gpa.free(path); + const path = try comp.local_cache_directory.join(gpa, &.{tmp_dir_sub_path}); + errdefer gpa.free(path); - const handle = try comp.local_cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{}); - errdefer handle.close(); + const handle = try comp.local_cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{}); + errdefer handle.close(); - break :d .{ - .path = path, - .handle = handle, + break :d .{ + .path = path, + .handle = handle, + }; }; - }; + whole.tmp_artifact_directory = tmp_artifact_directory; - // This updates the output directory for linker outputs. - if (comp.bin_file.options.module) |module| { - module.zig_cache_artifact_directory = tmp_artifact_directory.?; - } + // Now that the directory is known, it is time to create the Emit + // objects and call link.File.open. - // This resets the link.File to operate as if we called openPath() in create() - // instead of simulating -fno-emit-bin. - var options = comp.bin_file.options.move(); - if (comp.whole_bin_sub_path) |sub_path| { - options.emit = .{ - .directory = tmp_artifact_directory.?, - .sub_path = std.fs.path.basename(sub_path), - }; - } - if (comp.whole_implib_sub_path) |sub_path| { - options.implib_emit = .{ - .directory = tmp_artifact_directory.?, - .sub_path = std.fs.path.basename(sub_path), - }; - } - if (comp.whole_docs_sub_path) |sub_path| { - options.docs_emit = .{ - .directory = tmp_artifact_directory.?, - .sub_path = std.fs.path.basename(sub_path), - }; - } - var old_bin_file = comp.bin_file; - comp.bin_file = try link.File.openPath(comp.gpa, options); - old_bin_file.destroy(); + if (whole.implib_sub_path) |sub_path| { + comp.implib_emit = .{ + .directory = tmp_artifact_directory, + .sub_path = std.fs.path.basename(sub_path), + }; + } + + if (whole.docs_sub_path) |sub_path| { + comp.docs_emit = .{ + .directory = tmp_artifact_directory, + .sub_path = std.fs.path.basename(sub_path), + }; + } + + if (whole.bin_sub_path) |sub_path| { + const emit: Emit = .{ + .directory = tmp_artifact_directory, + .sub_path = std.fs.path.basename(sub_path), + }; + comp.bin_file = try link.File.createEmpty(arena, comp, emit, whole.lf_open_opts); + } + }, + .incremental => {}, } // For compiling C objects, we rely on the cache hash system to avoid duplicating work. @@ -2433,13 +2133,13 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void } } - if (comp.bin_file.options.module) |module| { - module.compile_log_text.shrinkAndFree(module.gpa, 0); + if (comp.module) |module| { + module.compile_log_text.shrinkAndFree(gpa, 0); module.generation += 1; // 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 @@ -2448,22 +2148,21 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void // import_table here. // Likewise, in the case of `zig test`, the test runner is the root source file, // and so there is nothing to import the main file. - if (comp.bin_file.options.is_test) { + if (comp.config.is_test) { _ = 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); } // Put a work item in for every known source file to detect if // it changed, and, if so, re-compute ZIR and then queue the job // to update it. - // We still want AstGen work items for stage1 so that we expose compile errors - // that are implemented in stage2 but not stage1. try comp.astgen_work_queue.ensureUnusedCapacity(module.import_table.count()); - for (module.import_table.values()) |value| { - comp.astgen_work_queue.writeItemAssumeCapacity(value); + for (module.import_table.values()) |file| { + if (file.mod.isBuiltin()) continue; + comp.astgen_work_queue.writeItemAssumeCapacity(file); } // Put a work item in for checking if any files used with `@embedFile` changed. @@ -2473,34 +2172,34 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void } try comp.work_queue.writeItem(.{ .analyze_mod = std_mod }); - if (comp.bin_file.options.is_test) { + if (comp.config.is_test) { 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 }); } } try comp.performAllTheWork(main_progress_node); - if (comp.bin_file.options.module) |module| { + if (comp.module) |module| { if (builtin.mode == .Debug and comp.verbose_intern_pool) { std.debug.print("intern pool stats for '{s}':\n", .{ - comp.bin_file.options.root_name, + comp.root_name, }); module.intern_pool.dump(); } if (builtin.mode == .Debug and comp.verbose_generic_instances) { std.debug.print("generic instances for '{s}:0x{x}':\n", .{ - comp.bin_file.options.root_name, + comp.root_name, @as(usize, @intFromPtr(module)), }); - module.intern_pool.dumpGenericInstances(comp.gpa); + module.intern_pool.dumpGenericInstances(gpa); } - if (comp.bin_file.options.is_test and comp.totalErrorCount() == 0) { + if (comp.config.is_test and comp.totalErrorCount() == 0) { // The `test_functions` decl has been intentionally postponed until now, // at which point we must populate it with the list of test functions that // have been discovered and not filtered out. @@ -2516,106 +2215,188 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void return; } - // Flush takes care of -femit-bin, but we still have -femit-llvm-ir, -femit-llvm-bc, and - // -femit-asm to handle, in the case of C objects. + // Flush below handles -femit-bin but there is still -femit-llvm-ir, + // -femit-llvm-bc, and -femit-asm, in the case of C objects. comp.emitOthers(); - if (comp.whole_cache_manifest != null) { - const digest = man.final(); + switch (comp.cache_use) { + .whole => |whole| { + const digest = man.final(); - // Rename the temporary directory into place. - var directory = tmp_artifact_directory.?; - tmp_artifact_directory = null; - - directory.handle.close(); - defer restorePrevZigCacheArtifactDirectory(comp, &directory); - - const o_sub_path = try std.fs.path.join(comp.gpa, &[_][]const u8{ "o", &digest }); - defer comp.gpa.free(o_sub_path); - - // Work around windows `AccessDenied` if any files within this directory are open - // by closing and reopening the file handles. - const need_writable_dance = builtin.os.tag == .windows and comp.bin_file.file != null; - if (need_writable_dance) { - // We cannot just call `makeExecutable` as it makes a false assumption that we have a - // file handle open only when linking an executable file. This used to be true when - // our linkers were incapable of emitting relocatables and static archive. Now that - // they are capable, we need to unconditionally close the file handle and re-open it - // in the follow up call to `makeWritable`. - comp.bin_file.file.?.close(); - comp.bin_file.file = null; - } + // Rename the temporary directory into place. + // Close tmp dir and link.File to avoid open handle during rename. + if (whole.tmp_artifact_directory) |*tmp_directory| { + tmp_directory.handle.close(); + if (tmp_directory.path) |p| gpa.free(p); + whole.tmp_artifact_directory = null; + } else unreachable; - try comp.bin_file.renameTmpIntoCache(comp.local_cache_directory, tmp_dir_sub_path, o_sub_path); - comp.wholeCacheModeSetBinFilePath(&digest); + const s = std.fs.path.sep_str; + const tmp_dir_sub_path = "tmp" ++ s ++ Package.Manifest.hex64(tmp_dir_rand_int); + const o_sub_path = "o" ++ s ++ digest; + + // Work around windows `AccessDenied` if any files within this + // directory are open by closing and reopening the file handles. + const need_writable_dance = w: { + if (builtin.os.tag == .windows) { + if (comp.bin_file) |lf| { + // We cannot just call `makeExecutable` as it makes a false + // assumption that we have a file handle open only when linking + // an executable file. This used to be true when our linkers + // were incapable of emitting relocatables and static archive. + // Now that they are capable, we need to unconditionally close + // the file handle and re-open it in the follow up call to + // `makeWritable`. + if (lf.file) |f| { + f.close(); + lf.file = null; + break :w true; + } + } + } + break :w false; + }; - // Has to be after the `wholeCacheModeSetBinFilePath` above. - if (need_writable_dance) { - try comp.bin_file.makeWritable(); - } + renameTmpIntoCache(comp.local_cache_directory, tmp_dir_sub_path, o_sub_path) catch |err| { + return comp.setMiscFailure( + .rename_results, + "failed to rename compilation results ('{}{s}') into local cache ('{}{s}'): {s}", + .{ + comp.local_cache_directory, tmp_dir_sub_path, + comp.local_cache_directory, o_sub_path, + @errorName(err), + }, + ); + }; + comp.wholeCacheModeSetBinFilePath(whole, &digest); + + // The linker flush functions need to know the final output path + // for debug info purposes because executable debug info contains + // references object file paths. + if (comp.bin_file) |lf| { + lf.emit = .{ + .directory = comp.local_cache_directory, + .sub_path = whole.bin_sub_path.?, + }; - // This is intentionally sandwiched between renameTmpIntoCache() and writeManifest(). - if (comp.bin_file.options.module) |module| { - // We need to set the zig_cache_artifact_directory for -femit-asm, -femit-llvm-ir, - // etc to know where to output to. - var artifact_dir = try comp.local_cache_directory.handle.openDir(o_sub_path, .{}); - defer artifact_dir.close(); + // Has to be after the `wholeCacheModeSetBinFilePath` above. + if (need_writable_dance) { + try lf.makeWritable(); + } + } - const dir_path = try comp.local_cache_directory.join(comp.gpa, &.{o_sub_path}); - defer comp.gpa.free(dir_path); + try flush(comp, arena, main_progress_node); + if (comp.totalErrorCount() != 0) return; - module.zig_cache_artifact_directory = .{ - .handle = artifact_dir, - .path = dir_path, + // Failure here only means an unnecessary cache miss. + man.writeManifest() catch |err| { + log.warn("failed to write cache manifest: {s}", .{@errorName(err)}); }; - try comp.flush(main_progress_node); - if (comp.totalErrorCount() != 0) return; + if (comp.bin_file) |lf| { + lf.destroy(); + comp.bin_file = null; + } - // Note the placement of this logic is relying on the call to - // `wholeCacheModeSetBinFilePath` above. - try maybeGenerateAutodocs(comp, main_progress_node); - } else { - try comp.flush(main_progress_node); + assert(whole.lock == null); + whole.lock = man.toOwnedLock(); + }, + .incremental => { + try flush(comp, arena, main_progress_node); if (comp.totalErrorCount() != 0) return; - } + }, + } +} - // Failure here only means an unnecessary cache miss. - man.writeManifest() catch |err| { - log.warn("failed to write cache manifest: {s}", .{@errorName(err)}); +fn flush(comp: *Compilation, arena: Allocator, prog_node: *std.Progress.Node) !void { + if (comp.bin_file) |lf| { + // This is needed before reading the error flags. + lf.flush(arena, prog_node) catch |err| switch (err) { + error.FlushFailure => {}, // error reported through link_error_flags + error.LLDReportedFailure => {}, // error reported via lockAndParseLldStderr + else => |e| return e, }; + } - assert(comp.bin_file.lock == null); - comp.bin_file.lock = man.toOwnedLock(); - } else { - try comp.flush(main_progress_node); + if (comp.module) |zcu| { + try link.File.C.flushEmitH(zcu); - if (comp.totalErrorCount() == 0) { - try maybeGenerateAutodocs(comp, main_progress_node); + if (zcu.llvm_object) |llvm_object| { + if (build_options.only_c) unreachable; + const default_emit = switch (comp.cache_use) { + .whole => |whole| .{ + .directory = whole.tmp_artifact_directory.?, + .sub_path = "dummy", + }, + .incremental => |incremental| .{ + .directory = incremental.artifact_directory, + .sub_path = "dummy", + }, + }; + try emitLlvmObject(comp, arena, default_emit, null, llvm_object, prog_node); } } - // Unload all source files to save memory. - // The ZIR needs to stay loaded in memory because (1) Decl objects contain references - // to it, and (2) generic instantiations, comptime calls, inline calls will need - // to reference the ZIR. - if (!comp.keep_source_files_loaded) { - if (comp.bin_file.options.module) |module| { - for (module.import_table.values()) |file| { - file.unloadTree(comp.gpa); - file.unloadSource(comp.gpa); - } - } + if (comp.totalErrorCount() == 0) { + try maybeGenerateAutodocs(comp, prog_node); + } +} + +/// This function is called by the frontend before flush(). It communicates that +/// `options.bin_file.emit` directory needs to be renamed from +/// `[zig-cache]/tmp/[random]` to `[zig-cache]/o/[digest]`. +/// The frontend would like to simply perform a file system rename, however, +/// some linker backends care about the file paths of the objects they are linking. +/// So this function call tells linker backends to rename the paths of object files +/// to observe the new directory path. +/// Linker backends which do not have this requirement can fall back to the simple +/// implementation at the bottom of this function. +/// This function is only called when CacheMode is `whole`. +fn renameTmpIntoCache( + cache_directory: Compilation.Directory, + tmp_dir_sub_path: []const u8, + o_sub_path: []const u8, +) !void { + var seen_eaccess = false; + while (true) { + std.fs.rename( + cache_directory.handle, + tmp_dir_sub_path, + cache_directory.handle, + o_sub_path, + ) catch |err| switch (err) { + // On Windows, rename fails with `AccessDenied` rather than `PathAlreadyExists`. + // See https://github.com/ziglang/zig/issues/8362 + error.AccessDenied => switch (builtin.os.tag) { + .windows => { + if (!seen_eaccess) return error.AccessDenied; + seen_eaccess = true; + try cache_directory.handle.deleteTree(o_sub_path); + continue; + }, + else => return error.AccessDenied, + }, + error.PathAlreadyExists => { + try cache_directory.handle.deleteTree(o_sub_path); + continue; + }, + error.FileNotFound => { + try cache_directory.handle.makePath("o"); + continue; + }, + else => |e| return e, + }; + break; } } fn maybeGenerateAutodocs(comp: *Compilation, prog_node: *std.Progress.Node) !void { - const mod = comp.bin_file.options.module orelse return; + const mod = comp.module orelse return; // TODO: do this in a separate job during performAllTheWork(). The // file copies at the end of generate() can also be extracted to // separate jobs if (!build_options.only_c and !build_options.only_core_functionality) { - if (comp.bin_file.options.docs_emit) |emit| { + if (comp.docs_emit) |emit| { var dir = try emit.directory.handle.makeOpenPath(emit.sub_path, .{}); defer dir.close(); @@ -2629,46 +2410,31 @@ fn maybeGenerateAutodocs(comp: *Compilation, prog_node: *std.Progress.Node) !voi } } -fn flush(comp: *Compilation, prog_node: *std.Progress.Node) !void { - // This is needed before reading the error flags. - comp.bin_file.flush(comp, prog_node) catch |err| switch (err) { - error.FlushFailure => {}, // error reported through link_error_flags - error.LLDReportedFailure => {}, // error reported via lockAndParseLldStderr - else => |e| return e, - }; - comp.link_error_flags = comp.bin_file.errorFlags(); - - if (comp.bin_file.options.module) |module| { - try link.File.C.flushEmitH(module); - } -} - /// Communicate the output binary location to parent Compilations. -fn wholeCacheModeSetBinFilePath(comp: *Compilation, digest: *const [Cache.hex_digest_len]u8) void { +fn wholeCacheModeSetBinFilePath( + comp: *Compilation, + whole: *CacheUse.Whole, + digest: *const [Cache.hex_digest_len]u8, +) void { const digest_start = 2; // "o/[digest]/[basename]" - if (comp.whole_bin_sub_path) |sub_path| { + if (whole.bin_sub_path) |sub_path| { @memcpy(sub_path[digest_start..][0..digest.len], digest); - - comp.bin_file.options.emit = .{ - .directory = comp.local_cache_directory, - .sub_path = sub_path, - }; } - if (comp.whole_implib_sub_path) |sub_path| { + if (whole.implib_sub_path) |sub_path| { @memcpy(sub_path[digest_start..][0..digest.len], digest); - comp.bin_file.options.implib_emit = .{ + comp.implib_emit = .{ .directory = comp.local_cache_directory, .sub_path = sub_path, }; } - if (comp.whole_docs_sub_path) |sub_path| { + if (whole.docs_sub_path) |sub_path| { @memcpy(sub_path[digest_start..][0..digest.len], digest); - comp.bin_file.options.docs_emit = .{ + comp.docs_emit = .{ .directory = comp.local_cache_directory, .sub_path = sub_path, }; @@ -2689,51 +2455,33 @@ fn prepareWholeEmitSubPath(arena: Allocator, opt_emit: ?EmitLoc) error{OutOfMemo /// anything from the link cache manifest. pub const link_hash_implementation_version = 10; -fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifest) !void { +fn addNonIncrementalStuffToCacheManifest( + comp: *Compilation, + arena: Allocator, + man: *Cache.Manifest, +) !void { const gpa = comp.gpa; - const target = comp.getTarget(); - - var arena_allocator = std.heap.ArenaAllocator.init(gpa); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); comptime assert(link_hash_implementation_version == 10); - if (comp.bin_file.options.module) |mod| { + if (comp.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); - man.hash.add(comp.bin_file.options.single_threaded); - man.hash.add(comp.bin_file.options.use_llvm); - man.hash.add(comp.bin_file.options.use_lib_llvm); - man.hash.add(comp.bin_file.options.dll_export_fns); - man.hash.add(comp.bin_file.options.is_test); - man.hash.add(comp.test_evented_io); + man.hash.add(comp.config.test_evented_io); man.hash.addOptionalBytes(comp.test_filter); man.hash.addOptionalBytes(comp.test_name_prefix); - man.hash.add(comp.bin_file.options.skip_linker_dependencies); + man.hash.add(comp.skip_linker_dependencies); man.hash.add(comp.formatted_panics); man.hash.add(mod.emit_h != null); man.hash.add(mod.error_limit); - man.hash.addOptional(comp.bin_file.options.want_structured_cfg); + } else { + cache_helpers.addModule(&man.hash, comp.root_mod); } - try man.addOptionalFile(comp.bin_file.options.linker_script); - try man.addOptionalFile(comp.bin_file.options.version_script); - - for (comp.bin_file.options.objects) |obj| { + for (comp.objects) |obj| { _ = try man.addFile(obj.path, null); man.hash.add(obj.must_link); man.hash.add(obj.loption); @@ -2759,88 +2507,99 @@ fn addNonIncrementalStuffToCacheManifest(comp: *Compilation, man: *Cache.Manifes } } + man.hash.add(comp.config.use_llvm); + man.hash.add(comp.config.use_lib_llvm); + man.hash.add(comp.config.is_test); + man.hash.add(comp.config.import_memory); + man.hash.add(comp.config.export_memory); + man.hash.add(comp.config.shared_memory); + man.hash.add(comp.config.dll_export_fns); + man.hash.add(comp.config.rdynamic); + + man.hash.addOptionalBytes(comp.sysroot); + man.hash.addOptional(comp.version); + man.hash.add(comp.link_eh_frame_hdr); + man.hash.add(comp.skip_linker_dependencies); + man.hash.add(comp.include_compiler_rt); man.hash.addListOfBytes(comp.rc_include_dir_list); + man.hash.addListOfBytes(comp.force_undefined_symbols.keys()); + man.hash.addListOfBytes(comp.framework_dirs); + try link.hashAddSystemLibs(man, comp.system_libs); cache_helpers.addOptionalEmitLoc(&man.hash, comp.emit_asm); 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); - man.hash.add(comp.bin_file.options.eh_frame_hdr); - man.hash.add(comp.bin_file.options.emit_relocs); - man.hash.add(comp.bin_file.options.rdynamic); - man.hash.addListOfBytes(comp.bin_file.options.lib_dirs); - man.hash.addListOfBytes(comp.bin_file.options.rpath_list); - man.hash.addListOfBytes(comp.bin_file.options.symbol_wrap_set.keys()); - man.hash.add(comp.bin_file.options.each_lib_rpath); - man.hash.add(comp.bin_file.options.build_id); - man.hash.add(comp.bin_file.options.skip_linker_dependencies); - man.hash.add(comp.bin_file.options.z_nodelete); - man.hash.add(comp.bin_file.options.z_notext); - man.hash.add(comp.bin_file.options.z_defs); - man.hash.add(comp.bin_file.options.z_origin); - man.hash.add(comp.bin_file.options.z_nocopyreloc); - man.hash.add(comp.bin_file.options.z_now); - man.hash.add(comp.bin_file.options.z_relro); - man.hash.add(comp.bin_file.options.z_common_page_size orelse 0); - man.hash.add(comp.bin_file.options.z_max_page_size orelse 0); - man.hash.add(comp.bin_file.options.hash_style); - man.hash.add(comp.bin_file.options.compress_debug_sections); - man.hash.add(comp.bin_file.options.include_compiler_rt); - man.hash.addOptional(comp.bin_file.options.sort_section); - if (comp.bin_file.options.link_libc) { - man.hash.add(comp.bin_file.options.libc_installation != null); - if (comp.bin_file.options.libc_installation) |libc_installation| { + man.hash.addListOfBytes(comp.global_cc_argv); + + const opts = comp.cache_use.whole.lf_open_opts; + + try man.addOptionalFile(opts.linker_script); + try man.addOptionalFile(opts.version_script); + + man.hash.addOptional(opts.stack_size); + man.hash.addOptional(opts.image_base); + man.hash.addOptional(opts.gc_sections); + man.hash.add(opts.emit_relocs); + man.hash.addListOfBytes(opts.lib_dirs); + man.hash.addListOfBytes(opts.rpath_list); + man.hash.addListOfBytes(opts.symbol_wrap_set.keys()); + man.hash.add(opts.each_lib_rpath); + if (comp.config.link_libc) { + man.hash.add(comp.libc_installation != null); + const target = comp.root_mod.resolved_target.result; + if (comp.libc_installation) |libc_installation| { man.hash.addOptionalBytes(libc_installation.crt_dir); if (target.abi == .msvc) { man.hash.addOptionalBytes(libc_installation.msvc_lib_dir); man.hash.addOptionalBytes(libc_installation.kernel32_lib_dir); } } - man.hash.addOptionalBytes(comp.bin_file.options.dynamic_linker); - } - man.hash.addOptionalBytes(comp.bin_file.options.soname); - man.hash.addOptional(comp.bin_file.options.version); - try link.hashAddSystemLibs(man, comp.bin_file.options.system_libs); - man.hash.addListOfBytes(comp.bin_file.options.force_undefined_symbols.keys()); - man.hash.addOptional(comp.bin_file.options.allow_shlib_undefined); - man.hash.add(comp.bin_file.options.bind_global_refs_locally); - man.hash.add(comp.bin_file.options.tsan); - man.hash.addOptionalBytes(comp.bin_file.options.sysroot); - man.hash.add(comp.bin_file.options.linker_optimization); + man.hash.addOptionalBytes(target.dynamic_linker.get()); + } + man.hash.addOptional(opts.allow_shlib_undefined); + man.hash.add(opts.bind_global_refs_locally); + + // ELF specific stuff + man.hash.add(opts.z_nodelete); + man.hash.add(opts.z_notext); + man.hash.add(opts.z_defs); + man.hash.add(opts.z_origin); + man.hash.add(opts.z_nocopyreloc); + man.hash.add(opts.z_now); + man.hash.add(opts.z_relro); + man.hash.add(opts.z_common_page_size orelse 0); + man.hash.add(opts.z_max_page_size orelse 0); + man.hash.add(opts.hash_style); + man.hash.add(opts.compress_debug_sections); + man.hash.addOptional(opts.sort_section); + man.hash.addOptionalBytes(opts.soname); + man.hash.add(opts.build_id); // WASM specific stuff - man.hash.add(comp.bin_file.options.import_memory); - man.hash.add(comp.bin_file.options.export_memory); - man.hash.addOptional(comp.bin_file.options.initial_memory); - man.hash.addOptional(comp.bin_file.options.max_memory); - man.hash.add(comp.bin_file.options.shared_memory); - man.hash.addOptional(comp.bin_file.options.global_base); + man.hash.addOptional(opts.initial_memory); + man.hash.addOptional(opts.max_memory); + man.hash.addOptional(opts.global_base); // Mach-O specific stuff - man.hash.addListOfBytes(comp.bin_file.options.framework_dirs); - try link.hashAddFrameworks(man, comp.bin_file.options.frameworks); - try man.addOptionalFile(comp.bin_file.options.entitlements); - man.hash.addOptional(comp.bin_file.options.pagezero_size); - man.hash.addOptional(comp.bin_file.options.headerpad_size); - man.hash.add(comp.bin_file.options.headerpad_max_install_names); - man.hash.add(comp.bin_file.options.dead_strip_dylibs); + try link.File.MachO.hashAddFrameworks(man, opts.frameworks); + try man.addOptionalFile(opts.entitlements); + man.hash.addOptional(opts.pagezero_size); + man.hash.addOptional(opts.headerpad_size); + man.hash.add(opts.headerpad_max_install_names); + man.hash.add(opts.dead_strip_dylibs); // COFF specific stuff - man.hash.addOptional(comp.bin_file.options.subsystem); - man.hash.add(comp.bin_file.options.tsaware); - man.hash.add(comp.bin_file.options.nxcompat); - man.hash.add(comp.bin_file.options.dynamicbase); - man.hash.addOptional(comp.bin_file.options.major_subsystem_version); - man.hash.addOptional(comp.bin_file.options.minor_subsystem_version); + man.hash.addOptional(opts.subsystem); + man.hash.add(opts.tsaware); + man.hash.add(opts.nxcompat); + man.hash.add(opts.dynamicbase); + man.hash.addOptional(opts.major_subsystem_version); + man.hash.addOptional(opts.minor_subsystem_version); } fn emitOthers(comp: *Compilation) void { - if (comp.bin_file.options.output_mode != .Obj or comp.bin_file.options.module != null or + if (comp.config.output_mode != .Obj or comp.module != null or comp.c_object_table.count() == 0) { return; @@ -2877,6 +2636,50 @@ fn emitOthers(comp: *Compilation) void { } } +pub fn emitLlvmObject( + comp: *Compilation, + arena: Allocator, + default_emit: Emit, + bin_emit_loc: ?EmitLoc, + llvm_object: *LlvmObject, + prog_node: *std.Progress.Node, +) !void { + if (build_options.only_c) @compileError("unreachable"); + + var sub_prog_node = prog_node.start("LLVM Emit Object", 0); + sub_prog_node.activate(); + sub_prog_node.context.refresh(); + defer sub_prog_node.end(); + + try llvm_object.emit(.{ + .pre_ir_path = comp.verbose_llvm_ir, + .pre_bc_path = comp.verbose_llvm_bc, + .bin_path = try resolveEmitLoc(arena, default_emit, bin_emit_loc), + .asm_path = try resolveEmitLoc(arena, default_emit, comp.emit_asm), + .post_ir_path = try resolveEmitLoc(arena, default_emit, comp.emit_llvm_ir), + .post_bc_path = try resolveEmitLoc(arena, default_emit, comp.emit_llvm_bc), + + .is_debug = comp.root_mod.optimize_mode == .Debug, + .is_small = comp.root_mod.optimize_mode == .ReleaseSmall, + .time_report = comp.time_report, + .sanitize_thread = comp.config.any_sanitize_thread, + .lto = comp.config.lto, + }); +} + +fn resolveEmitLoc( + arena: Allocator, + default_emit: Emit, + opt_loc: ?EmitLoc, +) Allocator.Error!?[*:0]const u8 { + const loc = opt_loc orelse return null; + const slice = if (loc.directory) |directory| + try directory.joinZ(arena, &.{loc.basename}) + else + try default_emit.basenamePath(arena, loc.basename); + return slice.ptr; +} + fn reportMultiModuleErrors(mod: *Module) !void { // Some cases can give you a whole bunch of multi-module errors, which it's not helpful to // print all of, so we'll cap the number of these to emit. @@ -2982,12 +2785,14 @@ fn reportMultiModuleErrors(mod: *Module) !void { /// binary is concerned. This will remove the write flag, or close the file, /// or whatever is needed so that it can be executed. /// After this, one must call` makeFileWritable` before calling `update`. -pub fn makeBinFileExecutable(self: *Compilation) !void { - return self.bin_file.makeExecutable(); +pub fn makeBinFileExecutable(comp: *Compilation) !void { + const lf = comp.bin_file orelse return; + return lf.makeExecutable(); } -pub fn makeBinFileWritable(self: *Compilation) !void { - return self.bin_file.makeWritable(); +pub fn makeBinFileWritable(comp: *Compilation) !void { + const lf = comp.bin_file orelse return; + return lf.makeWritable(); } const Header = extern struct { @@ -3006,10 +2811,10 @@ pub fn saveState(comp: *Compilation) !void { var bufs_list: [6]std.os.iovec_const = undefined; var bufs_len: usize = 0; - const emit = comp.bin_file.options.emit orelse return; + const lf = comp.bin_file orelse return; - if (comp.bin_file.options.module) |mod| { - const ip = &mod.intern_pool; + if (comp.module) |zcu| { + const ip = &zcu.intern_pool; const header: Header = .{ .intern_pool = .{ .items_len = @intCast(ip.items.len), @@ -3033,7 +2838,7 @@ pub fn saveState(comp: *Compilation) !void { } var basename_buf: [255]u8 = undefined; const basename = std.fmt.bufPrint(&basename_buf, "{s}.zcs", .{ - comp.bin_file.options.root_name, + comp.root_name, }) catch o: { basename_buf[basename_buf.len - 4 ..].* = ".zcs".*; break :o &basename_buf; @@ -3041,7 +2846,7 @@ pub fn saveState(comp: *Compilation) !void { // Using an atomic file prevents a crash or power failure from corrupting // the previous incremental compilation state. - var af = try emit.directory.handle.atomicFile(basename, .{}); + var af = try lf.emit.directory.handle.atomicFile(basename, .{}); defer af.deinit(); try af.file.pwritevAll(bufs_list[0..bufs_len], 0); try af.finish(); @@ -3057,23 +2862,23 @@ fn addBuf(bufs_list: []std.os.iovec_const, bufs_len: *usize, buf: []const u8) vo } /// This function is temporally single-threaded. -pub fn totalErrorCount(self: *Compilation) u32 { +pub fn totalErrorCount(comp: *Compilation) u32 { var total: usize = - self.misc_failures.count() + - @intFromBool(self.alloc_failure_occurred) + - self.lld_errors.items.len; + comp.misc_failures.count() + + @intFromBool(comp.alloc_failure_occurred) + + comp.lld_errors.items.len; - for (self.failed_c_objects.values()) |bundle| { + for (comp.failed_c_objects.values()) |bundle| { total += bundle.diags.len; } if (!build_options.only_core_functionality) { - for (self.failed_win32_resources.values()) |errs| { + for (comp.failed_win32_resources.values()) |errs| { total += errs.errorMessageCount(); } } - if (self.bin_file.options.module) |module| { + if (comp.module) |module| { total += module.failed_exports.count(); total += module.failed_embed_files.count(); @@ -3116,16 +2921,15 @@ pub fn totalErrorCount(self: *Compilation) u32 { // The "no entry point found" error only counts if there are no semantic analysis errors. if (total == 0) { - total += @intFromBool(self.link_error_flags.no_entry_point_found); + total += @intFromBool(comp.link_error_flags.no_entry_point_found); } - total += @intFromBool(self.link_error_flags.missing_libc); + total += @intFromBool(comp.link_error_flags.missing_libc); - // Misc linker errors - total += self.bin_file.miscErrors().len; + total += comp.link_errors.items.len; // Compile log errors only count if there are no other errors. if (total == 0) { - if (self.bin_file.options.module) |module| { + if (comp.module) |module| { total += @intFromBool(module.compile_log_decls.count() != 0); } } @@ -3134,24 +2938,24 @@ pub fn totalErrorCount(self: *Compilation) u32 { } /// This function is temporally single-threaded. -pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { - const gpa = self.gpa; +pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { + const gpa = comp.gpa; var bundle: ErrorBundle.Wip = undefined; try bundle.init(gpa); defer bundle.deinit(); - for (self.failed_c_objects.values()) |diag_bundle| { + for (comp.failed_c_objects.values()) |diag_bundle| { try diag_bundle.addToErrorBundle(&bundle); } if (!build_options.only_core_functionality) { - for (self.failed_win32_resources.values()) |error_bundle| { + for (comp.failed_win32_resources.values()) |error_bundle| { try bundle.addBundleAsRoots(error_bundle); } } - for (self.lld_errors.items) |lld_error| { + for (comp.lld_errors.items) |lld_error| { const notes_len = @as(u32, @intCast(lld_error.context_lines.len)); try bundle.addRootErrorMessage(.{ @@ -3165,19 +2969,19 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { })); } } - for (self.misc_failures.values()) |*value| { + for (comp.misc_failures.values()) |*value| { try bundle.addRootErrorMessage(.{ .msg = try bundle.addString(value.msg), .notes_len = if (value.children) |b| b.errorMessageCount() else 0, }); if (value.children) |b| try bundle.addBundleAsNotes(b); } - if (self.alloc_failure_occurred) { + if (comp.alloc_failure_occurred) { try bundle.addRootErrorMessage(.{ .msg = try bundle.addString("memory allocation failure"), }); } - if (self.bin_file.options.module) |module| { + if (comp.module) |module| { for (module.failed_files.keys(), module.failed_files.values()) |file, error_msg| { if (error_msg) |msg| { try addModuleErrorMsg(module, &bundle, msg.*); @@ -3249,14 +3053,14 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { } if (bundle.root_list.items.len == 0) { - if (self.link_error_flags.no_entry_point_found) { + if (comp.link_error_flags.no_entry_point_found) { try bundle.addRootErrorMessage(.{ .msg = try bundle.addString("no entry point found"), }); } } - if (self.link_error_flags.missing_libc) { + if (comp.link_error_flags.missing_libc) { try bundle.addRootErrorMessage(.{ .msg = try bundle.addString("libc not available"), .notes_len = 2, @@ -3270,7 +3074,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { })); } - for (self.bin_file.miscErrors()) |link_err| { + for (comp.link_errors.items) |link_err| { try bundle.addRootErrorMessage(.{ .msg = try bundle.addString(link_err.msg), .notes_len = @intCast(link_err.notes.len), @@ -3283,7 +3087,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { } } - if (self.bin_file.options.module) |module| { + if (comp.module) |module| { if (bundle.root_list.items.len == 0 and module.compile_log_decls.count() != 0) { const keys = module.compile_log_decls.keys(); const values = module.compile_log_decls.values(); @@ -3309,9 +3113,9 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { } } - assert(self.totalErrorCount() == bundle.root_list.items.len); + assert(comp.totalErrorCount() == bundle.root_list.items.len); - const compile_log_text = if (self.bin_file.options.module) |m| m.compile_log_text.items else ""; + const compile_log_text = if (comp.module) |m| m.compile_log_text.items else ""; return bundle.toOwnedBundle(compile_log_text); } @@ -3344,7 +3148,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; @@ -3354,7 +3158,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; @@ -3595,13 +3399,25 @@ pub fn performAllTheWork( // 1. to avoid race condition of zig processes truncating each other's builtin.zig files // 2. optimization; in the hot path it only incurs a stat() syscall, which happens // in the `astgen_wait_group`. - if (comp.bin_file.options.module) |mod| { - if (mod.job_queued_update_builtin_zig) { - mod.job_queued_update_builtin_zig = false; + if (comp.job_queued_update_builtin_zig) b: { + comp.job_queued_update_builtin_zig = false; + const zcu = comp.module orelse break :b; + _ = zcu; + // TODO put all the modules in a flat array to make them easy to iterate. + var seen: std.AutoArrayHashMapUnmanaged(*Package.Module, void) = .{}; + defer seen.deinit(comp.gpa); + try seen.put(comp.gpa, comp.root_mod, {}); + var i: usize = 0; + while (i < seen.count()) : (i += 1) { + const mod = seen.keys()[i]; + for (mod.deps.values()) |dep| + try seen.put(comp.gpa, dep, {}); + + const file = mod.builtin_file orelse continue; comp.astgen_wait_group.start(); try comp.thread_pool.spawn(workerUpdateBuiltinZigFile, .{ - comp, mod, &comp.astgen_wait_group, + comp, mod, file, &comp.astgen_wait_group, }); } } @@ -3637,15 +3453,15 @@ pub fn performAllTheWork( } } - if (comp.bin_file.options.module) |mod| { + if (comp.module) |mod| { try reportMultiModuleErrors(mod); } - if (comp.bin_file.options.module) |mod| { + if (comp.module) |mod| { mod.sema_prog_node = main_progress_node.start("Semantic Analysis", 0); mod.sema_prog_node.activate(); } - defer if (comp.bin_file.options.module) |mod| { + defer if (comp.module) |mod| { mod.sema_prog_node.end(); mod.sema_prog_node = undefined; }; @@ -3679,7 +3495,7 @@ pub fn performAllTheWork( fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !void { switch (job) { .codegen_decl => |decl_index| { - const module = comp.bin_file.options.module.?; + const module = comp.module.?; const decl = module.declPtr(decl_index); switch (decl.analysis) { @@ -3717,14 +3533,14 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v const named_frame = tracy.namedFrame("codegen_func"); defer named_frame.end(); - const module = comp.bin_file.options.module.?; + const module = comp.module.?; module.ensureFuncBodyAnalyzed(func) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => return, }; }, .emit_h_decl => |decl_index| { - const module = comp.bin_file.options.module.?; + const module = comp.module.?; const decl = module.declPtr(decl_index); switch (decl.analysis) { @@ -3783,13 +3599,13 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v } }, .analyze_decl => |decl_index| { - const module = comp.bin_file.options.module.?; + const module = comp.module.?; module.ensureDeclAnalyzed(decl_index) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => return, }; const decl = module.declPtr(decl_index); - if (decl.kind == .@"test" and comp.bin_file.options.is_test) { + if (decl.kind == .@"test" and comp.config.is_test) { // Tests are always emitted in test binaries. The decl_refs are created by // Module.populateTestFunctions, but this will not queue body analysis, so do // that now. @@ -3801,9 +3617,10 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v defer named_frame.end(); const gpa = comp.gpa; - const module = comp.bin_file.options.module.?; + const module = comp.module.?; const decl = module.declPtr(decl_index); - comp.bin_file.updateDeclLineNumber(module, decl_index) catch |err| { + const lf = comp.bin_file.?; + lf.updateDeclLineNumber(module, decl_index) catch |err| { try module.failed_decls.ensureUnusedCapacity(gpa, 1); module.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create( gpa, @@ -3818,7 +3635,7 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v const named_frame = tracy.namedFrame("analyze_mod"); defer named_frame.end(); - const module = comp.bin_file.options.module.?; + const module = comp.module.?; module.semaPkg(pkg) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, error.AnalysisFail => return, @@ -3875,10 +3692,13 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v }; }, .windows_import_lib => |index| { + if (build_options.only_c) + @panic("building import libs not included in core functionality"); + const named_frame = tracy.namedFrame("windows_import_lib"); defer named_frame.end(); - const link_lib = comp.bin_file.options.system_libs.keys()[index]; + const link_lib = comp.system_libs.keys()[index]; mingw.buildImportLib(comp, link_lib) catch |err| { // TODO Surface more error details. comp.lockAndSetMiscFailure( @@ -3997,7 +3817,7 @@ fn workerAstGenFile( child_prog_node.activate(); defer child_prog_node.end(); - const mod = comp.bin_file.options.module.?; + const mod = comp.module.?; mod.astGenFile(file) catch |err| switch (err) { error.AnalysisFail => return, else => { @@ -4065,19 +3885,17 @@ fn workerAstGenFile( fn workerUpdateBuiltinZigFile( comp: *Compilation, - mod: *Module, + mod: *Package.Module, + file: *Module.File, wg: *WaitGroup, ) void { defer wg.finish(); - - mod.populateBuiltinFile() catch |err| { - const dir_path: []const u8 = mod.zig_cache_artifact_directory.path orelse "."; - + Builtin.populateFile(comp, mod, file) catch |err| { comp.mutex.lock(); defer comp.mutex.unlock(); - comp.setMiscFailure(.write_builtin_zig, "unable to write builtin.zig to {s}: {s}", .{ - dir_path, @errorName(err), + comp.setMiscFailure(.write_builtin_zig, "unable to write '{}{s}': {s}", .{ + mod.root, mod.root_src_path, @errorName(err), }); }; } @@ -4100,7 +3918,7 @@ fn workerCheckEmbedFile( } fn detectEmbedFileUpdate(comp: *Compilation, embed_file: *Module.EmbedFile) !void { - const mod = comp.bin_file.options.module.?; + const mod = comp.module.?; const ip = &mod.intern_pool; const sub_file_path = ip.stringToSlice(embed_file.sub_file_path); var file = try embed_file.owner.root.openFile(sub_file_path, .{}); @@ -4118,21 +3936,24 @@ fn detectEmbedFileUpdate(comp: *Compilation, embed_file: *Module.EmbedFile) !voi @panic("TODO: handle embed file incremental update"); } -pub fn obtainCObjectCacheManifest(comp: *const Compilation) Cache.Manifest { +pub fn obtainCObjectCacheManifest( + comp: *const Compilation, + owner_mod: *Package.Module, +) Cache.Manifest { var man = comp.cache_parent.obtain(); // Only things that need to be added on top of the base hash, and only things // that apply both to @cImport and compiling C objects. No linking stuff here! // Also nothing that applies only to compiling .zig code. - man.hash.add(comp.sanitize_c); - man.hash.addListOfBytes(comp.clang_argv); - man.hash.add(comp.bin_file.options.link_libcpp); + cache_helpers.addModule(&man.hash, owner_mod); + man.hash.addListOfBytes(comp.global_cc_argv); + man.hash.add(comp.config.link_libcpp); // When libc_installation is null it means that Zig generated this dir list // based on the zig library directory alone. The zig lib directory file // path is purposefully either in the cache or not in the cache. The // decision should not be overridden here. - if (comp.bin_file.options.libc_installation != null) { + if (comp.libc_installation != null) { man.hash.addListOfBytes(comp.libc_include_dir_list); } @@ -4152,7 +3973,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); } }; @@ -4160,19 +3981,19 @@ pub const CImportResult = struct { /// Caller owns returned memory. /// This API is currently coupled pretty tightly to stage1's needs; it will need to be reworked /// a bit when we want to start using it from self-hosted. -pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { +pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module) !CImportResult { if (build_options.only_core_functionality) @panic("@cImport is not available in a zig2.c build"); const tracy_trace = trace(@src()); defer tracy_trace.end(); const cimport_zig_basename = "cimport.zig"; - var man = comp.obtainCObjectCacheManifest(); + var man = comp.obtainCObjectCacheManifest(owner_mod); defer man.deinit(); man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects man.hash.addBytes(c_src); - man.hash.add(comp.c_frontend); + man.hash.add(comp.config.c_frontend); // If the previous invocation resulted in clang errors, we will see a hit // here with 0 files in the manifest, in which case it is actually a miss. @@ -4209,15 +4030,15 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { var argv = std.ArrayList([]const u8).init(comp.gpa); defer argv.deinit(); - try argv.append(@tagName(comp.c_frontend)); // argv[0] is program name, actual args start at [1] - try comp.addTranslateCCArgs(arena, &argv, .c, out_dep_path); + try argv.append(@tagName(comp.config.c_frontend)); // argv[0] is program name, actual args start at [1] + try comp.addTranslateCCArgs(arena, &argv, .c, out_dep_path, owner_mod); try argv.append(out_h_path); if (comp.verbose_cc) { dump_argv(argv.items); } - var tree = switch (comp.c_frontend) { + var tree = switch (comp.config.c_frontend) { .aro => tree: { const translate_c = @import("aro_translate_c.zig"); _ = translate_c; @@ -4265,10 +4086,13 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { const dep_basename = std.fs.path.basename(out_dep_path); try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); - if (comp.whole_cache_manifest) |whole_cache_manifest| { - comp.whole_cache_manifest_mutex.lock(); - defer comp.whole_cache_manifest_mutex.unlock(); - try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); + switch (comp.cache_use) { + .whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| { + whole.cache_manifest_mutex.lock(); + defer whole.cache_manifest_mutex.unlock(); + try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); + }, + .incremental => {}, } const digest = man.final(); @@ -4297,7 +4121,7 @@ pub fn cImport(comp: *Compilation, c_src: []const u8) !CImportResult { }; } - const out_zig_path = try comp.local_cache_directory.join(comp.arena.allocator(), &.{ + const out_zig_path = try comp.local_cache_directory.join(comp.arena, &.{ "o", &digest, cimport_zig_basename, }); if (comp.verbose_cimport) { @@ -4423,7 +4247,7 @@ fn reportRetryableAstGenError( file: *Module.File, err: anyerror, ) error{OutOfMemory}!void { - const mod = comp.bin_file.options.module.?; + const mod = comp.module.?; const gpa = mod.gpa; file.status = .retryable_failure; @@ -4462,7 +4286,7 @@ fn reportRetryableEmbedFileError( embed_file: *Module.EmbedFile, err: anyerror, ) error{OutOfMemory}!void { - const mod = comp.bin_file.options.module.?; + const mod = comp.module.?; const gpa = mod.gpa; const src_loc = embed_file.src_loc; const ip = &mod.intern_pool; @@ -4482,7 +4306,7 @@ fn reportRetryableEmbedFileError( } fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.Progress.Node) !void { - if (comp.c_frontend == .aro) { + if (comp.config.c_frontend == .aro) { return comp.failCObj(c_object, "aro does not support compiling C objects yet", .{}); } if (!build_options.have_llvm) { @@ -4505,7 +4329,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P _ = comp.failed_c_objects.swapRemove(c_object); } - var man = comp.obtainCObjectCacheManifest(); + var man = comp.obtainCObjectCacheManifest(c_object.src.owner); defer man.deinit(); man.hash.add(comp.clang_preprocessor_mode); @@ -4529,10 +4353,10 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P // Special case when doing build-obj for just one C file. When there are more than one object // file and building an object we need to link them together, but with just one it should go // directly to the output file. - const direct_o = comp.c_source_files.len == 1 and comp.bin_file.options.module == null and - comp.bin_file.options.output_mode == .Obj and comp.bin_file.options.objects.len == 0; + const direct_o = comp.c_source_files.len == 1 and comp.module == null and + comp.config.output_mode == .Obj and comp.objects.len == 0; const o_basename_noext = if (direct_o) - comp.bin_file.options.root_name + comp.root_name else c_source_basename[0 .. c_source_basename.len - std.fs.path.extension(c_source_basename).len]; @@ -4582,12 +4406,12 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P if (std.process.can_execv and direct_o and comp.disable_c_depfile and comp.clang_passthrough_mode) { - try comp.addCCArgs(arena, &argv, ext, null); + try comp.addCCArgs(arena, &argv, ext, null, c_object.src.owner); try argv.appendSlice(c_object.src.extra_flags); try argv.appendSlice(c_object.src.cache_exempt_flags); - const out_obj_path = if (comp.bin_file.options.emit) |emit| - try emit.directory.join(arena, &.{emit.sub_path}) + const out_obj_path = if (comp.bin_file) |lf| + try lf.emit.directory.join(arena, &.{lf.emit.sub_path}) else "/dev/null"; @@ -4625,7 +4449,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P null else try std.fmt.allocPrint(arena, "{s}.d", .{out_obj_path}); - try comp.addCCArgs(arena, &argv, ext, out_dep_path); + try comp.addCCArgs(arena, &argv, ext, out_dep_path, c_object.src.owner); try argv.appendSlice(c_object.src.extra_flags); try argv.appendSlice(c_object.src.cache_exempt_flags); @@ -4720,10 +4544,15 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P const dep_basename = std.fs.path.basename(dep_file_path); // Add the files depended on to the cache system. try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); - if (comp.whole_cache_manifest) |whole_cache_manifest| { - comp.whole_cache_manifest_mutex.lock(); - defer comp.whole_cache_manifest_mutex.unlock(); - try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); + switch (comp.cache_use) { + .whole => |whole| { + if (whole.cache_manifest) |whole_cache_manifest| { + whole.cache_manifest_mutex.lock(); + defer whole.cache_manifest_mutex.unlock(); + try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); + } + }, + .incremental => {}, } // Just to save disk space, we delete the file because it is never needed again. zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { @@ -4973,7 +4802,7 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 // mode. While these defines are not normally present when calling rc.exe directly, // them being defined matches the behavior of how MSVC calls rc.exe which is the more // relevant behavior in this case. - try comp.addCCArgs(arena, &argv, .rc, out_dep_path); + try comp.addCCArgs(arena, &argv, .rc, out_dep_path, rc_src.owner); if (comp.verbose_cc) { dump_argv(argv.items); @@ -5019,10 +4848,13 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 const dep_basename = std.fs.path.basename(out_dep_path); // Add the files depended on to the cache system. try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); - if (comp.whole_cache_manifest) |whole_cache_manifest| { - comp.whole_cache_manifest_mutex.lock(); - defer comp.whole_cache_manifest_mutex.unlock(); - try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); + switch (comp.cache_use) { + .whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| { + whole.cache_manifest_mutex.lock(); + defer whole.cache_manifest_mutex.unlock(); + try whole_cache_manifest.addDepFilePost(zig_cache_tmp_dir, dep_basename); + }, + .incremental => {}, } // Just to save disk space, we delete the file because it is never needed again. zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { @@ -5094,10 +4926,13 @@ fn updateWin32Resource(comp: *Compilation, win32_resource: *Win32Resource, win32 for (dependencies_list.items) |dep_file_path| { try man.addFilePost(dep_file_path); - if (comp.whole_cache_manifest) |whole_cache_manifest| { - comp.whole_cache_manifest_mutex.lock(); - defer comp.whole_cache_manifest_mutex.unlock(); - try whole_cache_manifest.addFilePost(dep_file_path); + switch (comp.cache_use) { + .whole => |whole| if (whole.cache_manifest) |whole_cache_manifest| { + whole.cache_manifest_mutex.lock(); + defer whole.cache_manifest_mutex.unlock(); + try whole_cache_manifest.addFilePost(dep_file_path); + }, + .incremental => {}, } } @@ -5151,11 +4986,12 @@ pub fn addTranslateCCArgs( argv: *std.ArrayList([]const u8), ext: FileExt, out_dep_path: ?[]const u8, + owner_mod: *Package.Module, ) !void { - try argv.appendSlice(&[_][]const u8{ "-x", "c" }); - try comp.addCCArgs(arena, argv, ext, out_dep_path); + try argv.appendSlice(&.{ "-x", "c" }); + try comp.addCCArgs(arena, argv, ext, out_dep_path, owner_mod); // This gives us access to preprocessing entities, presumably at the cost of performance. - try argv.appendSlice(&[_][]const u8{ "-Xclang", "-detailed-preprocessing-record" }); + try argv.appendSlice(&.{ "-Xclang", "-detailed-preprocessing-record" }); } /// Add common C compiler args between translate-c and C object compilation. @@ -5165,8 +5001,9 @@ pub fn addCCArgs( argv: *std.ArrayList([]const u8), ext: FileExt, out_dep_path: ?[]const u8, + mod: *Package.Module, ) !void { - const target = comp.getTarget(); + const target = mod.resolved_target.result; // As of Clang 16.x, it will by default read extra flags from /etc/clang. // I'm sure the person who implemented this means well, but they have a lot @@ -5187,19 +5024,19 @@ pub fn addCCArgs( try argv.append("-fno-caret-diagnostics"); } - if (comp.bin_file.options.function_sections) { + if (comp.function_sections) { try argv.append("-ffunction-sections"); } - if (comp.bin_file.options.data_sections) { + if (comp.data_sections) { try argv.append("-fdata-sections"); } - if (comp.bin_file.options.no_builtin) { + if (comp.no_builtin) { try argv.append("-fno-builtin"); } - if (comp.bin_file.options.link_libcpp) { + if (comp.config.link_libcpp) { const libcxx_include_path = try std.fs.path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, "libcxx", "include", }); @@ -5220,7 +5057,7 @@ pub fn addCCArgs( try argv.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); try argv.append("-D_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS"); - if (comp.bin_file.options.single_threaded) { + if (!comp.config.any_non_single_threaded) { try argv.append("-D_LIBCPP_HAS_NO_THREADS"); } @@ -5235,7 +5072,7 @@ pub fn addCCArgs( })); } - if (comp.bin_file.options.link_libunwind) { + if (comp.config.link_libunwind) { const libunwind_include_path = try std.fs.path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, "libunwind", "include", }); @@ -5244,7 +5081,7 @@ pub fn addCCArgs( try argv.append(libunwind_include_path); } - if (comp.bin_file.options.link_libc and target.isGnuLibC()) { + if (comp.config.link_libc and target.isGnuLibC()) { const target_version = target.os.version_range.linux.glibc; const glibc_minor_define = try std.fmt.allocPrint(arena, "-D__GLIBC_MINOR__={d}", .{ target_version.minor, @@ -5269,7 +5106,7 @@ pub fn addCCArgs( "-nostdinc", "-fno-spell-checking", }); - if (comp.bin_file.options.lto) { + if (comp.config.lto) { try argv.append("-flto"); } @@ -5281,7 +5118,7 @@ pub fn addCCArgs( try argv.appendSlice(&.{ "-iframework", framework_dir }); } - for (comp.bin_file.options.framework_dirs) |framework_dir| { + for (comp.framework_dirs) |framework_dir| { try argv.appendSlice(&.{ "-F", framework_dir }); } @@ -5324,9 +5161,8 @@ 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)})); + if (mod.code_model != .default) { + try argv.append(try std.fmt.allocPrint(arena, "-mcmodel={s}", .{@tagName(mod.code_model)})); } switch (target.os.tag) { @@ -5375,7 +5211,7 @@ pub fn addCCArgs( try argv.append("-mthumb"); } - if (comp.sanitize_c and !comp.bin_file.options.tsan) { + if (mod.sanitize_c and !mod.sanitize_thread) { try argv.append("-fsanitize=undefined"); try argv.append("-fsanitize-trap=undefined"); // It is very common, and well-defined, for a pointer on one side of a C ABI @@ -5386,27 +5222,27 @@ pub fn addCCArgs( // Without this flag, Clang would invoke UBSAN when such an extern // function was called. try argv.append("-fno-sanitize=function"); - } else if (comp.sanitize_c and comp.bin_file.options.tsan) { + } else if (mod.sanitize_c and mod.sanitize_thread) { try argv.append("-fsanitize=undefined,thread"); try argv.append("-fsanitize-trap=undefined"); try argv.append("-fno-sanitize=function"); - } else if (!comp.sanitize_c and comp.bin_file.options.tsan) { + } else if (!mod.sanitize_c and mod.sanitize_thread) { try argv.append("-fsanitize=thread"); } - if (comp.bin_file.options.red_zone) { + if (mod.red_zone) { try argv.append("-mred-zone"); } else if (target_util.hasRedZone(target)) { try argv.append("-mno-red-zone"); } - if (comp.bin_file.options.omit_frame_pointer) { + if (mod.omit_frame_pointer) { try argv.append("-fomit-frame-pointer"); } else { try argv.append("-fno-omit-frame-pointer"); } - const ssp_buf_size = comp.bin_file.options.stack_protector; + const ssp_buf_size = mod.stack_protector; if (ssp_buf_size != 0) { try argv.appendSlice(&[_][]const u8{ "-fstack-protector-strong", @@ -5417,7 +5253,7 @@ pub fn addCCArgs( try argv.append("-fno-stack-protector"); } - switch (comp.bin_file.options.optimize_mode) { + switch (mod.optimize_mode) { .Debug => { // windows c runtime requires -D_DEBUG if using debug libraries try argv.append("-D_DEBUG"); @@ -5447,11 +5283,11 @@ pub fn addCCArgs( }, } - if (target_util.supports_fpic(target) and comp.bin_file.options.pic) { + if (target_util.supports_fpic(target) and mod.pic) { try argv.append("-fPIC"); } - if (comp.unwind_tables) { + if (mod.unwind_tables) { try argv.append("-funwind-tables"); } else { try argv.append("-fno-unwind-tables"); @@ -5536,22 +5372,21 @@ pub fn addCCArgs( }, } - if (!comp.bin_file.options.strip) { - switch (target.ofmt) { - .coff => { - // -g is required here because -gcodeview doesn't trigger debug info - // generation, it only changes the type of information generated. - try argv.appendSlice(&.{ "-g", "-gcodeview" }); - }, - .elf, .macho => { - try argv.append("-gdwarf-4"); - if (comp.bin_file.options.dwarf_format) |f| switch (f) { - .@"32" => try argv.append("-gdwarf32"), - .@"64" => try argv.append("-gdwarf64"), - }; - }, - else => try argv.append("-g"), - } + try argv.ensureUnusedCapacity(2); + switch (comp.config.debug_format) { + .strip => {}, + .code_view => { + // -g is required here because -gcodeview doesn't trigger debug info + // generation, it only changes the type of information generated. + argv.appendSliceAssumeCapacity(&.{ "-g", "-gcodeview" }); + }, + .dwarf => |f| { + argv.appendAssumeCapacity("-gdwarf-4"); + switch (f) { + .@"32" => argv.appendAssumeCapacity("-gdwarf32"), + .@"64" => argv.appendAssumeCapacity("-gdwarf64"), + } + }, } if (target_util.llvmMachineAbi(target)) |mabi| { @@ -5574,7 +5409,16 @@ pub fn addCCArgs( try argv.append("-ffreestanding"); } - try argv.appendSlice(comp.clang_argv); + if (mod.resolved_target.is_native_os and mod.resolved_target.is_native_abi) { + try argv.ensureUnusedCapacity(comp.native_system_include_paths.len * 2); + for (comp.native_system_include_paths) |include_path| { + argv.appendAssumeCapacity("-isystem"); + argv.appendAssumeCapacity(include_path); + } + } + + try argv.appendSlice(comp.global_cc_argv); + try argv.appendSlice(mod.cc_argv); } fn failCObj( @@ -5621,68 +5465,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; @@ -6064,7 +5846,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 { @@ -6285,21 +6067,21 @@ pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const { return comp.crt_files.get(basename).?.full_object_path; } - const lci = comp.bin_file.options.libc_installation orelse return error.LibCInstallationNotAvailable; + const lci = comp.libc_installation orelse return error.LibCInstallationNotAvailable; const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir; const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename }); return full_path; } fn wantBuildLibCFromSource(comp: Compilation) bool { - const is_exe_or_dyn_lib = switch (comp.bin_file.options.output_mode) { + const is_exe_or_dyn_lib = switch (comp.config.output_mode) { .Obj => false, - .Lib => comp.bin_file.options.link_mode == .Dynamic, + .Lib => comp.config.link_mode == .Dynamic, .Exe => true, }; - return comp.bin_file.options.link_libc and is_exe_or_dyn_lib and - comp.bin_file.options.libc_installation == null and - comp.bin_file.options.target.ofmt != .c; + const ofmt = comp.root_mod.resolved_target.result.ofmt; + return comp.config.link_libc and is_exe_or_dyn_lib and + comp.libc_installation == null and ofmt != .c; } fn wantBuildGLibCFromSource(comp: Compilation) bool { @@ -6321,13 +6103,13 @@ fn wantBuildMinGWFromSource(comp: Compilation) bool { } fn wantBuildLibUnwindFromSource(comp: *Compilation) bool { - const is_exe_or_dyn_lib = switch (comp.bin_file.options.output_mode) { + const is_exe_or_dyn_lib = switch (comp.config.output_mode) { .Obj => false, - .Lib => comp.bin_file.options.link_mode == .Dynamic, + .Lib => comp.config.link_mode == .Dynamic, .Exe => true, }; - return is_exe_or_dyn_lib and comp.bin_file.options.link_libunwind and - comp.bin_file.options.target.ofmt != .c; + const ofmt = comp.root_mod.resolved_target.result.ofmt; + return is_exe_or_dyn_lib and comp.config.link_libunwind and ofmt != .c; } fn setAllocFailure(comp: *Compilation) void { @@ -6377,7 +6159,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() }); @@ -6430,7 +6212,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, @@ -6448,7 +6230,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, @@ -6456,222 +6238,8 @@ 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("};\n"); - - try buffer.writer().print( - \\pub const target = std.Target{{ - \\ .cpu = cpu, - \\ .os = os, - \\ .abi = abi, - \\ .ofmt = object_format, - \\}}; - \\pub const object_format = std.Target.ObjectFormat.{}; - \\pub const mode = std.builtin.OptimizeMode.{}; - \\pub const link_libc = {}; - \\pub const link_libcpp = {}; - \\pub const have_error_return_tracing = {}; - \\pub const valgrind_support = {}; - \\pub const sanitize_thread = {}; - \\pub const position_independent_code = {}; - \\pub const position_independent_executable = {}; - \\pub const strip_debug_info = {}; - \\pub const code_model = std.builtin.CodeModel.{}; - \\pub const omit_frame_pointer = {}; - \\ - , .{ - std.zig.fmtId(@tagName(target.ofmt)), - std.zig.fmtId(@tagName(comp.bin_file.options.optimize_mode)), - comp.bin_file.options.link_libc, - comp.bin_file.options.link_libcpp, - comp.bin_file.options.error_return_tracing, - comp.bin_file.options.valgrind, - comp.bin_file.options.tsan, - comp.bin_file.options.pic, - comp.bin_file.options.pie, - comp.bin_file.options.strip, - std.zig.fmtId(@tagName(comp.bin_file.options.machine_code_model)), - comp.bin_file.options.omit_frame_pointer, - }); - - if (target.os.tag == .wasi) { - const wasi_exec_model_fmt = std.zig.fmtId(@tagName(comp.bin_file.options.wasi_exec_model)); - try buffer.writer().print( - \\pub const wasi_exec_model = std.builtin.WasiExecModel.{}; - \\ - , .{wasi_exec_model_fmt}); - } - - if (comp.bin_file.options.is_test) { - try buffer.appendSlice( - \\pub var test_functions: []const std.builtin.TestFn = undefined; // overwritten later - \\ - ); - if (comp.test_evented_io) { - try buffer.appendSlice( - \\pub const test_io_mode = .evented; - \\ - ); - } else { - try buffer.appendSlice( - \\pub const test_io_mode = .blocking; - \\ - ); - } - } - - return buffer.toOwnedSliceSentinel(0); + const target = comp.root_mod.resolved_target.result; + return target_util.zigBackend(target, comp.config.use_llvm); } pub fn updateSubCompilation( @@ -6718,60 +6286,83 @@ fn buildOutputFromZig( const tracy_trace = trace(@src()); defer tracy_trace.end(); + const gpa = comp.gpa; + var arena_allocator = std.heap.ArenaAllocator.init(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 unwind_tables = comp.link_eh_frame_hdr; + const strip = comp.compilerRtStrip(); + const optimize_mode = comp.compilerRtOptMode(); + + const config = try Config.resolve(.{ + .output_mode = output_mode, + .link_mode = .Static, + .resolved_target = comp.root_mod.resolved_target, + .is_test = false, + .have_zcu = true, + .emit_bin = true, + .root_optimize_mode = optimize_mode, + .root_strip = strip, + .link_libc = comp.config.link_libc, + .any_unwind_tables = unwind_tables, + }); + + const root_mod = try Package.Module.create(arena, .{ + .global_cache_directory = comp.global_cache_directory, + .paths = .{ + .root = .{ .root_dir = comp.zig_lib_directory }, + .root_src_path = src_basename, + }, .fully_qualified_name = "root", - }; + .inherited = .{ + .resolved_target = comp.root_mod.resolved_target, + .strip = strip, + .stack_check = false, + .stack_protector = 0, + .red_zone = comp.root_mod.red_zone, + .omit_frame_pointer = comp.root_mod.omit_frame_pointer, + .unwind_tables = unwind_tables, + .pic = comp.root_mod.pic, + .optimize_mode = optimize_mode, + .structured_cfg = comp.root_mod.structured_cfg, + }, + .global = config, + .cc_argv = &.{}, + .parent = null, + .builtin_mod = null, + }); 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. - .basename = bin_basename, - }; - const sub_compilation = try Compilation.create(comp.gpa, .{ + const sub_compilation = try Compilation.create(gpa, arena, .{ .global_cache_directory = comp.global_cache_directory, .local_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, + .self_exe_path = comp.self_exe_path, + .config = config, + .root_mod = root_mod, .cache_mode = .whole, - .target = target, .root_name = root_name, - .main_mod = &main_mod, - .output_mode = output_mode, .thread_pool = comp.thread_pool, - .libc_installation = comp.bin_file.options.libc_installation, - .emit_bin = emit_bin, - .optimize_mode = comp.compilerRtOptMode(), - .link_mode = .Static, + .libc_installation = comp.libc_installation, + .emit_bin = .{ + .directory = null, // Put it in the cache directory. + .basename = bin_basename, + }, .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, + .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, .verbose_intern_pool = comp.verbose_intern_pool, .verbose_generic_instances = comp.verbose_intern_pool, @@ -6781,20 +6372,13 @@ fn buildOutputFromZig( .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, - .link_libc = comp.bin_file.options.link_libc, - .want_structured_cfg = comp.bin_file.options.want_structured_cfg, }); defer sub_compilation.destroy(); try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); assert(out.* == null); - out.* = Compilation.CRTFile{ - .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ - sub_compilation.bin_file.options.emit.?.sub_path, - }), - .lock = sub_compilation.bin_file.toOwnedLock(), - }; + out.* = try sub_compilation.toCrtFile(); } pub fn build_crt_file( @@ -6803,56 +6387,89 @@ 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, + /// These elements have to get mutated to add the owner module after it is + /// created within this function. + c_source_files: []CSourceFile, ) !void { const tracy_trace = trace(@src()); defer tracy_trace.end(); - const target = comp.getTarget(); - const basename = try std.zig.binNameAlloc(comp.gpa, .{ + const gpa = comp.gpa; + var arena_allocator = std.heap.ArenaAllocator.init(gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + const basename = try std.zig.binNameAlloc(gpa, .{ .root_name = root_name, - .target = target, + .target = comp.root_mod.resolved_target.result, .output_mode = output_mode, }); - errdefer comp.gpa.free(basename); - const sub_compilation = try Compilation.create(comp.gpa, .{ + const config = try Config.resolve(.{ + .output_mode = output_mode, + .resolved_target = comp.root_mod.resolved_target, + .is_test = false, + .have_zcu = false, + .emit_bin = true, + .root_optimize_mode = comp.compilerRtOptMode(), + .root_strip = comp.compilerRtStrip(), + .link_libc = false, + .lto = switch (output_mode) { + .Lib => comp.config.lto, + .Obj, .Exe => false, + }, + }); + const root_mod = try Package.Module.create(arena, .{ + .global_cache_directory = comp.global_cache_directory, + .paths = .{ + .root = .{ .root_dir = comp.zig_lib_directory }, + .root_src_path = "", + }, + .fully_qualified_name = "root", + .inherited = .{ + .resolved_target = comp.root_mod.resolved_target, + .strip = comp.compilerRtStrip(), + .stack_check = false, + .stack_protector = 0, + .sanitize_c = false, + .sanitize_thread = false, + .red_zone = comp.root_mod.red_zone, + .omit_frame_pointer = comp.root_mod.omit_frame_pointer, + .valgrind = false, + .unwind_tables = false, + .pic = comp.root_mod.pic, + .optimize_mode = comp.compilerRtOptMode(), + .structured_cfg = comp.root_mod.structured_cfg, + }, + .global = config, + .cc_argv = &.{}, + .parent = null, + .builtin_mod = null, + }); + + for (c_source_files) |*item| { + item.owner = root_mod; + } + + const sub_compilation = try Compilation.create(gpa, arena, .{ .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, + .self_exe_path = comp.self_exe_path, .cache_mode = .whole, - .target = target, + .config = config, + .root_mod = root_mod, .root_name = root_name, - .main_mod = null, - .output_mode = output_mode, .thread_pool = comp.thread_pool, - .libc_installation = comp.bin_file.options.libc_installation, + .libc_installation = comp.libc_installation, .emit_bin = .{ .directory = null, // Put it in the cache directory. .basename = basename, }, - .optimize_mode = comp.compilerRtOptMode(), - .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_pic = comp.bin_file.options.pic, - .want_pie = null, - .want_lto = switch (output_mode) { - .Lib => comp.bin_file.options.lto, - .Obj, .Exe => false, - }, .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, .c_source_files = c_source_files, .verbose_cc = comp.verbose_cc, - .verbose_link = comp.bin_file.options.verbose_link, + .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, .verbose_intern_pool = comp.verbose_intern_pool, .verbose_generic_instances = comp.verbose_generic_instances, @@ -6862,21 +6479,22 @@ pub fn build_crt_file( .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .skip_linker_dependencies = true, - .link_libc = comp.bin_file.options.link_libc, - .want_structured_cfg = comp.bin_file.options.want_structured_cfg, }); defer sub_compilation.destroy(); try comp.updateSubCompilation(sub_compilation, misc_task_tag, prog_node); - try comp.crt_files.ensureUnusedCapacity(comp.gpa, 1); + try comp.crt_files.ensureUnusedCapacity(gpa, 1); + comp.crt_files.putAssumeCapacityNoClobber(basename, try sub_compilation.toCrtFile()); +} - comp.crt_files.putAssumeCapacityNoClobber(basename, .{ - .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ - sub_compilation.bin_file.options.emit.?.sub_path, +pub fn toCrtFile(comp: *Compilation) Allocator.Error!CRTFile { + return .{ + .full_object_path = try comp.local_cache_directory.join(comp.gpa, &.{ + comp.cache_use.whole.bin_sub_path.?, }), - .lock = sub_compilation.bin_file.toOwnedLock(), - }); + .lock = comp.cache_use.whole.moveLock(), + }; } pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void { @@ -6884,21 +6502,24 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void { // This can happen when the user uses `build-exe foo.obj -lkernel32` and // then when we create a sub-Compilation for zig libc, it also tries to // build kernel32.lib. - if (comp.bin_file.options.skip_linker_dependencies) return; + if (comp.skip_linker_dependencies) return; // This happens when an `extern "foo"` function is referenced. // If we haven't seen this library yet and we're targeting Windows, we need // to queue up a work item to produce the DLL import library for this. - const gop = try comp.bin_file.options.system_libs.getOrPut(comp.gpa, lib_name); - if (!gop.found_existing and comp.getTarget().os.tag == .windows) { + const gop = try comp.system_libs.getOrPut(comp.gpa, lib_name); + if (!gop.found_existing) { gop.value_ptr.* = .{ .needed = true, .weak = false, .path = null, }; - try comp.work_queue.writeItem(.{ - .windows_import_lib = comp.bin_file.options.system_libs.count() - 1, - }); + const target = comp.root_mod.resolved_target.result; + if (target.os.tag == .windows and target.ofmt != .c) { + try comp.work_queue.writeItem(.{ + .windows_import_lib = comp.system_libs.count() - 1, + }); + } } } @@ -6906,10 +6527,11 @@ pub fn addLinkLib(comp: *Compilation, lib_name: []const u8) !void { /// compiler-rt, libcxx, libc, libunwind, etc. pub fn compilerRtOptMode(comp: Compilation) std.builtin.OptimizeMode { if (comp.debug_compiler_runtime_libs) { - return comp.bin_file.options.optimize_mode; + return comp.root_mod.optimize_mode; } - switch (comp.bin_file.options.optimize_mode) { - .Debug, .ReleaseSafe => return target_util.defaultCompilerRtOptimizeMode(comp.getTarget()), + const target = comp.root_mod.resolved_target.result; + switch (comp.root_mod.optimize_mode) { + .Debug, .ReleaseSafe => return target_util.defaultCompilerRtOptimizeMode(target), .ReleaseFast => return .ReleaseFast, .ReleaseSmall => return .ReleaseSmall, } @@ -6918,5 +6540,5 @@ pub fn compilerRtOptMode(comp: Compilation) std.builtin.OptimizeMode { /// This decides whether to strip debug info for all zig-provided libraries, including /// compiler-rt, libcxx, libc, libunwind, etc. pub fn compilerRtStrip(comp: Compilation) bool { - return comp.bin_file.options.strip; + return comp.root_mod.strip; } diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig new file mode 100644 index 000000000000..78273f66e42e --- /dev/null +++ b/src/Compilation/Config.zig @@ -0,0 +1,512 @@ +//! User-specified settings that have all the defaults resolved into concrete +//! values. These values are observable before calling Compilation.create for +//! the benefit of Module creation API, which needs access to these details in +//! order to resolve per-Module defaults. + +have_zcu: bool, +output_mode: std.builtin.OutputMode, +link_mode: std.builtin.LinkMode, +link_libc: bool, +link_libcpp: bool, +link_libunwind: bool, +/// True if and only if the c_source_files field will have nonzero length when +/// calling Compilation.create. +any_c_source_files: bool, +/// This is true if any Module has unwind_tables set explicitly to true. Until +/// Compilation.create is called, it is possible for this to be false while in +/// fact all Module instances have unwind_tables=true due to the default +/// being unwind_tables=true. After Compilation.create is called this will +/// also take into account the default setting, making this value true if and +/// only if any Module has unwind_tables set to true. +any_unwind_tables: bool, +/// This is true if any Module has single_threaded set explicitly to false. Until +/// Compilation.create is called, it is possible for this to be false while in +/// fact all Module instances have single_threaded=false due to the default +/// being non-single-threaded. After Compilation.create is called this will +/// also take into account the default setting, making this value true if and +/// only if any Module has single_threaded set to false. +any_non_single_threaded: bool, +/// This is true if and only if any Module has error_tracing set to true. +/// Function types and function calling convention depend on this global value, +/// however, other kinds of error tracing are omitted depending on the +/// per-Module setting. +any_error_tracing: bool, +any_sanitize_thread: 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, +debug_format: DebugFormat, +root_strip: bool, +root_error_tracing: bool, +dll_export_fns: bool, +rdynamic: bool, + +pub const CFrontend = enum { clang, aro }; + +pub const DebugFormat = union(enum) { + strip, + dwarf: std.dwarf.Format, + code_view, +}; + +pub const Options = struct { + output_mode: std.builtin.OutputMode, + resolved_target: Module.ResolvedTarget, + is_test: bool, + have_zcu: bool, + emit_bin: bool, + root_optimize_mode: ?std.builtin.OptimizeMode = null, + root_strip: ?bool = null, + root_error_tracing: ?bool = null, + link_mode: ?std.builtin.LinkMode = null, + ensure_libc_on_non_freestanding: bool = false, + ensure_libcpp_on_non_freestanding: bool = false, + any_non_single_threaded: bool = false, + any_sanitize_thread: bool = false, + any_unwind_tables: bool = false, + any_dyn_libs: bool = false, + any_c_source_files: bool = false, + any_non_stripped: bool = false, + any_error_tracing: bool = false, + emit_llvm_ir: bool = false, + emit_llvm_bc: bool = false, + link_libc: ?bool = null, + 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, + /// 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, + debug_format: ?DebugFormat = null, + dll_export_fns: ?bool = null, + rdynamic: ?bool = null, +}; + +pub const ResolveError = error{ + WasiExecModelRequiresWasi, + SharedMemoryIsWasmOnly, + ObjectFilesCannotShareMemory, + SharedMemoryRequiresAtomicsAndBulkMemory, + ThreadsRequireSharedMemory, + EmittingLlvmModuleRequiresLlvmBackend, + LlvmLacksTargetSupport, + ZigLacksTargetSupport, + EmittingBinaryRequiresLlvmLibrary, + LldIncompatibleObjectFormat, + LtoRequiresLld, + SanitizeThreadRequiresLibCpp, + LibCppRequiresLibUnwind, + OsRequiresLibC, + LibCppRequiresLibC, + LibUnwindRequiresLibC, + TargetCannotDynamicLink, + LibCRequiresDynamicLinking, + SharedLibrariesRequireDynamicLinking, + ExportMemoryAndDynamicIncompatible, + DynamicLibraryPrecludesPie, + TargetRequiresPie, + SanitizeThreadRequiresPie, + BackendLacksErrorTracing, + LlvmLibraryUnavailable, + LldUnavailable, + ClangUnavailable, + DllExportFnsRequiresWindows, +}; + +pub fn resolve(options: Options) ResolveError!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; + }; + + // *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 we have no zig code to compile, no need for LLVM. + if (!options.have_zcu) break :b false; + + // 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; + if (!target_util.hasLlvmSupport(target, target.ofmt)) + return error.LlvmLacksTargetSupport; + + 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 Zig does not support the target, then we can't use it. + if (target_util.zigBackend(target, false) == .other) { + if (options.use_llvm == false) return error.ZigLacksTargetSupport; + break :b true; + } + + if (options.use_llvm) |x| break :b x; + + // 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 and options.emit_bin) 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 (options.emit_bin and options.have_zcu) { + if (!use_lib_llvm and use_llvm) { + // Explicit request to use LLVM to produce an object file, but without + // using LLVM libraries. Impossible. + return error.EmittingBinaryRequiresLlvmLibrary; + } + + if (target_util.zigBackend(target, use_llvm) == .other) { + // There is no compiler backend available for this target. + return error.ZigLacksTargetSupport; + } + } + + // Make a decision on whether to use LLD or our own linker. + const use_lld = b: { + if (!target_util.hasLldSupport(target.ofmt)) { + if (options.use_lld == true) return error.LldIncompatibleObjectFormat; + break :b false; + } + + if (!build_options.have_llvm) { + if (options.use_lld == true) return error.LldUnavailable; + 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.any_c_source_files) 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 (options.output_mode == .Obj); + 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; + }; + + const root_strip = b: { + if (options.root_strip) |x| break :b x; + if (root_optimize_mode == .ReleaseSmall) break :b true; + if (!target_util.hasDebugInfo(target)) break :b true; + break :b false; + }; + + const debug_format: DebugFormat = b: { + if (root_strip and !options.any_non_stripped) break :b .strip; + break :b switch (target.ofmt) { + .elf, .macho, .wasm => .{ .dwarf = .@"32" }, + .coff => .code_view, + .c => switch (target.os.tag) { + .windows, .uefi => .code_view, + else => .{ .dwarf = .@"32" }, + }, + .spirv, .nvptx, .dxcontainer, .hex, .raw, .plan9 => .strip, + }; + }; + + const backend_supports_error_tracing = target_util.backendSupportsFeature( + target.cpu.arch, + target.ofmt, + use_llvm, + .error_return_trace, + ); + + const root_error_tracing = b: { + if (options.root_error_tracing) |x| break :b x; + if (root_strip) break :b false; + if (!backend_supports_error_tracing) break :b false; + break :b switch (root_optimize_mode) { + .Debug => true, + .ReleaseSafe, .ReleaseFast, .ReleaseSmall => false, + }; + }; + + const any_error_tracing = root_error_tracing or options.any_error_tracing; + if (any_error_tracing and !backend_supports_error_tracing) + return error.BackendLacksErrorTracing; + + const rdynamic = options.rdynamic orelse false; + + const dll_export_fns = b: { + if (target.os.tag != .windows) { + if (options.dll_export_fns == true) + return error.DllExportFnsRequiresWindows; + break :b false; + } + if (options.dll_export_fns) |x| break :b x; + if (rdynamic) break :b true; + break :b switch (options.output_mode) { + .Obj, .Exe => false, + .Lib => link_mode == .Dynamic, + }; + }; + + 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, + .any_c_source_files = options.any_c_source_files, + .any_non_single_threaded = options.any_non_single_threaded, + .any_error_tracing = any_error_tracing, + .any_sanitize_thread = options.any_sanitize_thread, + .root_error_tracing = root_error_tracing, + .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, + .wasi_exec_model = wasi_exec_model, + .debug_format = debug_format, + .root_strip = root_strip, + .dll_export_fns = dll_export_fns, + .rdynamic = rdynamic, + }; +} + +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 6e5609f63c02..9d3810f671e5 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -14,7 +14,9 @@ const BigIntMutable = std.math.big.int.Mutable; const Target = std.Target; const Ast = std.zig.Ast; -const Module = @This(); +/// Deprecated, use `Zcu`. +const Module = Zcu; +const Zcu = @This(); const Compilation = @import("Compilation.zig"); const Cache = std.Build.Cache; const Value = @import("value.zig").Value; @@ -35,6 +37,7 @@ const clang = @import("clang.zig"); const InternPool = @import("InternPool.zig"); const Alignment = InternPool.Alignment; const BuiltinFn = std.zig.BuiltinFn; +const LlvmObject = @import("codegen/llvm.zig").Object; comptime { @setEvalBranchQuota(4000); @@ -51,14 +54,17 @@ comptime { /// General-purpose allocator. Used for both temporary and long-term storage. gpa: Allocator, comp: *Compilation, +/// Usually, the LlvmObject is managed by linker code, however, in the case +/// that -fno-emit-bin is specified, the linker code never executes, so we +/// store the LlvmObject here. +llvm_object: ?*LlvmObject, -/// Where build artifacts and incremental compilation metadata serialization go. -zig_cache_artifact_directory: Compilation.Directory, /// Pointer to externally managed resource. 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. @@ -153,8 +159,6 @@ stage1_flags: packed struct { reserved: u2 = 0, } = .{}, -job_queued_update_builtin_zig: bool = true, - compile_log_text: ArrayListUnmanaged(u8) = .{}, emit_h: ?*GlobalEmitH, @@ -622,7 +626,8 @@ pub const Decl = struct { // Sanitize the name for nvptx which is more restrictive. // TODO This should be handled by the backend, not the frontend. Have a // look at how the C backend does it for inspiration. - if (mod.comp.bin_file.options.target.cpu.arch.isNvptx()) { + const cpu_arch = mod.root_mod.resolved_target.result.cpu.arch; + if (cpu_arch.isNvptx()) { for (ip.string_bytes.items[start..]) |*byte| switch (byte.*) { '{', '}', '*', '[', ']', '(', ')', ',', ' ', '\'' => byte.* = '_', else => {}, @@ -949,15 +954,21 @@ pub const File = struct { pub fn deinit(file: *File, mod: *Module) void { const gpa = mod.gpa; + const is_builtin = file.mod.isBuiltin(); log.debug("deinit File {s}", .{file.sub_file_path}); + if (is_builtin) { + file.unloadTree(gpa); + file.unloadZir(gpa); + } else { + gpa.free(file.sub_file_path); + file.unload(gpa); + } file.deleted_decls.deinit(gpa); file.outdated_decls.deinit(gpa); file.references.deinit(gpa); if (file.root_decl.unwrap()) |root_decl| { mod.destroyDecl(root_decl); } - gpa.free(file.sub_file_path); - file.unload(gpa); if (file.prev_zir) |prev_zir| { prev_zir.deinit(gpa); gpa.destroy(prev_zir); @@ -1019,8 +1030,9 @@ pub const File = struct { pub fn destroy(file: *File, mod: *Module) void { const gpa = mod.gpa; + const is_builtin = file.mod.isBuiltin(); file.deinit(mod); - gpa.destroy(file); + if (!is_builtin) gpa.destroy(file); } pub fn renderFullyQualifiedName(file: File, writer: anytype) !void { @@ -2469,108 +2481,111 @@ pub fn init(mod: *Module) !void { try mod.global_error_set.put(gpa, .empty, {}); } -pub fn deinit(mod: *Module) void { - const gpa = mod.gpa; +pub fn deinit(zcu: *Zcu) void { + const gpa = zcu.gpa; - for (mod.import_table.keys()) |key| { + if (zcu.llvm_object) |llvm_object| { + if (build_options.only_c) unreachable; + llvm_object.deinit(); + } + + for (zcu.import_table.keys()) |key| { gpa.free(key); } - var failed_decls = mod.failed_decls; - mod.failed_decls = .{}; - for (mod.import_table.values()) |value| { - value.destroy(mod); + var failed_decls = zcu.failed_decls; + zcu.failed_decls = .{}; + for (zcu.import_table.values()) |value| { + value.destroy(zcu); } - mod.import_table.deinit(gpa); + zcu.import_table.deinit(gpa); - for (mod.embed_table.keys(), mod.embed_table.values()) |path, embed_file| { + for (zcu.embed_table.keys(), zcu.embed_table.values()) |path, embed_file| { gpa.free(path); gpa.destroy(embed_file); } - mod.embed_table.deinit(gpa); + zcu.embed_table.deinit(gpa); - mod.compile_log_text.deinit(gpa); + zcu.compile_log_text.deinit(gpa); - mod.zig_cache_artifact_directory.handle.close(); - mod.local_zir_cache.handle.close(); - mod.global_zir_cache.handle.close(); + zcu.local_zir_cache.handle.close(); + zcu.global_zir_cache.handle.close(); for (failed_decls.values()) |value| { value.destroy(gpa); } failed_decls.deinit(gpa); - if (mod.emit_h) |emit_h| { + if (zcu.emit_h) |emit_h| { for (emit_h.failed_decls.values()) |value| { value.destroy(gpa); } emit_h.failed_decls.deinit(gpa); emit_h.decl_table.deinit(gpa); emit_h.allocated_emit_h.deinit(gpa); - gpa.destroy(emit_h); } - for (mod.failed_files.values()) |value| { + for (zcu.failed_files.values()) |value| { if (value) |msg| msg.destroy(gpa); } - mod.failed_files.deinit(gpa); + zcu.failed_files.deinit(gpa); - for (mod.failed_embed_files.values()) |msg| { + for (zcu.failed_embed_files.values()) |msg| { msg.destroy(gpa); } - mod.failed_embed_files.deinit(gpa); + zcu.failed_embed_files.deinit(gpa); - for (mod.failed_exports.values()) |value| { + for (zcu.failed_exports.values()) |value| { value.destroy(gpa); } - mod.failed_exports.deinit(gpa); + zcu.failed_exports.deinit(gpa); - for (mod.cimport_errors.values()) |*errs| { + for (zcu.cimport_errors.values()) |*errs| { errs.deinit(gpa); } - mod.cimport_errors.deinit(gpa); + zcu.cimport_errors.deinit(gpa); - mod.compile_log_decls.deinit(gpa); + zcu.compile_log_decls.deinit(gpa); - for (mod.decl_exports.values()) |*export_list| { + for (zcu.decl_exports.values()) |*export_list| { export_list.deinit(gpa); } - mod.decl_exports.deinit(gpa); + zcu.decl_exports.deinit(gpa); - for (mod.value_exports.values()) |*export_list| { + for (zcu.value_exports.values()) |*export_list| { export_list.deinit(gpa); } - mod.value_exports.deinit(gpa); + zcu.value_exports.deinit(gpa); - for (mod.export_owners.values()) |*value| { + for (zcu.export_owners.values()) |*value| { freeExportList(gpa, value); } - mod.export_owners.deinit(gpa); + zcu.export_owners.deinit(gpa); - mod.global_error_set.deinit(gpa); + zcu.global_error_set.deinit(gpa); - mod.test_functions.deinit(gpa); + zcu.test_functions.deinit(gpa); - for (mod.global_assembly.values()) |s| { + for (zcu.global_assembly.values()) |s| { gpa.free(s); } - mod.global_assembly.deinit(gpa); + zcu.global_assembly.deinit(gpa); - mod.reference_table.deinit(gpa); + zcu.reference_table.deinit(gpa); { - var it = mod.intern_pool.allocated_namespaces.iterator(0); + var it = zcu.intern_pool.allocated_namespaces.iterator(0); while (it.next()) |namespace| { namespace.decls.deinit(gpa); namespace.usingnamespace_set.deinit(gpa); } } - mod.intern_pool.deinit(gpa); - mod.tmp_hack_arena.deinit(); + zcu.intern_pool.deinit(gpa); + zcu.tmp_hack_arena.deinit(); - mod.capture_scope_parents.deinit(gpa); - mod.runtime_capture_scopes.deinit(gpa); - mod.comptime_capture_scopes.deinit(gpa); + zcu.capture_scope_parents.deinit(gpa); + zcu.runtime_capture_scopes.deinit(gpa); + zcu.comptime_capture_scopes.deinit(gpa); } pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void { @@ -2632,6 +2647,8 @@ comptime { } pub fn astGenFile(mod: *Module, file: *File) !void { + assert(!file.mod.isBuiltin()); + const tracy = trace(@src()); defer tracy.end(); @@ -3076,72 +3093,6 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void { } } -pub fn populateBuiltinFile(mod: *Module) !void { - const tracy = trace(@src()); - defer tracy.end(); - - const comp = mod.comp; - const builtin_mod, const file = blk: { - comp.mutex.lock(); - defer comp.mutex.unlock(); - - const builtin_mod = mod.main_mod.deps.get("builtin").?; - const result = try mod.importPkg(builtin_mod); - break :blk .{ builtin_mod, result.file }; - }; - const gpa = mod.gpa; - file.source = try comp.generateBuiltinZigSource(gpa); - file.source_loaded = true; - - if (builtin_mod.root.statFile(builtin_mod.root_src_path)) |stat| { - if (stat.size != file.source.len) { - log.warn( - "the cached file '{}{s}' had the wrong size. Expected {d}, found {d}. " ++ - "Overwriting with correct file contents now", - .{ builtin_mod.root, builtin_mod.root_src_path, file.source.len, stat.size }, - ); - - try writeBuiltinFile(file, builtin_mod); - } else { - file.stat = .{ - .size = stat.size, - .inode = stat.inode, - .mtime = stat.mtime, - }; - } - } else |err| switch (err) { - error.BadPathName => unreachable, // it's always "builtin.zig" - error.NameTooLong => unreachable, // it's always "builtin.zig" - error.PipeBusy => unreachable, // it's not a pipe - error.WouldBlock => unreachable, // not asking for non-blocking I/O - - error.FileNotFound => try writeBuiltinFile(file, builtin_mod), - - else => |e| return e, - } - - file.tree = try Ast.parse(gpa, file.source, .zig); - file.tree_loaded = true; - assert(file.tree.errors.len == 0); // builtin.zig must parse - - file.zir = try AstGen.generate(gpa, file.tree); - file.zir_loaded = true; - file.status = .success_zir; -} - -fn writeBuiltinFile(file: *File, builtin_mod: *Package.Module) !void { - var af = try builtin_mod.root.atomicFile(builtin_mod.root_src_path, .{}); - defer af.deinit(); - try af.file.writeAll(file.source); - try af.finish(); - - file.stat = .{ - .size = file.source.len, - .inode = 0, // dummy value - .mtime = 0, // dummy value - }; -} - pub fn mapOldZirToNew( gpa: Allocator, old_zir: Zir, @@ -3228,6 +3179,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { .complete => return, .outdated => blk: { + if (build_options.only_c) unreachable; // The exports this Decl performs will be re-discovered, so we remove them here // prior to re-analysis. try mod.deleteDeclExports(decl_index); @@ -3280,14 +3232,14 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { } } -pub fn ensureFuncBodyAnalyzed(mod: *Module, func_index: InternPool.Index) SemaError!void { +pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, func_index: InternPool.Index) SemaError!void { const tracy = trace(@src()); defer tracy.end(); - const ip = &mod.intern_pool; - const func = mod.funcInfo(func_index); + const ip = &zcu.intern_pool; + const func = zcu.funcInfo(func_index); const decl_index = func.owner_decl; - const decl = mod.declPtr(decl_index); + const decl = zcu.declPtr(decl_index); switch (decl.analysis) { .unreferenced => unreachable, @@ -3311,13 +3263,13 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func_index: InternPool.Index) SemaEr .success => return, } - const gpa = mod.gpa; + const gpa = zcu.gpa; var tmp_arena = std.heap.ArenaAllocator.init(gpa); defer tmp_arena.deinit(); const sema_arena = tmp_arena.allocator(); - var air = mod.analyzeFnBody(func_index, sema_arena) catch |err| switch (err) { + var air = zcu.analyzeFnBody(func_index, sema_arena) catch |err| switch (err) { error.AnalysisFail => { if (func.analysis(ip).state == .in_progress) { // If this decl caused the compile error, the analysis field would @@ -3331,25 +3283,22 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func_index: InternPool.Index) SemaEr }; defer air.deinit(gpa); - const comp = mod.comp; - - const no_bin_file = (comp.bin_file.options.emit == null and - comp.emit_asm == null and - comp.emit_llvm_ir == null and - comp.emit_llvm_bc == null); + const comp = zcu.comp; const dump_air = builtin.mode == .Debug and comp.verbose_air; const dump_llvm_ir = builtin.mode == .Debug and (comp.verbose_llvm_ir != null or comp.verbose_llvm_bc != null); - if (no_bin_file and !dump_air and !dump_llvm_ir) return; + if (comp.bin_file == null and zcu.llvm_object == null and !dump_air and !dump_llvm_ir) { + return; + } var liveness = try Liveness.analyze(gpa, air, ip); defer liveness.deinit(gpa); if (dump_air) { - const fqn = try decl.getFullyQualifiedName(mod); + const fqn = try decl.getFullyQualifiedName(zcu); std.debug.print("# Begin Function AIR: {}:\n", .{fqn.fmt(ip)}); - @import("print_air.zig").dump(mod, air, liveness); + @import("print_air.zig").dump(zcu, air, liveness); std.debug.print("# End Function AIR: {}\n\n", .{fqn.fmt(ip)}); } @@ -3365,12 +3314,12 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func_index: InternPool.Index) SemaEr verify.verify() catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => { - try mod.failed_decls.ensureUnusedCapacity(gpa, 1); - mod.failed_decls.putAssumeCapacityNoClobber( + try zcu.failed_decls.ensureUnusedCapacity(gpa, 1); + zcu.failed_decls.putAssumeCapacityNoClobber( decl_index, try Module.ErrorMsg.create( gpa, - decl.srcLoc(mod), + decl.srcLoc(zcu), "invalid liveness: {s}", .{@errorName(err)}, ), @@ -3381,27 +3330,32 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func_index: InternPool.Index) SemaEr }; } - if (no_bin_file and !dump_llvm_ir) return; - - comp.bin_file.updateFunc(mod, func_index, air, liveness) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => { - decl.analysis = .codegen_failure; - return; - }, - else => { - try mod.failed_decls.ensureUnusedCapacity(gpa, 1); - mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create( - gpa, - decl.srcLoc(mod), - "unable to codegen: {s}", - .{@errorName(err)}, - )); - decl.analysis = .codegen_failure_retryable; - return; - }, - }; - return; + if (comp.bin_file) |lf| { + lf.updateFunc(zcu, func_index, air, liveness) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.AnalysisFail => { + decl.analysis = .codegen_failure; + }, + else => { + try zcu.failed_decls.ensureUnusedCapacity(gpa, 1); + zcu.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create( + gpa, + decl.srcLoc(zcu), + "unable to codegen: {s}", + .{@errorName(err)}, + )); + decl.analysis = .codegen_failure_retryable; + }, + }; + } else if (zcu.llvm_object) |llvm_object| { + if (build_options.only_c) unreachable; + llvm_object.updateFunc(zcu, func_index, air, liveness) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.AnalysisFail => { + decl.analysis = .codegen_failure; + }, + }; + } }, } } @@ -3477,6 +3431,9 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { if (file.root_decl != .none) return; const gpa = mod.gpa; + log.debug("semaFile mod={s} sub_file_path={s}", .{ + file.mod.fully_qualified_name, file.sub_file_path, + }); // Because these three things each reference each other, `undefined` // placeholders are used before being set after the struct type gains an @@ -3558,25 +3515,29 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void { new_decl.owns_tv = true; new_decl.analysis = .complete; - if (mod.comp.whole_cache_manifest) |whole_cache_manifest| { - const source = file.getSource(gpa) catch |err| { - try reportRetryableFileError(mod, file, "unable to load source: {s}", .{@errorName(err)}); - return error.AnalysisFail; - }; + const comp = mod.comp; + switch (comp.cache_use) { + .whole => |whole| if (whole.cache_manifest) |man| { + const source = file.getSource(gpa) catch |err| { + try reportRetryableFileError(mod, file, "unable to load source: {s}", .{@errorName(err)}); + return error.AnalysisFail; + }; - const resolved_path = std.fs.path.resolve(gpa, &.{ - file.mod.root.root_dir.path orelse ".", - file.mod.root.sub_path, - file.sub_file_path, - }) catch |err| { - try reportRetryableFileError(mod, file, "unable to resolve path: {s}", .{@errorName(err)}); - return error.AnalysisFail; - }; - errdefer gpa.free(resolved_path); + const resolved_path = std.fs.path.resolve(gpa, &.{ + file.mod.root.root_dir.path orelse ".", + file.mod.root.sub_path, + file.sub_file_path, + }) catch |err| { + try reportRetryableFileError(mod, file, "unable to resolve path: {s}", .{@errorName(err)}); + return error.AnalysisFail; + }; + errdefer gpa.free(resolved_path); - mod.comp.whole_cache_manifest_mutex.lock(); - defer mod.comp.whole_cache_manifest_mutex.unlock(); - try whole_cache_manifest.addFilePostContents(resolved_path, source.bytes, source.stat); + whole.cache_manifest_mutex.lock(); + defer whole.cache_manifest_mutex.unlock(); + try man.addFilePostContents(resolved_path, source.bytes, source.stat); + }, + .incremental => {}, } } @@ -3588,6 +3549,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { defer tracy.end(); const decl = mod.declPtr(decl_index); + const ip = &mod.intern_pool; if (decl.getFileScope(mod).status != .success_zir) { return error.AnalysisFail; @@ -3597,20 +3559,18 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { const zir = decl.getFileScope(mod).zir; const zir_datas = zir.instructions.items(.data); - // 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; const std_decl = mod.declPtr(std_file.root_decl.unwrap().?); const std_namespace = std_decl.getInnerNamespace(mod).?; - const builtin_str = try mod.intern_pool.getOrPutString(gpa, "builtin"); + const builtin_str = try ip.getOrPutString(gpa, "builtin"); const builtin_decl = mod.declPtr(std_namespace.decls.getKeyAdapted(builtin_str, DeclAdapter{ .mod = mod }) orelse break :blk .none); const builtin_namespace = builtin_decl.getInnerNamespaceIndex(mod).unwrap() orelse break :blk .none; if (decl.src_namespace != builtin_namespace) break :blk .none; // We're in builtin.zig. This could be a builtin we need to add to a specific InternPool index. - const decl_name = mod.intern_pool.stringToSlice(decl.name); for ([_]struct { []const u8, InternPool.Index }{ .{ "AtomicOrder", .atomic_order_type }, .{ "AtomicRmwOp", .atomic_rmw_op_type }, @@ -3624,6 +3584,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { .{ "ExternOptions", .extern_options_type }, .{ "Type", .type_info_type }, }) |pair| { + const decl_name = ip.stringToSlice(decl.name); if (std.mem.eql(u8, decl_name, pair[0])) { break :blk pair[1]; } @@ -3719,7 +3680,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { return true; } - const ip = &mod.intern_pool; switch (ip.indexToKey(decl_tv.val.toIntern())) { .func => |func| { const owns_tv = func.owner_decl == decl_index; @@ -3863,25 +3823,24 @@ pub const ImportFileResult = struct { is_pkg: bool, }; -/// https://github.com/ziglang/zig/issues/14307 -pub fn importPkg(mod: *Module, pkg: *Package.Module) !ImportFileResult { - const gpa = mod.gpa; +pub fn importPkg(zcu: *Zcu, mod: *Package.Module) !ImportFileResult { + const gpa = zcu.gpa; // The resolved path is used as the key in the import table, to detect if // an import refers to the same as another, despite different relative paths // or differently mapped package names. const resolved_path = try std.fs.path.resolve(gpa, &.{ - pkg.root.root_dir.path orelse ".", - pkg.root.sub_path, - pkg.root_src_path, + mod.root.root_dir.path orelse ".", + mod.root.sub_path, + mod.root_src_path, }); var keep_resolved_path = false; defer if (!keep_resolved_path) gpa.free(resolved_path); - const gop = try mod.import_table.getOrPut(gpa, resolved_path); - errdefer _ = mod.import_table.pop(); + const gop = try zcu.import_table.getOrPut(gpa, resolved_path); + errdefer _ = zcu.import_table.pop(); if (gop.found_existing) { - try gop.value_ptr.*.addReference(mod.*, .{ .root = pkg }); + try gop.value_ptr.*.addReference(zcu.*, .{ .root = mod }); return ImportFileResult{ .file = gop.value_ptr.*, .is_new = false, @@ -3889,7 +3848,18 @@ pub fn importPkg(mod: *Module, pkg: *Package.Module) !ImportFileResult { }; } - const sub_file_path = try gpa.dupe(u8, pkg.root_src_path); + if (mod.builtin_file) |builtin_file| { + keep_resolved_path = true; // It's now owned by import_table. + gop.value_ptr.* = builtin_file; + try builtin_file.addReference(zcu.*, .{ .root = mod }); + return .{ + .file = builtin_file, + .is_new = false, + .is_pkg = true, + }; + } + + const sub_file_path = try gpa.dupe(u8, mod.root_src_path); errdefer gpa.free(sub_file_path); const new_file = try gpa.create(File); @@ -3907,10 +3877,10 @@ pub fn importPkg(mod: *Module, pkg: *Package.Module) !ImportFileResult { .tree = undefined, .zir = undefined, .status = .never_loaded, - .mod = pkg, + .mod = mod, .root_decl = .none, }; - try new_file.addReference(mod.*, .{ .root = pkg }); + try new_file.addReference(zcu.*, .{ .root = mod }); return ImportFileResult{ .file = new_file, .is_new = true, @@ -3924,10 +3894,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); @@ -4118,12 +4085,16 @@ fn newEmbedFile( const actual_read = try file.readAll(ptr); if (actual_read != size) return error.UnexpectedEndOfFile; - if (mod.comp.whole_cache_manifest) |whole_cache_manifest| { - const copied_resolved_path = try gpa.dupe(u8, resolved_path); - errdefer gpa.free(copied_resolved_path); - mod.comp.whole_cache_manifest_mutex.lock(); - defer mod.comp.whole_cache_manifest_mutex.unlock(); - try whole_cache_manifest.addFilePostContents(copied_resolved_path, ptr, stat); + const comp = mod.comp; + switch (comp.cache_use) { + .whole => |whole| if (whole.cache_manifest) |man| { + const copied_resolved_path = try gpa.dupe(u8, resolved_path); + errdefer gpa.free(copied_resolved_path); + whole.cache_manifest_mutex.lock(); + defer whole.cache_manifest_mutex.unlock(); + try man.addFilePostContents(copied_resolved_path, ptr, stat); + }, + .incremental => {}, } const array_ty = try ip.get(gpa, .{ .array_type = .{ @@ -4312,14 +4283,14 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err 1 => blk: { // test decl with no name. Skip the part where we check against // the test name filter. - if (!comp.bin_file.options.is_test) break :blk false; + if (!comp.config.is_test) break :blk false; if (decl_mod != mod.main_mod) break :blk false; try mod.test_functions.put(gpa, new_decl_index, {}); break :blk true; }, else => blk: { if (!is_named_test) break :blk false; - if (!comp.bin_file.options.is_test) break :blk false; + if (!comp.config.is_test) break :blk false; if (decl_mod != mod.main_mod) break :blk false; if (comp.test_filter) |test_filter| { if (mem.indexOf(u8, ip.stringToSlice(decl_name), test_filter) == null) { @@ -4331,6 +4302,9 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err }, }; if (want_analysis) { + log.debug("scanDecl queue analyze_decl file='{s}' decl_name='{s}' decl_index={d}", .{ + namespace.file_scope.sub_file_path, ip.stringToSlice(decl_name), new_decl_index, + }); comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl_index }); } new_decl.is_pub = is_pub; @@ -4373,14 +4347,9 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err decl.has_linksection_or_addrspace = has_linksection_or_addrspace; decl.zir_decl_index = @enumFromInt(decl_sub_index); if (decl.getOwnedFunction(mod) != null) { - switch (comp.bin_file.tag) { - .coff, .elf, .macho, .plan9 => { - // TODO Look into detecting when this would be unnecessary by storing enough state - // in `Decl` to notice that the line number did not change. - comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); - }, - .c, .wasm, .spirv, .nvptx => {}, - } + // TODO Look into detecting when this would be unnecessary by storing enough state + // in `Decl` to notice that the line number did not change. + comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); } } @@ -4466,7 +4435,9 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) Allocator.Error!void } }, } - try mod.comp.bin_file.deleteDeclExport(decl_index, exp.opts.name); + if (mod.comp.bin_file) |lf| { + try lf.deleteDeclExport(decl_index, exp.opts.name); + } if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| { failed_kv.value.destroy(mod.gpa); } @@ -4628,7 +4599,7 @@ pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocato // If we don't get an error return trace from a caller, create our own. if (func.analysis(ip).calls_or_awaits_errorable_fn and - mod.comp.bin_file.options.error_return_tracing and + mod.comp.config.any_error_tracing and !sema.fn_ret_ty.isError(mod)) { sema.setupErrorReturnTrace(&inner_block, last_arg_index) catch |err| switch (err) { @@ -4721,25 +4692,6 @@ pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocato }; } -fn markOutdatedDecl(mod: *Module, decl_index: Decl.Index) !void { - const decl = mod.declPtr(decl_index); - try mod.comp.work_queue.writeItem(.{ .analyze_decl = decl_index }); - if (mod.failed_decls.fetchSwapRemove(decl_index)) |kv| { - kv.value.destroy(mod.gpa); - } - if (mod.cimport_errors.fetchSwapRemove(decl_index)) |kv| { - var errors = kv.value; - errors.deinit(mod.gpa); - } - if (mod.emit_h) |emit_h| { - if (emit_h.failed_decls.fetchSwapRemove(decl_index)) |kv| { - kv.value.destroy(mod.gpa); - } - } - _ = mod.compile_log_decls.swapRemove(decl_index); - decl.analysis = .outdated; -} - pub fn createNamespace(mod: *Module, initialization: Namespace) !Namespace.Index { return mod.intern_pool.createNamespace(mod.gpa, initialization); } @@ -4875,12 +4827,18 @@ pub fn errNoteNonLazy( }; } -pub fn getTarget(mod: Module) Target { - return mod.comp.bin_file.options.target; +/// Deprecated. There is no global target for a Zig Compilation Unit. Instead, +/// look up the target based on the Module that contains the source code being +/// analyzed. +pub fn getTarget(zcu: Module) Target { + return zcu.root_mod.resolved_target.result; } -pub fn optimizeMode(mod: Module) std.builtin.OptimizeMode { - return mod.comp.bin_file.options.optimize_mode; +/// Deprecated. There is no global optimization mode for a Zig Compilation +/// Unit. Instead, look up the optimization mode based on the Module that +/// contains the source code being analyzed. +pub fn optimizeMode(zcu: Module) std.builtin.OptimizeMode { + return zcu.root_mod.optimize_mode; } fn lockAndClearFileCompileError(mod: *Module, file: *File) void { @@ -5288,43 +5246,57 @@ pub fn processExports(mod: *Module) !void { const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, *Export); fn processExportsInner( - mod: *Module, + zcu: *Zcu, symbol_exports: *SymbolExports, exported: Exported, exports: []const *Export, ) error{OutOfMemory}!void { - const gpa = mod.gpa; + const gpa = zcu.gpa; for (exports) |new_export| { const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name); if (gop.found_existing) { new_export.status = .failed_retryable; - try mod.failed_exports.ensureUnusedCapacity(gpa, 1); - const src_loc = new_export.getSrcLoc(mod); + try zcu.failed_exports.ensureUnusedCapacity(gpa, 1); + const src_loc = new_export.getSrcLoc(zcu); const msg = try ErrorMsg.create(gpa, src_loc, "exported symbol collision: {}", .{ - new_export.opts.name.fmt(&mod.intern_pool), + new_export.opts.name.fmt(&zcu.intern_pool), }); errdefer msg.destroy(gpa); const other_export = gop.value_ptr.*; - const other_src_loc = other_export.getSrcLoc(mod); - try mod.errNoteNonLazy(other_src_loc, msg, "other symbol here", .{}); - mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg); + const other_src_loc = other_export.getSrcLoc(zcu); + try zcu.errNoteNonLazy(other_src_loc, msg, "other symbol here", .{}); + zcu.failed_exports.putAssumeCapacityNoClobber(new_export, msg); new_export.status = .failed; } else { gop.value_ptr.* = new_export; } } - mod.comp.bin_file.updateExports(mod, exported, exports) catch |err| switch (err) { + if (zcu.comp.bin_file) |lf| { + try handleUpdateExports(zcu, exports, lf.updateExports(zcu, exported, exports)); + } else if (zcu.llvm_object) |llvm_object| { + if (build_options.only_c) unreachable; + try handleUpdateExports(zcu, exports, llvm_object.updateExports(zcu, exported, exports)); + } +} + +fn handleUpdateExports( + zcu: *Zcu, + exports: []const *Export, + result: link.File.UpdateExportsError!void, +) Allocator.Error!void { + const gpa = zcu.gpa; + result catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, - else => { + error.AnalysisFail => { const new_export = exports[0]; new_export.status = .failed_retryable; - try mod.failed_exports.ensureUnusedCapacity(gpa, 1); - const src_loc = new_export.getSrcLoc(mod); + try zcu.failed_exports.ensureUnusedCapacity(gpa, 1); + const src_loc = new_export.getSrcLoc(zcu); const msg = try ErrorMsg.create(gpa, src_loc, "unable to export: {s}", .{ @errorName(err), }); - mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg); + zcu.failed_exports.putAssumeCapacityNoClobber(new_export, msg); }, }; } @@ -5335,7 +5307,7 @@ pub fn populateTestFunctions( ) !void { const gpa = mod.gpa; const ip = &mod.intern_pool; - const builtin_mod = mod.main_mod.deps.get("builtin").?; + const builtin_mod = mod.root_mod.getBuiltinDependency(); const builtin_file = (mod.importPkg(builtin_mod) catch unreachable).file; const root_decl = mod.declPtr(builtin_file.root_decl.unwrap().?); const builtin_namespace = mod.namespacePtr(root_decl.src_namespace); @@ -5467,39 +5439,39 @@ pub fn populateTestFunctions( try mod.linkerUpdateDecl(decl_index); } -pub fn linkerUpdateDecl(mod: *Module, decl_index: Decl.Index) !void { - const comp = mod.comp; - - const no_bin_file = (comp.bin_file.options.emit == null and - comp.emit_asm == null and - comp.emit_llvm_ir == null and - comp.emit_llvm_bc == null); +pub fn linkerUpdateDecl(zcu: *Zcu, decl_index: Decl.Index) !void { + const comp = zcu.comp; - const dump_llvm_ir = builtin.mode == .Debug and (comp.verbose_llvm_ir != null or comp.verbose_llvm_bc != null); - - if (no_bin_file and !dump_llvm_ir) return; - - const decl = mod.declPtr(decl_index); - - comp.bin_file.updateDecl(mod, decl_index) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => { - decl.analysis = .codegen_failure; - return; - }, - else => { - const gpa = mod.gpa; - try mod.failed_decls.ensureUnusedCapacity(gpa, 1); - mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create( - gpa, - decl.srcLoc(mod), - "unable to codegen: {s}", - .{@errorName(err)}, - )); - decl.analysis = .codegen_failure_retryable; - return; - }, - }; + if (comp.bin_file) |lf| { + lf.updateDecl(zcu, decl_index) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.AnalysisFail => { + const decl = zcu.declPtr(decl_index); + decl.analysis = .codegen_failure; + }, + else => { + const decl = zcu.declPtr(decl_index); + const gpa = zcu.gpa; + try zcu.failed_decls.ensureUnusedCapacity(gpa, 1); + zcu.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create( + gpa, + decl.srcLoc(zcu), + "unable to codegen: {s}", + .{@errorName(err)}, + )); + decl.analysis = .codegen_failure_retryable; + }, + }; + } else if (zcu.llvm_object) |llvm_object| { + if (build_options.only_c) unreachable; + llvm_object.updateDecl(zcu, decl_index) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.AnalysisFail => { + const decl = zcu.declPtr(decl_index); + decl.analysis = .codegen_failure; + }, + }; + } } fn reportRetryableFileError( @@ -5592,10 +5564,6 @@ pub fn addGlobalAssembly(mod: *Module, decl_index: Decl.Index, source: []const u } } -pub fn wantDllExports(mod: Module) bool { - return mod.comp.bin_file.options.dll_export_fns and mod.getTarget().os.tag == .windows; -} - pub fn getDeclExports(mod: Module, decl_index: Decl.Index) []const *Export { if (mod.decl_exports.get(decl_index)) |l| { return l.items; @@ -5622,21 +5590,11 @@ pub const Feature = enum { safety_checked_instructions, }; -pub fn backendSupportsFeature(mod: Module, feature: Feature) bool { - return switch (feature) { - .panic_fn => mod.comp.bin_file.options.target.ofmt == .c or - mod.comp.bin_file.options.use_llvm or - mod.comp.bin_file.options.target.cpu.arch == .x86_64, - .panic_unwrap_error => mod.comp.bin_file.options.target.ofmt == .c or - mod.comp.bin_file.options.use_llvm, - .safety_check_formatted => mod.comp.bin_file.options.target.ofmt == .c or - mod.comp.bin_file.options.use_llvm, - .error_return_trace => mod.comp.bin_file.options.use_llvm, - .is_named_enum_value => mod.comp.bin_file.options.use_llvm, - .error_set_has_value => mod.comp.bin_file.options.use_llvm or mod.comp.bin_file.options.target.isWasm(), - .field_reordering => mod.comp.bin_file.options.use_llvm, - .safety_checked_instructions => mod.comp.bin_file.options.use_llvm, - }; +pub fn backendSupportsFeature(zcu: Module, feature: Feature) bool { + const cpu_arch = zcu.root_mod.resolved_target.result.cpu.arch; + const ofmt = zcu.root_mod.resolved_target.result.ofmt; + const use_llvm = zcu.comp.config.use_llvm; + return target_util.backendSupportsFeature(cpu_arch, ofmt, use_llvm, feature); } /// Shortcut for calling `intern_pool.get`. diff --git a/src/Package.zig b/src/Package.zig index ab06ca985208..da2e214154b4 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -88,10 +88,10 @@ pub const Path = struct { p: Path, sub_path: []const u8, options: fs.Dir.AtomicFileOptions, + buf: *[fs.MAX_PATH_BYTES]u8, ) !fs.AtomicFile { - var buf: [fs.MAX_PATH_BYTES]u8 = undefined; const joined_path = if (p.sub_path.len == 0) sub_path else p: { - break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{ + break :p std.fmt.bufPrint(buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{ p.sub_path, sub_path, }) catch return error.NameTooLong; }; @@ -108,6 +108,16 @@ pub const Path = struct { return p.root_dir.handle.access(joined_path, flags); } + pub fn makePath(p: Path, sub_path: []const u8) !void { + var buf: [fs.MAX_PATH_BYTES]u8 = undefined; + const joined_path = if (p.sub_path.len == 0) sub_path else p: { + break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{ + p.sub_path, sub_path, + }) catch return error.NameTooLong; + }; + return p.root_dir.handle.makePath(joined_path); + } + pub fn format( self: Path, comptime fmt_string: []const u8, diff --git a/src/Package/Module.zig b/src/Package/Module.zig index 0c8d40bffa65..66d5a21eab7d 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,465 @@ fully_qualified_name: []const u8, /// responsible for detecting these names and using the correct package. deps: Deps = .{}, +resolved_target: ResolvedTarget, +optimize_mode: std.builtin.OptimizeMode, +code_model: std.builtin.CodeModel, +single_threaded: bool, +error_tracing: bool, +valgrind: bool, +pic: bool, +strip: bool, +omit_frame_pointer: bool, +stack_check: bool, +stack_protector: u32, +red_zone: bool, +sanitize_c: bool, +sanitize_thread: bool, +unwind_tables: bool, +cc_argv: []const []const u8, +/// (SPIR-V) whether to generate a structured control flow graph or not +structured_cfg: bool, + +/// If the module is an `@import("builtin")` module, this is the `File` that +/// is preallocated for it. Otherwise this field is null. +builtin_file: ?*File, + pub const Deps = std.StringArrayHashMapUnmanaged(*Module); +pub fn isBuiltin(m: Module) bool { + return m.builtin_file != null; +} + pub const Tree = struct { /// Each `Package` exposes a `Module` with build.zig as its root source file. build_module_table: std.AutoArrayHashMapUnmanaged(MultiHashHexDigest, *Module), }; -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, + structured_cfg: ?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 { + if (options.inherited.sanitize_thread == true) assert(options.global.any_sanitize_thread); + if (options.inherited.single_threaded == false) assert(options.global.any_non_single_threaded); + if (options.inherited.unwind_tables == true) assert(options.global.any_unwind_tables); + if (options.inherited.error_tracing == true) assert(options.global.any_error_tracing); + + 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; + break :b options.global.root_strip; + }; + + 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; + break :b options.global.root_error_tracing; + }; + + 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 false; + } + 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: { + const use_zig_backend = options.global.have_zcu or + (options.global.any_c_source_files and options.global.c_frontend == .aro); + if (use_zig_backend and !target_util.supportsStackProtector(target, zig_backend)) { + if (options.inherited.stack_protector) |x| { + if (x > 0) return error.StackProtectorUnsupportedByTarget; + } + break :sp 0; + } + + if (options.global.any_c_source_files and options.global.c_frontend == .clang and + !target_util.clangSupportsStackProtector(target)) + { + 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 structured_cfg = b: { + if (options.inherited.structured_cfg) |x| break :b x; + if (options.parent) |p| break :b p.structured_cfg; + // We always want a structured control flow in shaders. This option is + // only relevant for OpenCL kernels. + break :b switch (target.os.tag) { + .opencl => false, + else => true, + }; + }; + + 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 mod = try arena.create(Module); + mod.* = .{ + .root = options.paths.root, + .root_src_path = options.paths.root_src_path, + .fully_qualified_name = options.fully_qualified_name, + .resolved_target = .{ + .result = target, + .is_native_os = resolved_target.is_native_os, + .is_native_abi = resolved_target.is_native_abi, + .llvm_cpu_features = llvm_cpu_features, + }, + .optimize_mode = optimize_mode, + .single_threaded = single_threaded, + .error_tracing = error_tracing, + .valgrind = valgrind, + .pic = pic, + .strip = strip, + .omit_frame_pointer = omit_frame_pointer, + .stack_check = stack_check, + .stack_protector = stack_protector, + .code_model = code_model, + .red_zone = red_zone, + .sanitize_c = sanitize_c, + .sanitize_thread = sanitize_thread, + .unwind_tables = unwind_tables, + .cc_argv = options.cc_argv, + .structured_cfg = structured_cfg, + .builtin_file = null, + }; + + const opt_builtin_mod = options.builtin_mod orelse b: { + if (!options.global.have_zcu) break :b null; + + const generated_builtin_source = try Builtin.generate(.{ + .target = target, + .zig_backend = zig_backend, + .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 new_file = try arena.create(File); + + const digest = Cache.HashHelper.oneShot(generated_builtin_source); + const builtin_sub_path = try arena.dupe(u8, "b" ++ std.fs.path.sep_str ++ digest); + const new = try arena.create(Module); + 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, + .sanitize_c = sanitize_c, + .sanitize_thread = sanitize_thread, + .unwind_tables = unwind_tables, + .cc_argv = &.{}, + .structured_cfg = structured_cfg, + .builtin_file = new_file, + }; + new_file.* = .{ + .sub_file_path = "builtin.zig", + .source = generated_builtin_source, + .source_loaded = true, + .tree_loaded = false, + .zir_loaded = false, + .stat = undefined, + .tree = undefined, + .zir = undefined, + .status = .never_loaded, + .mod = new, + .root_decl = .none, + }; + break :b new; + }; + + if (opt_builtin_mod) |builtin_mod| { + 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, + .structured_cfg = undefined, + .builtin_file = null, + }; + return mod; +} + +/// Asserts that the module has a builtin module, which is not true for non-zig +/// modules such as ones only used for `@embedFile`, or the root module when +/// there is no Zig Compilation Unit. +pub fn getBuiltinDependency(m: Module) *Module { + const result = m.deps.values()[0]; + assert(result.isBuiltin()); + return result; } const Module = @This(); @@ -32,3 +480,9 @@ 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"); +const File = @import("../Module.zig").File; diff --git a/src/Sema.zig b/src/Sema.zig index 2f1dc0732829..a01dd174a85d 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -784,6 +784,11 @@ pub const Block = struct { } } + pub fn ownerModule(block: Block) *Package.Module { + const zcu = block.sema.mod; + return zcu.namespacePtr(block.namespace).file_scope.mod; + } + pub fn startAnonDecl(block: *Block) !WipAnonDecl { return WipAnonDecl{ .block = block, @@ -1324,13 +1329,13 @@ fn analyzeBodyInner( }, .dbg_block_begin => { dbg_block_begins += 1; - try sema.zirDbgBlockBegin(block); + try zirDbgBlockBegin(block); i += 1; continue; }, .dbg_block_end => { dbg_block_begins -= 1; - try sema.zirDbgBlockEnd(block); + try zirDbgBlockEnd(block); i += 1; continue; }, @@ -1825,17 +1830,17 @@ fn analyzeBodyInner( }; // balance out dbg_block_begins in case of early noreturn - const noreturn_inst = block.instructions.popOrNull(); - while (dbg_block_begins > 0) { - dbg_block_begins -= 1; - if (block.is_comptime or mod.comp.bin_file.options.strip) continue; - - _ = try block.addInst(.{ - .tag = .dbg_block_end, - .data = undefined, - }); + if (!block.is_comptime and !block.ownerModule().strip) { + const noreturn_inst = block.instructions.popOrNull(); + while (dbg_block_begins > 0) { + dbg_block_begins -= 1; + _ = try block.addInst(.{ + .tag = .dbg_block_end, + .data = undefined, + }); + } + if (noreturn_inst) |some| try block.instructions.append(sema.gpa, some); } - if (noreturn_inst) |some| try block.instructions.append(sema.gpa, some); // We may have overwritten the capture scope due to a `repeat` instruction where // the body had a capture; restore it now. @@ -2040,9 +2045,10 @@ fn analyzeAsType( pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void { const mod = sema.mod; + const comp = mod.comp; const gpa = sema.gpa; const ip = &mod.intern_pool; - if (!mod.backendSupportsFeature(.error_return_trace)) return; + if (!comp.config.any_error_tracing) return; assert(!block.is_comptime); var err_trace_block = block.makeSubBlock(); @@ -5733,7 +5739,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr // Ignore the result, all the relevant operations have written to c_import_buf already. _ = try sema.analyzeBodyBreak(&child_block, body); - var c_import_res = comp.cImport(c_import_buf.items) catch |err| + var c_import_res = comp.cImport(c_import_buf.items, parent_block.ownerModule()) catch |err| return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); defer c_import_res.deinit(gpa); @@ -5742,7 +5748,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr const msg = try sema.errMsg(&child_block, src, "C import failed", .{}); errdefer msg.destroy(gpa); - if (!comp.bin_file.options.link_libc) + if (!comp.config.link_libc) try sema.errNote(&child_block, src, msg, "libc headers not available; compilation does not link against libc", .{}); const gop = try mod.cimport_errors.getOrPut(gpa, sema.owner_decl_index); @@ -5754,14 +5760,39 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr }; return sema.failWithOwnedErrorMsg(&child_block, msg); } - const c_import_mod = try Package.Module.create(comp.arena.allocator(), .{ - .root = .{ - .root_dir = Compilation.Directory.cwd(), - .sub_path = std.fs.path.dirname(c_import_res.out_zig_path) orelse "", + const parent_mod = parent_block.ownerModule(); + const c_import_mod = Package.Module.create(comp.arena, .{ + .global_cache_directory = comp.global_cache_directory, + .paths = .{ + .root = .{ + .root_dir = Compilation.Directory.cwd(), + .sub_path = std.fs.path.dirname(c_import_res.out_zig_path) orelse "", + }, + .root_src_path = std.fs.path.basename(c_import_res.out_zig_path), }, - .root_src_path = std.fs.path.basename(c_import_res.out_zig_path), .fully_qualified_name = c_import_res.out_zig_path, - }); + .cc_argv = parent_mod.cc_argv, + .inherited = .{}, + .global = comp.config, + .parent = parent_mod, + .builtin_mod = parent_mod.getBuiltinDependency(), + }) catch |err| switch (err) { + // None of these are possible because we are creating a package with + // the exact same configuration as the parent package, which already + // passed these checks. + error.ValgrindUnsupportedOnTarget => unreachable, + error.TargetRequiresSingleThreaded => unreachable, + error.BackendRequiresSingleThreaded => unreachable, + error.TargetRequiresPic => unreachable, + error.PieRequiresPic => unreachable, + error.DynamicLinkingRequiresPic => unreachable, + error.TargetHasNoRedZone => unreachable, + error.StackCheckUnsupportedByTarget => unreachable, + error.StackProtectorUnsupportedByTarget => unreachable, + error.StackProtectorUnavailableWithoutLibC => unreachable, + + else => |e| return e, + }; const result = mod.importPkg(c_import_mod) catch |err| return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)}); @@ -6267,7 +6298,7 @@ fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!voi // ZIR code that possibly will need to generate runtime code. So error messages // and other source locations must not rely on sema.src being set from dbg_stmt // instructions. - if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return; + if (block.is_comptime or block.ownerModule().strip) return; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; @@ -6292,8 +6323,8 @@ fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!voi }); } -fn zirDbgBlockBegin(sema: *Sema, block: *Block) CompileError!void { - if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return; +fn zirDbgBlockBegin(block: *Block) CompileError!void { + if (block.is_comptime or block.ownerModule().strip) return; _ = try block.addInst(.{ .tag = .dbg_block_begin, @@ -6301,8 +6332,8 @@ fn zirDbgBlockBegin(sema: *Sema, block: *Block) CompileError!void { }); } -fn zirDbgBlockEnd(sema: *Sema, block: *Block) CompileError!void { - if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return; +fn zirDbgBlockEnd(block: *Block) CompileError!void { + if (block.is_comptime or block.ownerModule().strip) return; _ = try block.addInst(.{ .tag = .dbg_block_end, @@ -6316,7 +6347,7 @@ fn zirDbgVar( inst: Zir.Inst.Index, air_tag: Air.Inst.Tag, ) CompileError!void { - if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return; + if (block.is_comptime or block.ownerModule().strip) return; const str_op = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_op; const operand = try sema.resolveInst(str_op.operand); @@ -6513,8 +6544,7 @@ pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref const gpa = sema.gpa; const src = sema.src; - if (!mod.backendSupportsFeature(.error_return_trace)) return .none; - if (!mod.comp.bin_file.options.error_return_tracing) return .none; + if (!block.ownerModule().error_tracing) return .none; if (block.is_comptime) return .none; @@ -6698,7 +6728,7 @@ fn zirCall( input_is_error = false; } - if (mod.backendSupportsFeature(.error_return_trace) and mod.comp.bin_file.options.error_return_tracing and + if (block.ownerModule().error_tracing and !block.is_comptime and !block.is_typeof and (input_is_error or pop_error_return_trace)) { const return_ty = sema.typeOf(call_inst); @@ -7451,7 +7481,7 @@ fn analyzeCall( new_fn_info.return_type = sema.fn_ret_ty.toIntern(); const new_func_resolved_ty = try mod.funcType(new_fn_info); if (!is_comptime_call and !block.is_typeof) { - try sema.emitDbgInline(block, prev_fn_index, module_fn_index, new_func_resolved_ty, .dbg_inline_begin); + try emitDbgInline(block, prev_fn_index, module_fn_index, new_func_resolved_ty, .dbg_inline_begin); const zir_tags = sema.code.instructions.items(.tag); for (fn_info.param_body) |param| switch (zir_tags[@intFromEnum(param)]) { @@ -7489,7 +7519,7 @@ fn analyzeCall( if (!is_comptime_call and !block.is_typeof and sema.typeOf(result).zigTypeTag(mod) != .NoReturn) { - try sema.emitDbgInline( + try emitDbgInline( block, module_fn_index, prev_fn_index, @@ -8062,15 +8092,13 @@ fn resolveTupleLazyValues(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) } fn emitDbgInline( - sema: *Sema, block: *Block, old_func: InternPool.Index, new_func: InternPool.Index, new_func_ty: Type, tag: Air.Inst.Tag, ) CompileError!void { - const mod = sema.mod; - if (mod.comp.bin_file.options.strip) return; + if (block.ownerModule().strip) return; // Recursive inline call; no dbg_inline needed. if (old_func == new_func) return; @@ -9058,7 +9086,7 @@ fn resolveGenericBody( /// Given a library name, examines if the library name should end up in /// `link.File.Options.system_libs` table (for example, libc is always -/// specified via dedicated flag `link.File.Options.link_libc` instead), +/// specified via dedicated flag `link_libc` instead), /// and puts it there if it doesn't exist. /// It also dupes the library name which can then be saved as part of the /// respective `Decl` (either `ExternFn` or `Var`). @@ -9075,8 +9103,8 @@ fn handleExternLibName( const comp = mod.comp; const target = mod.getTarget(); log.debug("extern fn symbol expected in lib '{s}'", .{lib_name}); - if (target_util.is_libc_lib_name(target, lib_name)) { - if (!comp.bin_file.options.link_libc) { + if (target.is_libc_lib_name(lib_name)) { + if (!comp.config.link_libc) { return sema.fail( block, src_loc, @@ -9086,22 +9114,25 @@ fn handleExternLibName( } break :blk; } - if (target_util.is_libcpp_lib_name(target, lib_name)) { - if (!comp.bin_file.options.link_libcpp) { - return sema.fail( - block, - src_loc, - "dependency on libc++ must be explicitly specified in the build command", - .{}, - ); - } + if (target.is_libcpp_lib_name(lib_name)) { + if (!comp.config.link_libcpp) return sema.fail( + block, + src_loc, + "dependency on libc++ must be explicitly specified in the build command", + .{}, + ); break :blk; } if (mem.eql(u8, lib_name, "unwind")) { - comp.bin_file.options.link_libunwind = true; + if (!comp.config.link_libunwind) return sema.fail( + block, + src_loc, + "dependency on libunwind must be explicitly specified in the build command", + .{}, + ); break :blk; } - if (!target.isWasm() and !comp.bin_file.options.pic) { + if (!target.isWasm() and !block.ownerModule().pic) { return sema.fail( block, src_loc, @@ -18728,18 +18759,14 @@ fn retWithErrTracing( fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool { const mod = sema.mod; - if (!mod.backendSupportsFeature(.error_return_trace)) return false; - - return fn_ret_ty.isError(mod) and - mod.comp.bin_file.options.error_return_tracing; + return fn_ret_ty.isError(mod) and mod.comp.config.any_error_tracing; } fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].save_err_ret_index; - if (!mod.backendSupportsFeature(.error_return_trace)) return; - if (!mod.comp.bin_file.options.error_return_tracing) return; + if (!block.ownerModule().error_tracing) return; // This is only relevant at runtime. if (block.is_comptime or block.is_typeof) return; @@ -18764,9 +18791,8 @@ fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) const mod = sema.mod; const ip = &mod.intern_pool; - if (!mod.backendSupportsFeature(.error_return_trace)) return; if (!ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn) return; - if (!mod.comp.bin_file.options.error_return_tracing) return; + if (!start_block.ownerModule().error_tracing) return; const tracy = trace(@src()); defer tracy.end(); @@ -20037,8 +20063,7 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { if (sema.owner_func_index != .none and ip.funcAnalysis(sema.owner_func_index).calls_or_awaits_errorable_fn and - mod.comp.bin_file.options.error_return_tracing and - mod.backendSupportsFeature(.error_return_trace)) + block.ownerModule().error_tracing) { return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty); } @@ -34496,7 +34521,9 @@ pub fn resolveFnTypes(sema: *Sema, fn_ty: Type) CompileError!void { try sema.resolveTypeFully(Type.fromInterned(fn_ty_info.return_type)); - if (mod.comp.bin_file.options.error_return_tracing and Type.fromInterned(fn_ty_info.return_type).isError(mod)) { + if (mod.comp.config.any_error_tracing and + Type.fromInterned(fn_ty_info.return_type).isError(mod)) + { // Ensure the type exists so that backends can assume that. _ = try sema.getBuiltinType("StackTrace"); } @@ -36668,7 +36695,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/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 9ad7b0bbaa07..ee5e58ae05e9 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -329,7 +329,7 @@ const BigTomb = struct { const Self = @This(); pub fn generate( - bin_file: *link.File, + lf: *link.File, src_loc: Module.SrcLoc, func_index: InternPool.Index, air: Air, @@ -337,31 +337,30 @@ pub fn generate( code: *std.ArrayList(u8), debug_output: DebugInfoOutput, ) CodeGenError!Result { - if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { - @panic("Attempted to compile for architecture that was disabled by build configuration"); - } - - const mod = bin_file.options.module.?; - const func = mod.funcInfo(func_index); - const fn_owner_decl = mod.declPtr(func.owner_decl); + const gpa = lf.comp.gpa; + const zcu = lf.comp.module.?; + const func = zcu.funcInfo(func_index); + const fn_owner_decl = zcu.declPtr(func.owner_decl); assert(fn_owner_decl.has_tv); const fn_type = fn_owner_decl.ty; + const namespace = zcu.namespacePtr(fn_owner_decl.src_namespace); + const target = &namespace.file_scope.mod.resolved_target.result; - var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); + var branch_stack = std.ArrayList(Branch).init(gpa); defer { assert(branch_stack.items.len == 1); - branch_stack.items[0].deinit(bin_file.allocator); + branch_stack.items[0].deinit(gpa); branch_stack.deinit(); } try branch_stack.append(.{}); var function = Self{ - .gpa = bin_file.allocator, + .gpa = gpa, .air = air, .liveness = liveness, .debug_output = debug_output, - .target = &bin_file.options.target, - .bin_file = bin_file, + .target = target, + .bin_file = lf, .func_index = func_index, .owner_decl = func.owner_decl, .err_msg = null, @@ -375,15 +374,15 @@ pub fn generate( .end_di_line = func.rbrace_line, .end_di_column = func.rbrace_column, }; - defer function.stack.deinit(bin_file.allocator); - defer function.blocks.deinit(bin_file.allocator); - defer function.exitlude_jump_relocs.deinit(bin_file.allocator); - defer function.dbg_info_relocs.deinit(bin_file.allocator); + defer function.stack.deinit(gpa); + defer function.blocks.deinit(gpa); + defer function.exitlude_jump_relocs.deinit(gpa); + defer function.dbg_info_relocs.deinit(gpa); var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { error.CodegenFail => return Result{ .fail = function.err_msg.? }, error.OutOfRegisters => return Result{ - .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), }, else => |e| return e, }; @@ -397,7 +396,7 @@ pub fn generate( function.gen() catch |err| switch (err) { error.CodegenFail => return Result{ .fail = function.err_msg.? }, error.OutOfRegisters => return Result{ - .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), }, else => |e| return e, }; @@ -408,15 +407,15 @@ pub fn generate( var mir = Mir{ .instructions = function.mir_instructions.toOwnedSlice(), - .extra = try function.mir_extra.toOwnedSlice(bin_file.allocator), + .extra = try function.mir_extra.toOwnedSlice(gpa), }; - defer mir.deinit(bin_file.allocator); + defer mir.deinit(gpa); var emit = Emit{ .mir = mir, - .bin_file = bin_file, + .bin_file = lf, .debug_output = debug_output, - .target = &bin_file.options.target, + .target = target, .src_loc = src_loc, .code = code, .prev_di_pc = 0, @@ -476,7 +475,7 @@ pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { } fn gen(self: *Self) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const cc = self.fn_type.fnCallingConvention(mod); if (cc != .Naked) { // stp fp, lr, [sp, #-16]! @@ -656,7 +655,7 @@ fn gen(self: *Self) !void { } fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ip = &mod.intern_pool; const air_tags = self.air.instructions.items(.tag); @@ -1028,7 +1027,7 @@ fn allocMem( /// Use a pointer instruction as the basis for allocating stack memory. fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const elem_ty = self.typeOfIndex(inst).childType(mod); if (!elem_ty.hasRuntimeBits(mod)) { @@ -1048,7 +1047,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { } fn allocRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool, maybe_inst: ?Air.Inst.Index) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse { return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; @@ -1139,7 +1138,7 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { } fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result: MCValue = switch (self.ret_mcv) { .none, .register => .{ .ptr_stack_offset = try self.allocMemPtr(inst) }, .stack_offset => blk: { @@ -1176,7 +1175,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const operand = ty_op.operand; const operand_mcv = try self.resolveInst(operand); const operand_ty = self.typeOf(operand); @@ -1257,7 +1256,7 @@ fn trunc( operand_ty: Type, dest_ty: Type, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const info_a = operand_ty.intInfo(mod); const info_b = dest_ty.intInfo(mod); @@ -1320,7 +1319,7 @@ fn airIntFromBool(self: *Self, inst: Air.Inst.Index) !void { fn airNot(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand = try self.resolveInst(ty_op.operand); const operand_ty = self.typeOf(ty_op.operand); @@ -1415,7 +1414,7 @@ fn minMax( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO ARM min/max on floats", .{}), .Vector => return self.fail("TODO ARM min/max on vectors", .{}), @@ -1905,7 +1904,7 @@ fn addSub( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO binary operations on floats", .{}), .Vector => return self.fail("TODO binary operations on vectors", .{}), @@ -1966,7 +1965,7 @@ fn mul( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Vector => return self.fail("TODO binary operations on vectors", .{}), .Int => { @@ -1998,7 +1997,7 @@ fn divFloat( _ = rhs_ty; _ = maybe_inst; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO div_float", .{}), .Vector => return self.fail("TODO div_float on vectors", .{}), @@ -2014,7 +2013,7 @@ fn divTrunc( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO div on floats", .{}), .Vector => return self.fail("TODO div on vectors", .{}), @@ -2048,7 +2047,7 @@ fn divFloor( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO div on floats", .{}), .Vector => return self.fail("TODO div on vectors", .{}), @@ -2081,7 +2080,7 @@ fn divExact( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO div on floats", .{}), .Vector => return self.fail("TODO div on vectors", .{}), @@ -2117,7 +2116,7 @@ fn rem( ) InnerError!MCValue { _ = maybe_inst; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO rem/mod on floats", .{}), .Vector => return self.fail("TODO rem/mod on vectors", .{}), @@ -2188,7 +2187,7 @@ fn modulo( _ = rhs_ty; _ = maybe_inst; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO mod on floats", .{}), .Vector => return self.fail("TODO mod on vectors", .{}), @@ -2206,7 +2205,7 @@ fn wrappingArithmetic( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Vector => return self.fail("TODO binary operations on vectors", .{}), .Int => { @@ -2241,7 +2240,7 @@ fn bitwise( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Vector => return self.fail("TODO binary operations on vectors", .{}), .Int => { @@ -2276,7 +2275,7 @@ fn shiftExact( ) InnerError!MCValue { _ = rhs_ty; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Vector => return self.fail("TODO binary operations on vectors", .{}), .Int => { @@ -2326,7 +2325,7 @@ fn shiftNormal( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Vector => return self.fail("TODO binary operations on vectors", .{}), .Int => { @@ -2366,7 +2365,7 @@ fn booleanOp( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Bool => { assert((try lhs_bind.resolveToImmediate(self)) == null); // should have been handled by Sema @@ -2393,7 +2392,7 @@ fn ptrArithmetic( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Pointer => { assert(rhs_ty.eql(Type.usize, mod)); @@ -2516,7 +2515,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; @@ -2644,7 +2643,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result: MCValue = result: { const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; @@ -2868,7 +2867,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result: MCValue = result: { const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; @@ -3016,7 +3015,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { } fn optionalPayload(self: *Self, inst: Air.Inst.Index, mcv: MCValue, optional_ty: Type) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const payload_ty = optional_ty.optionalChild(mod); if (!payload_ty.hasRuntimeBits(mod)) return MCValue.none; if (optional_ty.isPtrLikeOptional(mod)) { @@ -3060,7 +3059,7 @@ fn errUnionErr( error_union_ty: Type, maybe_inst: ?Air.Inst.Index, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const err_ty = error_union_ty.errorUnionSet(mod); const payload_ty = error_union_ty.errorUnionPayload(mod); if (err_ty.errorSetIsEmpty(mod)) { @@ -3140,7 +3139,7 @@ fn errUnionPayload( error_union_ty: Type, maybe_inst: ?Air.Inst.Index, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const err_ty = error_union_ty.errorUnionSet(mod); const payload_ty = error_union_ty.errorUnionPayload(mod); if (err_ty.errorSetIsEmpty(mod)) { @@ -3252,7 +3251,7 @@ fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void { } fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; if (self.liveness.isUnused(inst)) { @@ -3297,7 +3296,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { /// T to E!T fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const error_union_ty = ty_op.ty.toType(); @@ -3323,7 +3322,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const error_union_ty = ty_op.ty.toType(); const error_ty = error_union_ty.errorUnionSet(mod); const payload_ty = error_union_ty.errorUnionPayload(mod); @@ -3426,7 +3425,7 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { } fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const slice_ty = self.typeOf(bin_op.lhs); const result: MCValue = if (!slice_ty.isVolatilePtr(mod) and self.liveness.isUnused(inst)) .dead else result: { @@ -3450,7 +3449,7 @@ fn ptrElemVal( ptr_ty: Type, maybe_inst: ?Air.Inst.Index, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const elem_ty = ptr_ty.childType(mod); const elem_size = @as(u32, @intCast(elem_ty.abiSize(mod))); @@ -3492,7 +3491,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { } fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ptr_ty = self.typeOf(bin_op.lhs); const result: MCValue = if (!ptr_ty.isVolatilePtr(mod) and self.liveness.isUnused(inst)) .dead else result: { @@ -3615,7 +3614,7 @@ fn reuseOperand( } fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const elem_ty = ptr_ty.childType(mod); const elem_size = elem_ty.abiSize(mod); @@ -3863,7 +3862,7 @@ fn genInlineMemsetCode( } fn airLoad(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const elem_ty = self.typeOfIndex(inst); const elem_size = elem_ty.abiSize(mod); @@ -3894,7 +3893,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { } fn genLdrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size = ty.abiSize(mod); const tag: Mir.Inst.Tag = switch (abi_size) { @@ -3917,7 +3916,7 @@ fn genLdrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type } fn genStrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size = ty.abiSize(mod); const tag: Mir.Inst.Tag = switch (abi_size) { @@ -3939,7 +3938,7 @@ fn genStrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type } fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; log.debug("store: storing {} to {}", .{ value, ptr }); const abi_size = value_ty.abiSize(mod); @@ -4092,7 +4091,7 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue { return if (self.liveness.isUnused(inst)) .dead else result: { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const mcv = try self.resolveInst(operand); const ptr_ty = self.typeOf(operand); const struct_ty = ptr_ty.childType(mod); @@ -4117,7 +4116,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const operand = extra.struct_operand; const index = extra.field_index; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const mcv = try self.resolveInst(operand); const struct_ty = self.typeOf(operand); const struct_field_ty = struct_ty.structFieldType(index, mod); @@ -4167,7 +4166,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { } fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { @@ -4195,7 +4194,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { while (self.args[arg_index] == .none) arg_index += 1; self.arg_index = arg_index + 1; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty = self.typeOfIndex(inst); const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; const src_index = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.src_index; @@ -4250,7 +4249,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const extra = self.air.extraData(Air.Call, pl_op.payload); const args = @as([]const Air.Inst.Ref, @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len])); const ty = self.typeOf(callee); - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const fn_ty = switch (ty.zigTypeTag(mod)) { .Fn => ty, @@ -4422,7 +4421,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier } fn airRet(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand = try self.resolveInst(un_op); const ret_ty = self.fn_type.fnReturnType(mod); @@ -4454,7 +4453,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { } fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const ptr = try self.resolveInst(un_op); const ptr_ty = self.typeOf(un_op); @@ -4514,7 +4513,7 @@ fn cmp( lhs_ty: Type, op: math.CompareOperator, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const int_ty = switch (lhs_ty.zigTypeTag(mod)) { .Optional => blk: { const payload_ty = lhs_ty.optionalChild(mod); @@ -4622,7 +4621,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const func = mod.funcInfo(ty_fn.func); // TODO emit debug info for function change _ = func; @@ -4830,7 +4829,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { } fn isNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const sentinel: struct { ty: Type, bind: ReadArg.Bind } = if (!operand_ty.isPtrLikeOptional(mod)) blk: { const payload_ty = operand_ty.optionalChild(mod); if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) @@ -4886,7 +4885,7 @@ fn isErr( error_union_bind: ReadArg.Bind, error_union_ty: Type, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const error_type = error_union_ty.errorUnionSet(mod); if (error_type.errorSetIsEmpty(mod)) { @@ -4928,7 +4927,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { } fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand_ptr = try self.resolveInst(un_op); @@ -4955,7 +4954,7 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { } fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand_ptr = try self.resolveInst(un_op); @@ -4982,7 +4981,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { } fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand_ptr = try self.resolveInst(un_op); @@ -5009,7 +5008,7 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { } fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand_ptr = try self.resolveInst(un_op); @@ -5226,7 +5225,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { } fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const block_data = self.blocks.getPtr(block).?; if (self.typeOf(operand).hasRuntimeBits(mod)) { @@ -5403,7 +5402,7 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { } fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size = @as(u32, @intCast(ty.abiSize(mod))); switch (mcv) { .dead => unreachable, @@ -5573,7 +5572,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro } fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (mcv) { .dead => unreachable, .unreach, .none => return, // Nothing to do. @@ -5735,7 +5734,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size = @as(u32, @intCast(ty.abiSize(mod))); switch (mcv) { .dead => unreachable, @@ -5934,7 +5933,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { } fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const ptr_ty = self.typeOf(ty_op.operand); @@ -6054,7 +6053,7 @@ fn airReduce(self: *Self, inst: Air.Inst.Index) !void { } fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const vector_ty = self.typeOfIndex(inst); const len = vector_ty.vectorLen(mod); const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; @@ -6098,7 +6097,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { } fn airTry(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const extra = self.air.extraData(Air.Try, pl_op.payload); const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]); @@ -6135,7 +6134,7 @@ fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void { } fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; // If the type has no codegen bits, no need to store it. const inst_ty = self.typeOf(inst); @@ -6200,7 +6199,7 @@ const CallMCValues = struct { /// Caller must call `CallMCValues.deinit`. fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ip = &mod.intern_pool; const fn_info = mod.typeToFunc(fn_ty).?; const cc = fn_info.cc; @@ -6333,7 +6332,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`. fn wantSafety(self: *Self) bool { - return switch (self.bin_file.options.optimize_mode) { + return switch (self.bin_file.comp.root_mod.optimize_mode) { .Debug => true, .ReleaseSafe => true, .ReleaseFast => false, @@ -6344,14 +6343,14 @@ fn wantSafety(self: *Self) bool { fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(self.err_msg == null); - self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); + self.err_msg = try ErrorMsg.create(self.gpa, self.src_loc, format, args); return error.CodegenFail; } fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(self.err_msg == null); - self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); + self.err_msg = try ErrorMsg.create(self.gpa, self.src_loc, format, args); return error.CodegenFail; } @@ -6363,7 +6362,7 @@ fn parseRegName(name: []const u8) ?Register { } fn registerAlias(self: *Self, reg: Register, ty: Type) Register { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size = ty.abiSize(mod); switch (reg.class()) { @@ -6392,11 +6391,11 @@ fn registerAlias(self: *Self, reg: Register, ty: Type) Register { } fn typeOf(self: *Self, inst: Air.Inst.Ref) Type { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; return self.air.typeOf(inst, &mod.intern_pool); } fn typeOfIndex(self: *Self, inst: Air.Inst.Index) Type { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; return self.air.typeOfIndex(inst, &mod.intern_pool); } diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index 9ba722f39368..3f629fd46f3e 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -218,14 +218,16 @@ pub fn emitMir( } pub fn deinit(emit: *Emit) void { + const comp = emit.bin_file.comp; + const gpa = comp.gpa; var iter = emit.branch_forward_origins.valueIterator(); while (iter.next()) |origin_list| { - origin_list.deinit(emit.bin_file.allocator); + origin_list.deinit(gpa); } - emit.branch_types.deinit(emit.bin_file.allocator); - emit.branch_forward_origins.deinit(emit.bin_file.allocator); - emit.code_offset_mapping.deinit(emit.bin_file.allocator); + emit.branch_types.deinit(gpa); + emit.branch_forward_origins.deinit(gpa); + emit.code_offset_mapping.deinit(gpa); emit.* = undefined; } @@ -314,8 +316,9 @@ fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index { } fn lowerBranches(emit: *Emit) !void { + const comp = emit.bin_file.comp; + const gpa = comp.gpa; const mir_tags = emit.mir.instructions.items(.tag); - const allocator = emit.bin_file.allocator; // First pass: Note down all branches and their target // instructions, i.e. populate branch_types, @@ -329,7 +332,7 @@ fn lowerBranches(emit: *Emit) !void { const target_inst = emit.branchTarget(inst); // Remember this branch instruction - try emit.branch_types.put(allocator, inst, BranchType.default(tag)); + try emit.branch_types.put(gpa, inst, BranchType.default(tag)); // Forward branches require some extra stuff: We only // know their offset once we arrive at the target @@ -339,14 +342,14 @@ fn lowerBranches(emit: *Emit) !void { // etc. if (target_inst > inst) { // Remember the branch instruction index - try emit.code_offset_mapping.put(allocator, inst, 0); + try emit.code_offset_mapping.put(gpa, inst, 0); if (emit.branch_forward_origins.getPtr(target_inst)) |origin_list| { - try origin_list.append(allocator, inst); + try origin_list.append(gpa, inst); } else { var origin_list: std.ArrayListUnmanaged(Mir.Inst.Index) = .{}; - try origin_list.append(allocator, inst); - try emit.branch_forward_origins.put(allocator, target_inst, origin_list); + try origin_list.append(gpa, inst); + try emit.branch_forward_origins.put(gpa, target_inst, origin_list); } } @@ -356,7 +359,7 @@ fn lowerBranches(emit: *Emit) !void { // putNoClobber may not be used as the put operation // may clobber the entry when multiple branches branch // to the same target instruction - try emit.code_offset_mapping.put(allocator, target_inst, 0); + try emit.code_offset_mapping.put(gpa, target_inst, 0); } } @@ -429,7 +432,9 @@ fn writeInstruction(emit: *Emit, instruction: Instruction) !void { fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(emit.err_msg == null); - emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args); + const comp = emit.bin_file.comp; + const gpa = comp.gpa; + emit.err_msg = try ErrorMsg.create(gpa, emit.src_loc, format, args); return error.EmitFail; } diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index ef3ee3871b98..7227347e1b9e 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -260,7 +260,7 @@ const DbgInfoReloc = struct { } fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) error{OutOfMemory}!void { - const mod = function.bin_file.options.module.?; + const mod = function.bin_file.comp.module.?; switch (function.debug_output) { .dwarf => |dw| { const loc: link.File.Dwarf.DeclState.DbgInfoLoc = switch (reloc.mcv) { @@ -289,7 +289,7 @@ const DbgInfoReloc = struct { } fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) !void { - const mod = function.bin_file.options.module.?; + const mod = function.bin_file.comp.module.?; const is_ptr = switch (reloc.tag) { .dbg_var_ptr => true, .dbg_var_val => false, @@ -336,7 +336,7 @@ const DbgInfoReloc = struct { const Self = @This(); pub fn generate( - bin_file: *link.File, + lf: *link.File, src_loc: Module.SrcLoc, func_index: InternPool.Index, air: Air, @@ -344,30 +344,29 @@ pub fn generate( code: *std.ArrayList(u8), debug_output: DebugInfoOutput, ) CodeGenError!Result { - if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { - @panic("Attempted to compile for architecture that was disabled by build configuration"); - } - - const mod = bin_file.options.module.?; - const func = mod.funcInfo(func_index); - const fn_owner_decl = mod.declPtr(func.owner_decl); + const gpa = lf.comp.gpa; + const zcu = lf.comp.module.?; + const func = zcu.funcInfo(func_index); + const fn_owner_decl = zcu.declPtr(func.owner_decl); assert(fn_owner_decl.has_tv); const fn_type = fn_owner_decl.ty; + const namespace = zcu.namespacePtr(fn_owner_decl.src_namespace); + const target = &namespace.file_scope.mod.resolved_target.result; - var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); + var branch_stack = std.ArrayList(Branch).init(gpa); defer { assert(branch_stack.items.len == 1); - branch_stack.items[0].deinit(bin_file.allocator); + branch_stack.items[0].deinit(gpa); branch_stack.deinit(); } try branch_stack.append(.{}); - var function = Self{ - .gpa = bin_file.allocator, + var function: Self = .{ + .gpa = gpa, .air = air, .liveness = liveness, - .target = &bin_file.options.target, - .bin_file = bin_file, + .target = target, + .bin_file = lf, .debug_output = debug_output, .func_index = func_index, .err_msg = null, @@ -381,15 +380,15 @@ pub fn generate( .end_di_line = func.rbrace_line, .end_di_column = func.rbrace_column, }; - defer function.stack.deinit(bin_file.allocator); - defer function.blocks.deinit(bin_file.allocator); - defer function.exitlude_jump_relocs.deinit(bin_file.allocator); - defer function.dbg_info_relocs.deinit(bin_file.allocator); + defer function.stack.deinit(gpa); + defer function.blocks.deinit(gpa); + defer function.exitlude_jump_relocs.deinit(gpa); + defer function.dbg_info_relocs.deinit(gpa); var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { error.CodegenFail => return Result{ .fail = function.err_msg.? }, error.OutOfRegisters => return Result{ - .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), }, else => |e| return e, }; @@ -403,7 +402,7 @@ pub fn generate( function.gen() catch |err| switch (err) { error.CodegenFail => return Result{ .fail = function.err_msg.? }, error.OutOfRegisters => return Result{ - .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), }, else => |e| return e, }; @@ -414,15 +413,15 @@ pub fn generate( var mir = Mir{ .instructions = function.mir_instructions.toOwnedSlice(), - .extra = try function.mir_extra.toOwnedSlice(bin_file.allocator), + .extra = try function.mir_extra.toOwnedSlice(gpa), }; - defer mir.deinit(bin_file.allocator); + defer mir.deinit(gpa); var emit = Emit{ .mir = mir, - .bin_file = bin_file, + .bin_file = lf, .debug_output = debug_output, - .target = &bin_file.options.target, + .target = target, .src_loc = src_loc, .code = code, .prev_di_pc = 0, @@ -482,7 +481,7 @@ pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { } fn gen(self: *Self) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const cc = self.fn_type.fnCallingConvention(mod); if (cc != .Naked) { // push {fp, lr} @@ -642,7 +641,7 @@ fn gen(self: *Self) !void { } fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ip = &mod.intern_pool; const air_tags = self.air.instructions.items(.tag); @@ -1010,7 +1009,7 @@ fn allocMem( /// Use a pointer instruction as the basis for allocating stack memory. fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const elem_ty = self.typeOfIndex(inst).childType(mod); if (!elem_ty.hasRuntimeBits(mod)) { @@ -1031,7 +1030,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { } fn allocRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool, maybe_inst: ?Air.Inst.Index) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse { return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); }; @@ -1118,7 +1117,7 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { } fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result: MCValue = switch (self.ret_mcv) { .none, .register => .{ .ptr_stack_offset = try self.allocMemPtr(inst) }, .stack_offset => blk: { @@ -1151,7 +1150,7 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void { } fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); @@ -1217,7 +1216,7 @@ fn trunc( operand_ty: Type, dest_ty: Type, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const info_a = operand_ty.intInfo(mod); const info_b = dest_ty.intInfo(mod); @@ -1281,7 +1280,7 @@ fn airIntFromBool(self: *Self, inst: Air.Inst.Index) !void { fn airNot(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; const operand_ty = self.typeOf(ty_op.operand); @@ -1377,7 +1376,7 @@ fn minMax( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO ARM min/max on floats", .{}), .Vector => return self.fail("TODO ARM min/max on vectors", .{}), @@ -1586,7 +1585,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; @@ -1699,7 +1698,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result: MCValue = result: { const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; @@ -1863,7 +1862,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result: MCValue = result: { const lhs_ty = self.typeOf(extra.lhs); const rhs_ty = self.typeOf(extra.rhs); @@ -2019,7 +2018,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { } fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const optional_ty = self.typeOfIndex(inst); @@ -2042,7 +2041,7 @@ fn errUnionErr( error_union_ty: Type, maybe_inst: ?Air.Inst.Index, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const err_ty = error_union_ty.errorUnionSet(mod); const payload_ty = error_union_ty.errorUnionPayload(mod); if (err_ty.errorSetIsEmpty(mod)) { @@ -2119,7 +2118,7 @@ fn errUnionPayload( error_union_ty: Type, maybe_inst: ?Air.Inst.Index, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const err_ty = error_union_ty.errorUnionSet(mod); const payload_ty = error_union_ty.errorUnionPayload(mod); if (err_ty.errorSetIsEmpty(mod)) { @@ -2229,7 +2228,7 @@ fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void { /// T to E!T fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const error_union_ty = ty_op.ty.toType(); @@ -2253,7 +2252,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { /// E to E!T fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const error_union_ty = ty_op.ty.toType(); @@ -2370,7 +2369,7 @@ fn ptrElemVal( ptr_ty: Type, maybe_inst: ?Air.Inst.Index, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const elem_ty = ptr_ty.childType(mod); const elem_size: u32 = @intCast(elem_ty.abiSize(mod)); @@ -2429,7 +2428,7 @@ fn ptrElemVal( } fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const slice_ty = self.typeOf(bin_op.lhs); const result: MCValue = if (!slice_ty.isVolatilePtr(mod) and self.liveness.isUnused(inst)) .dead else result: { @@ -2472,7 +2471,7 @@ fn arrayElemVal( array_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const elem_ty = array_ty.childType(mod); const mcv = try array_bind.resolveToMcv(self); @@ -2528,7 +2527,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { } fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ptr_ty = self.typeOf(bin_op.lhs); const result: MCValue = if (!ptr_ty.isVolatilePtr(mod) and self.liveness.isUnused(inst)) .dead else result: { @@ -2662,7 +2661,7 @@ fn reuseOperand( } fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const elem_ty = ptr_ty.childType(mod); const elem_size: u32 = @intCast(elem_ty.abiSize(mod)); @@ -2739,7 +2738,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo } fn airLoad(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const elem_ty = self.typeOfIndex(inst); const result: MCValue = result: { @@ -2768,7 +2767,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { } fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const elem_size: u32 = @intCast(value_ty.abiSize(mod)); switch (ptr) { @@ -2888,7 +2887,7 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue { return if (self.liveness.isUnused(inst)) .dead else result: { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const mcv = try self.resolveInst(operand); const ptr_ty = self.typeOf(operand); const struct_ty = ptr_ty.childType(mod); @@ -2912,7 +2911,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; const operand = extra.struct_operand; const index = extra.field_index; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const mcv = try self.resolveInst(operand); const struct_ty = self.typeOf(operand); @@ -3002,7 +3001,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { } fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { @@ -3396,7 +3395,7 @@ fn addSub( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO ARM binary operations on floats", .{}), .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), @@ -3452,7 +3451,7 @@ fn mul( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO ARM binary operations on floats", .{}), .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), @@ -3485,7 +3484,7 @@ fn divFloat( _ = rhs_ty; _ = maybe_inst; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO ARM binary operations on floats", .{}), .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), @@ -3501,7 +3500,7 @@ fn divTrunc( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO ARM binary operations on floats", .{}), .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), @@ -3544,7 +3543,7 @@ fn divFloor( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO ARM binary operations on floats", .{}), .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), @@ -3592,7 +3591,7 @@ fn divExact( _ = rhs_ty; _ = maybe_inst; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO ARM binary operations on floats", .{}), .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), @@ -3609,7 +3608,7 @@ fn rem( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO ARM binary operations on floats", .{}), .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), @@ -3678,7 +3677,7 @@ fn modulo( _ = rhs_ty; _ = maybe_inst; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO ARM binary operations on floats", .{}), .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), @@ -3696,7 +3695,7 @@ fn wrappingArithmetic( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), .Int => { @@ -3734,7 +3733,7 @@ fn bitwise( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), .Int => { @@ -3779,7 +3778,7 @@ fn shiftExact( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), .Int => { @@ -3818,7 +3817,7 @@ fn shiftNormal( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Vector => return self.fail("TODO ARM binary operations on vectors", .{}), .Int => { @@ -3861,7 +3860,7 @@ fn booleanOp( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Bool => { const lhs_immediate = try lhs_bind.resolveToImmediate(self); @@ -3895,7 +3894,7 @@ fn ptrArithmetic( rhs_ty: Type, maybe_inst: ?Air.Inst.Index, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lhs_ty.zigTypeTag(mod)) { .Pointer => { assert(rhs_ty.eql(Type.usize, mod)); @@ -3932,7 +3931,7 @@ fn ptrArithmetic( } fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, ty: Type) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size = ty.abiSize(mod); const tag: Mir.Inst.Tag = switch (abi_size) { @@ -3967,7 +3966,7 @@ fn genLdrRegister(self: *Self, dest_reg: Register, addr_reg: Register, ty: Type) } fn genStrRegister(self: *Self, source_reg: Register, addr_reg: Register, ty: Type) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size = ty.abiSize(mod); const tag: Mir.Inst.Tag = switch (abi_size) { @@ -4174,7 +4173,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { while (self.args[arg_index] == .none) arg_index += 1; self.arg_index = arg_index + 1; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty = self.typeOfIndex(inst); const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; const src_index = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.src_index; @@ -4229,7 +4228,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const extra = self.air.extraData(Air.Call, pl_op.payload); const args: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]); const ty = self.typeOf(callee); - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const fn_ty = switch (ty.zigTypeTag(mod)) { .Fn => ty, @@ -4380,7 +4379,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier } fn airRet(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand = try self.resolveInst(un_op); const ret_ty = self.fn_type.fnReturnType(mod); @@ -4412,7 +4411,7 @@ fn airRet(self: *Self, inst: Air.Inst.Index) !void { } fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const ptr = try self.resolveInst(un_op); const ptr_ty = self.typeOf(un_op); @@ -4473,7 +4472,7 @@ fn cmp( lhs_ty: Type, op: math.CompareOperator, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const int_ty = switch (lhs_ty.zigTypeTag(mod)) { .Optional => blk: { const payload_ty = lhs_ty.optionalChild(mod); @@ -4580,7 +4579,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const func = mod.funcInfo(ty_fn.func); // TODO emit debug info for function change _ = func; @@ -4795,7 +4794,7 @@ fn isNull( operand_bind: ReadArg.Bind, operand_ty: Type, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; if (operand_ty.isPtrLikeOptional(mod)) { assert(operand_ty.abiSize(mod) == 4); @@ -4829,7 +4828,7 @@ fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { } fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand_ptr = try self.resolveInst(un_op); @@ -4856,7 +4855,7 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { } fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand_ptr = try self.resolveInst(un_op); @@ -4876,7 +4875,7 @@ fn isErr( error_union_bind: ReadArg.Bind, error_union_ty: Type, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const error_type = error_union_ty.errorUnionSet(mod); if (error_type.errorSetIsEmpty(mod)) { @@ -4918,7 +4917,7 @@ fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { } fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand_ptr = try self.resolveInst(un_op); @@ -4945,7 +4944,7 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { } fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand_ptr = try self.resolveInst(un_op); @@ -5160,7 +5159,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { } fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const block_data = self.blocks.getPtr(block).?; if (self.typeOf(operand).hasRuntimeBits(mod)) { @@ -5331,7 +5330,7 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { } fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size: u32 = @intCast(ty.abiSize(mod)); switch (mcv) { .dead => unreachable, @@ -5493,7 +5492,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro } fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (mcv) { .dead => unreachable, .unreach, .none => return, // Nothing to do. @@ -5740,7 +5739,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size: u32 = @intCast(ty.abiSize(mod)); switch (mcv) { .dead => unreachable, @@ -5896,7 +5895,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { } fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const ptr_ty = self.typeOf(ty_op.operand); @@ -6015,7 +6014,7 @@ fn airReduce(self: *Self, inst: Air.Inst.Index) !void { } fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const vector_ty = self.typeOfIndex(inst); const len = vector_ty.vectorLen(mod); const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; @@ -6066,7 +6065,7 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = result: { const error_union_bind: ReadArg.Bind = .{ .inst = pl_op.operand }; const error_union_ty = self.typeOf(pl_op.operand); - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const error_union_size: u32 = @intCast(error_union_ty.abiSize(mod)); const error_union_align = error_union_ty.abiAlignment(mod); @@ -6097,7 +6096,7 @@ fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void { } fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; // If the type has no codegen bits, no need to store it. const inst_ty = self.typeOf(inst); @@ -6125,7 +6124,7 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { } fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const mcv: MCValue = switch (try codegen.genTypedValue( self.bin_file, self.src_loc, @@ -6161,7 +6160,7 @@ const CallMCValues = struct { /// Caller must call `CallMCValues.deinit`. fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ip = &mod.intern_pool; const fn_info = mod.typeToFunc(fn_ty).?; const cc = fn_info.cc; @@ -6282,7 +6281,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`. fn wantSafety(self: *Self) bool { - return switch (self.bin_file.options.optimize_mode) { + return switch (self.bin_file.comp.root_mod.optimize_mode) { .Debug => true, .ReleaseSafe => true, .ReleaseFast => false, @@ -6293,14 +6292,16 @@ fn wantSafety(self: *Self) bool { fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(self.err_msg == null); - self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); + const gpa = self.gpa; + self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args); return error.CodegenFail; } fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(self.err_msg == null); - self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); + const gpa = self.gpa; + self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args); return error.CodegenFail; } @@ -6312,11 +6313,11 @@ fn parseRegName(name: []const u8) ?Register { } fn typeOf(self: *Self, inst: Air.Inst.Ref) Type { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; return self.air.typeOf(inst, &mod.intern_pool); } fn typeOfIndex(self: *Self, inst: Air.Inst.Index) Type { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; return self.air.typeOfIndex(inst, &mod.intern_pool); } diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 45c3392918d5..2ab3b60ee6f6 100644 --- a/src/arch/arm/Emit.zig +++ b/src/arch/arm/Emit.zig @@ -152,14 +152,17 @@ pub fn emitMir( } pub fn deinit(emit: *Emit) void { + const comp = emit.bin_file.comp; + const gpa = comp.gpa; + var iter = emit.branch_forward_origins.valueIterator(); while (iter.next()) |origin_list| { - origin_list.deinit(emit.bin_file.allocator); + origin_list.deinit(gpa); } - emit.branch_types.deinit(emit.bin_file.allocator); - emit.branch_forward_origins.deinit(emit.bin_file.allocator); - emit.code_offset_mapping.deinit(emit.bin_file.allocator); + emit.branch_types.deinit(gpa); + emit.branch_forward_origins.deinit(gpa); + emit.code_offset_mapping.deinit(gpa); emit.* = undefined; } @@ -231,8 +234,9 @@ fn branchTarget(emit: *Emit, inst: Mir.Inst.Index) Mir.Inst.Index { } fn lowerBranches(emit: *Emit) !void { + const comp = emit.bin_file.comp; + const gpa = comp.gpa; const mir_tags = emit.mir.instructions.items(.tag); - const allocator = emit.bin_file.allocator; // First pass: Note down all branches and their target // instructions, i.e. populate branch_types, @@ -246,7 +250,7 @@ fn lowerBranches(emit: *Emit) !void { const target_inst = emit.branchTarget(inst); // Remember this branch instruction - try emit.branch_types.put(allocator, inst, BranchType.default(tag)); + try emit.branch_types.put(gpa, inst, BranchType.default(tag)); // Forward branches require some extra stuff: We only // know their offset once we arrive at the target @@ -256,14 +260,14 @@ fn lowerBranches(emit: *Emit) !void { // etc. if (target_inst > inst) { // Remember the branch instruction index - try emit.code_offset_mapping.put(allocator, inst, 0); + try emit.code_offset_mapping.put(gpa, inst, 0); if (emit.branch_forward_origins.getPtr(target_inst)) |origin_list| { - try origin_list.append(allocator, inst); + try origin_list.append(gpa, inst); } else { var origin_list: std.ArrayListUnmanaged(Mir.Inst.Index) = .{}; - try origin_list.append(allocator, inst); - try emit.branch_forward_origins.put(allocator, target_inst, origin_list); + try origin_list.append(gpa, inst); + try emit.branch_forward_origins.put(gpa, target_inst, origin_list); } } @@ -273,7 +277,7 @@ fn lowerBranches(emit: *Emit) !void { // putNoClobber may not be used as the put operation // may clobber the entry when multiple branches branch // to the same target instruction - try emit.code_offset_mapping.put(allocator, target_inst, 0); + try emit.code_offset_mapping.put(gpa, target_inst, 0); } } @@ -346,7 +350,9 @@ fn writeInstruction(emit: *Emit, instruction: Instruction) !void { fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(emit.err_msg == null); - emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args); + const comp = emit.bin_file.comp; + const gpa = comp.gpa; + emit.err_msg = try ErrorMsg.create(gpa, emit.src_loc, format, args); return error.EmitFail; } diff --git a/src/arch/riscv64/CodeGen.zig b/src/arch/riscv64/CodeGen.zig index c217288e63eb..7d3b0cd1a0ac 100644 --- a/src/arch/riscv64/CodeGen.zig +++ b/src/arch/riscv64/CodeGen.zig @@ -217,7 +217,7 @@ const BigTomb = struct { const Self = @This(); pub fn generate( - bin_file: *link.File, + lf: *link.File, src_loc: Module.SrcLoc, func_index: InternPool.Index, air: Air, @@ -225,30 +225,29 @@ pub fn generate( code: *std.ArrayList(u8), debug_output: DebugInfoOutput, ) CodeGenError!Result { - if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { - @panic("Attempted to compile for architecture that was disabled by build configuration"); - } - - const mod = bin_file.options.module.?; - const func = mod.funcInfo(func_index); - const fn_owner_decl = mod.declPtr(func.owner_decl); + const gpa = lf.comp.gpa; + const zcu = lf.comp.module.?; + const func = zcu.funcInfo(func_index); + const fn_owner_decl = zcu.declPtr(func.owner_decl); assert(fn_owner_decl.has_tv); const fn_type = fn_owner_decl.ty; + const namespace = zcu.namespacePtr(fn_owner_decl.src_namespace); + const target = &namespace.file_scope.mod.resolved_target.result; - var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); + var branch_stack = std.ArrayList(Branch).init(gpa); defer { assert(branch_stack.items.len == 1); - branch_stack.items[0].deinit(bin_file.allocator); + branch_stack.items[0].deinit(gpa); branch_stack.deinit(); } try branch_stack.append(.{}); var function = Self{ - .gpa = bin_file.allocator, + .gpa = gpa, .air = air, .liveness = liveness, - .target = &bin_file.options.target, - .bin_file = bin_file, + .target = target, + .bin_file = lf, .func_index = func_index, .code = code, .debug_output = debug_output, @@ -263,14 +262,14 @@ pub fn generate( .end_di_line = func.rbrace_line, .end_di_column = func.rbrace_column, }; - defer function.stack.deinit(bin_file.allocator); - defer function.blocks.deinit(bin_file.allocator); - defer function.exitlude_jump_relocs.deinit(bin_file.allocator); + defer function.stack.deinit(gpa); + defer function.blocks.deinit(gpa); + defer function.exitlude_jump_relocs.deinit(gpa); var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { error.CodegenFail => return Result{ .fail = function.err_msg.? }, error.OutOfRegisters => return Result{ - .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), }, else => |e| return e, }; @@ -284,22 +283,22 @@ pub fn generate( function.gen() catch |err| switch (err) { error.CodegenFail => return Result{ .fail = function.err_msg.? }, error.OutOfRegisters => return Result{ - .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), }, else => |e| return e, }; var mir = Mir{ .instructions = function.mir_instructions.toOwnedSlice(), - .extra = try function.mir_extra.toOwnedSlice(bin_file.allocator), + .extra = try function.mir_extra.toOwnedSlice(gpa), }; - defer mir.deinit(bin_file.allocator); + defer mir.deinit(gpa); var emit = Emit{ .mir = mir, - .bin_file = bin_file, + .bin_file = lf, .debug_output = debug_output, - .target = &bin_file.options.target, + .target = target, .src_loc = src_loc, .code = code, .prev_di_pc = 0, @@ -350,7 +349,7 @@ pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { } fn gen(self: *Self) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const cc = self.fn_type.fnCallingConvention(mod); if (cc != .Naked) { // TODO Finish function prologue and epilogue for riscv64. @@ -474,7 +473,7 @@ fn gen(self: *Self) !void { } fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ip = &mod.intern_pool; const air_tags = self.air.instructions.items(.tag); @@ -805,7 +804,7 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: Alignme /// Use a pointer instruction as the basis for allocating stack memory. fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const elem_ty = self.typeOfIndex(inst).childType(mod); const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse { return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); @@ -816,7 +815,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { } fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const elem_ty = self.typeOfIndex(inst); const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse { return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); @@ -893,7 +892,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const operand_ty = self.typeOf(ty_op.operand); const operand = try self.resolveInst(ty_op.operand); const info_a = operand_ty.intInfo(mod); @@ -1069,7 +1068,7 @@ fn binOp( lhs_ty: Type, rhs_ty: Type, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (tag) { // Arithmetic operations on integers and floats .add, @@ -1332,7 +1331,7 @@ fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void { fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const optional_ty = self.typeOfIndex(inst); // Optional with a zero-bit payload type is just a boolean true @@ -1506,7 +1505,7 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind } fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const elem_ty = ptr_ty.childType(mod); switch (ptr) { .none => unreachable, @@ -1532,7 +1531,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo } fn airLoad(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const elem_ty = self.typeOfIndex(inst); const result: MCValue = result: { @@ -1633,7 +1632,7 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { } fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg; const ty = arg.ty.toType(); const owner_decl = mod.funcOwnerDeclIndex(self.func_index); @@ -1710,7 +1709,7 @@ fn airFence(self: *Self) !void { } fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; if (modifier == .always_tail) return self.fail("TODO implement tail calls for riscv64", .{}); const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const fn_ty = self.typeOf(pl_op.operand); @@ -1812,7 +1811,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier } fn ret(self: *Self, mcv: MCValue) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ret_ty = self.fn_type.fnReturnType(mod); try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); // Just add space for an instruction, patch this later @@ -1843,7 +1842,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); const ty = self.typeOf(bin_op.lhs); - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; assert(ty.eql(self.typeOf(bin_op.rhs), mod)); if (ty.zigTypeTag(mod) == .ErrorSet) return self.fail("TODO implement cmp for errors", .{}); @@ -1887,7 +1886,7 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const func = mod.funcInfo(ty_fn.func); // TODO emit debug info for function change _ = func; @@ -2125,7 +2124,7 @@ fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void { fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { const block_data = self.blocks.getPtr(block).?; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; if (self.typeOf(operand).hasRuntimeBits(mod)) { const operand_mcv = try self.resolveInst(operand); const block_mcv = block_data.mcv; @@ -2508,7 +2507,7 @@ fn airReduce(self: *Self, inst: Air.Inst.Index) !void { } fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const vector_ty = self.typeOfIndex(inst); const len = vector_ty.vectorLen(mod); const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; @@ -2553,7 +2552,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { } fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; // If the type has no codegen bits, no need to store it. const inst_ty = self.typeOf(inst); @@ -2581,7 +2580,7 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { } fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const mcv: MCValue = switch (try codegen.genTypedValue( self.bin_file, self.src_loc, @@ -2617,7 +2616,7 @@ const CallMCValues = struct { /// Caller must call `CallMCValues.deinit`. fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ip = &mod.intern_pool; const fn_info = mod.typeToFunc(fn_ty).?; const cc = fn_info.cc; @@ -2709,7 +2708,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`. fn wantSafety(self: *Self) bool { - return switch (self.bin_file.options.optimize_mode) { + return switch (self.bin_file.comp.root_mod.optimize_mode) { .Debug => true, .ReleaseSafe => true, .ReleaseFast => false, @@ -2720,14 +2719,14 @@ fn wantSafety(self: *Self) bool { fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(self.err_msg == null); - self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); + self.err_msg = try ErrorMsg.create(self.gpa, self.src_loc, format, args); return error.CodegenFail; } fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(self.err_msg == null); - self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); + self.err_msg = try ErrorMsg.create(self.gpa, self.src_loc, format, args); return error.CodegenFail; } @@ -2739,11 +2738,11 @@ fn parseRegName(name: []const u8) ?Register { } fn typeOf(self: *Self, inst: Air.Inst.Ref) Type { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; return self.air.typeOf(inst, &mod.intern_pool); } fn typeOfIndex(self: *Self, inst: Air.Inst.Index) Type { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; return self.air.typeOfIndex(inst, &mod.intern_pool); } diff --git a/src/arch/riscv64/Emit.zig b/src/arch/riscv64/Emit.zig index 9d82cc38cc5d..f382f6f9ebf5 100644 --- a/src/arch/riscv64/Emit.zig +++ b/src/arch/riscv64/Emit.zig @@ -80,7 +80,9 @@ fn writeInstruction(emit: *Emit, instruction: Instruction) !void { fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(emit.err_msg == null); - emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args); + const comp = emit.bin_file.comp; + const gpa = comp.gpa; + emit.err_msg = try ErrorMsg.create(gpa, emit.src_loc, format, args); return error.EmitFail; } diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 79dd6ec8d8dc..e7db3be8b020 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -260,7 +260,7 @@ const BigTomb = struct { }; pub fn generate( - bin_file: *link.File, + lf: *link.File, src_loc: Module.SrcLoc, func_index: InternPool.Index, air: Air, @@ -268,31 +268,30 @@ pub fn generate( code: *std.ArrayList(u8), debug_output: DebugInfoOutput, ) CodeGenError!Result { - if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { - @panic("Attempted to compile for architecture that was disabled by build configuration"); - } - - const mod = bin_file.options.module.?; - const func = mod.funcInfo(func_index); - const fn_owner_decl = mod.declPtr(func.owner_decl); + const gpa = lf.comp.gpa; + const zcu = lf.comp.module.?; + const func = zcu.funcInfo(func_index); + const fn_owner_decl = zcu.declPtr(func.owner_decl); assert(fn_owner_decl.has_tv); const fn_type = fn_owner_decl.ty; + const namespace = zcu.namespacePtr(fn_owner_decl.src_namespace); + const target = &namespace.file_scope.mod.resolved_target.result; - var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); + var branch_stack = std.ArrayList(Branch).init(gpa); defer { assert(branch_stack.items.len == 1); - branch_stack.items[0].deinit(bin_file.allocator); + branch_stack.items[0].deinit(gpa); branch_stack.deinit(); } try branch_stack.append(.{}); var function = Self{ - .gpa = bin_file.allocator, + .gpa = gpa, .air = air, .liveness = liveness, - .target = &bin_file.options.target, + .target = target, .func_index = func_index, - .bin_file = bin_file, + .bin_file = lf, .code = code, .debug_output = debug_output, .err_msg = null, @@ -306,14 +305,14 @@ pub fn generate( .end_di_line = func.rbrace_line, .end_di_column = func.rbrace_column, }; - defer function.stack.deinit(bin_file.allocator); - defer function.blocks.deinit(bin_file.allocator); - defer function.exitlude_jump_relocs.deinit(bin_file.allocator); + defer function.stack.deinit(gpa); + defer function.blocks.deinit(gpa); + defer function.exitlude_jump_relocs.deinit(gpa); var call_info = function.resolveCallingConventionValues(fn_type, .callee) catch |err| switch (err) { error.CodegenFail => return Result{ .fail = function.err_msg.? }, error.OutOfRegisters => return Result{ - .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), }, else => |e| return e, }; @@ -327,22 +326,22 @@ pub fn generate( function.gen() catch |err| switch (err) { error.CodegenFail => return Result{ .fail = function.err_msg.? }, error.OutOfRegisters => return Result{ - .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), }, else => |e| return e, }; var mir = Mir{ .instructions = function.mir_instructions.toOwnedSlice(), - .extra = try function.mir_extra.toOwnedSlice(bin_file.allocator), + .extra = try function.mir_extra.toOwnedSlice(gpa), }; - defer mir.deinit(bin_file.allocator); + defer mir.deinit(gpa); var emit = Emit{ .mir = mir, - .bin_file = bin_file, + .bin_file = lf, .debug_output = debug_output, - .target = &bin_file.options.target, + .target = target, .src_loc = src_loc, .code = code, .prev_di_pc = 0, @@ -364,7 +363,7 @@ pub fn generate( } fn gen(self: *Self) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const cc = self.fn_type.fnCallingConvention(mod); if (cc != .Naked) { // TODO Finish function prologue and epilogue for sparc64. @@ -492,7 +491,7 @@ fn gen(self: *Self) !void { } fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ip = &mod.intern_pool; const air_tags = self.air.instructions.items(.tag); @@ -762,7 +761,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const lhs = try self.resolveInst(extra.lhs); const rhs = try self.resolveInst(extra.rhs); @@ -840,7 +839,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { } fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const vector_ty = self.typeOfIndex(inst); const len = vector_ty.vectorLen(mod); const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; @@ -874,7 +873,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { } fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const ptr_ty = self.typeOf(ty_op.operand); @@ -1011,7 +1010,7 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { } fn airArg(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const arg_index = self.arg_index; self.arg_index += 1; @@ -1206,7 +1205,7 @@ fn airBreakpoint(self: *Self) !void { } fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; // We have hardware byteswapper in SPARCv9, don't let mainstream compilers mislead you. @@ -1298,7 +1297,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier const extra = self.air.extraData(Air.Call, pl_op.payload); const args = @as([]const Air.Inst.Ref, @ptrCast(self.air.extra[extra.end .. extra.end + extra.data.args_len])); const ty = self.typeOf(callee); - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const fn_ty = switch (ty.zigTypeTag(mod)) { .Fn => ty, .Pointer => ty.childType(mod), @@ -1430,7 +1429,7 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void { fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const lhs = try self.resolveInst(bin_op.lhs); const rhs = try self.resolveInst(bin_op.rhs); @@ -1662,7 +1661,7 @@ fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const func = mod.funcInfo(ty_fn.func); // TODO emit debug info for function change _ = func; @@ -1758,7 +1757,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const operand_ty = self.typeOf(ty_op.operand); const operand = try self.resolveInst(ty_op.operand); const info_a = operand_ty.intInfo(mod); @@ -1819,7 +1818,7 @@ fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { } fn airLoad(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const elem_ty = self.typeOfIndex(inst); const elem_size = elem_ty.abiSize(mod); @@ -1903,7 +1902,7 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void { const rhs = try self.resolveInst(bin_op.rhs); const lhs_ty = self.typeOf(bin_op.lhs); const rhs_ty = self.typeOf(bin_op.rhs); - assert(lhs_ty.eql(rhs_ty, self.bin_file.options.module.?)); + assert(lhs_ty.eql(rhs_ty, self.bin_file.comp.module.?)); if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); @@ -2045,7 +2044,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { //const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const lhs = try self.resolveInst(extra.lhs); const rhs = try self.resolveInst(extra.rhs); @@ -2109,7 +2108,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { fn airNot(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand = try self.resolveInst(ty_op.operand); const operand_ty = self.typeOf(ty_op.operand); @@ -2341,7 +2340,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const lhs = try self.resolveInst(extra.lhs); const rhs = try self.resolveInst(extra.rhs); @@ -2446,7 +2445,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void { } fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const is_volatile = false; // TODO const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; @@ -2571,7 +2570,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { const operand = extra.struct_operand; const index = extra.field_index; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const mcv = try self.resolveInst(operand); const struct_ty = self.typeOf(operand); const struct_field_offset = @as(u32, @intCast(struct_ty.structFieldOffset(index, mod))); @@ -2704,7 +2703,7 @@ fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void { } fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const error_union_ty = self.typeOf(ty_op.operand); @@ -2718,7 +2717,7 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { } fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const error_union_ty = self.typeOf(ty_op.operand); @@ -2732,7 +2731,7 @@ fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { /// E to E!T fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const error_union_ty = ty_op.ty.toType(); @@ -2753,7 +2752,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { } fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const optional_ty = self.typeOfIndex(inst); @@ -2793,7 +2792,7 @@ fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: Alignme /// Use a pointer instruction as the basis for allocating stack memory. fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const elem_ty = self.typeOfIndex(inst).childType(mod); if (!elem_ty.hasRuntimeBits(mod)) { @@ -2813,7 +2812,7 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { } fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const elem_ty = self.typeOfIndex(inst); const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse { return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); @@ -2860,7 +2859,7 @@ fn binOp( rhs_ty: Type, metadata: ?BinOpMetadata, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (tag) { .add, .sub, @@ -3401,7 +3400,7 @@ fn binOpRegister( fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { const block_data = self.blocks.getPtr(block).?; - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; if (self.typeOf(operand).hasRuntimeBits(mod)) { const operand_mcv = try self.resolveInst(operand); const block_mcv = block_data.mcv; @@ -3521,7 +3520,7 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { /// Given an error union, returns the payload fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const err_ty = error_union_ty.errorUnionSet(mod); const payload_ty = error_union_ty.errorUnionPayload(mod); if (err_ty.errorSetIsEmpty(mod)) { @@ -3547,7 +3546,8 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(self.err_msg == null); - self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); + const gpa = self.gpa; + self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args); return error.CodegenFail; } @@ -3591,7 +3591,7 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live } fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg; const ty = arg.ty.toType(); const owner_decl = mod.funcOwnerDeclIndex(self.func_index); @@ -3740,7 +3740,7 @@ fn genLoadASI(self: *Self, value_reg: Register, addr_reg: Register, off_reg: Reg } fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (mcv) { .dead => unreachable, .unreach, .none => return, // Nothing to do. @@ -3951,7 +3951,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void } fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size = ty.abiSize(mod); switch (mcv) { .dead => unreachable, @@ -4125,7 +4125,7 @@ fn genStoreASI(self: *Self, value_reg: Register, addr_reg: Register, off_reg: Re } fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const mcv: MCValue = switch (try codegen.genTypedValue( self.bin_file, self.src_loc, @@ -4161,7 +4161,7 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { } fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const error_type = ty.errorUnionSet(mod); const payload_type = ty.errorUnionPayload(mod); @@ -4259,7 +4259,7 @@ fn jump(self: *Self, inst: Mir.Inst.Index) !void { } fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const elem_ty = ptr_ty.childType(mod); const elem_size = elem_ty.abiSize(mod); @@ -4330,7 +4330,7 @@ fn minMax( lhs_ty: Type, rhs_ty: Type, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; assert(lhs_ty.eql(rhs_ty, mod)); switch (lhs_ty.zigTypeTag(mod)) { .Float => return self.fail("TODO min/max on floats", .{}), @@ -4450,7 +4450,7 @@ fn realStackOffset(off: u32) u32 { /// Caller must call `CallMCValues.deinit`. fn resolveCallingConventionValues(self: *Self, fn_ty: Type, role: RegisterView) !CallMCValues { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ip = &mod.intern_pool; const fn_info = mod.typeToFunc(fn_ty).?; const cc = fn_info.cc; @@ -4542,7 +4542,7 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type, role: RegisterView) } fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty = self.typeOf(ref); // If the type has no codegen bits, no need to store it. @@ -4559,7 +4559,7 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue { } fn ret(self: *Self, mcv: MCValue) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ret_ty = self.fn_type.fnReturnType(mod); try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); @@ -4661,7 +4661,7 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void } fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size = value_ty.abiSize(mod); switch (ptr) { @@ -4703,7 +4703,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue { return if (self.liveness.isUnused(inst)) .dead else result: { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const mcv = try self.resolveInst(operand); const ptr_ty = self.typeOf(operand); const struct_ty = ptr_ty.childType(mod); @@ -4745,7 +4745,7 @@ fn trunc( operand_ty: Type, dest_ty: Type, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const info_a = operand_ty.intInfo(mod); const info_b = dest_ty.intInfo(mod); @@ -4857,7 +4857,7 @@ fn truncRegister( /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`. fn wantSafety(self: *Self) bool { - return switch (self.bin_file.options.optimize_mode) { + return switch (self.bin_file.comp.root_mod.optimize_mode) { .Debug => true, .ReleaseSafe => true, .ReleaseFast => false, @@ -4866,11 +4866,11 @@ fn wantSafety(self: *Self) bool { } fn typeOf(self: *Self, inst: Air.Inst.Ref) Type { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; return self.air.typeOf(inst, &mod.intern_pool); } fn typeOfIndex(self: *Self, inst: Air.Inst.Index) Type { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; return self.air.typeOfIndex(inst, &mod.intern_pool); } diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 4701035bc922..b2f2e6f79d08 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -152,14 +152,16 @@ pub fn emitMir( } pub fn deinit(emit: *Emit) void { + const comp = emit.bin_file.comp; + const gpa = comp.gpa; var iter = emit.branch_forward_origins.valueIterator(); while (iter.next()) |origin_list| { - origin_list.deinit(emit.bin_file.allocator); + origin_list.deinit(gpa); } - emit.branch_types.deinit(emit.bin_file.allocator); - emit.branch_forward_origins.deinit(emit.bin_file.allocator); - emit.code_offset_mapping.deinit(emit.bin_file.allocator); + emit.branch_types.deinit(gpa); + emit.branch_forward_origins.deinit(gpa); + emit.code_offset_mapping.deinit(gpa); emit.* = undefined; } @@ -511,7 +513,9 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) !void { fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(emit.err_msg == null); - emit.err_msg = try ErrorMsg.create(emit.bin_file.allocator, emit.src_loc, format, args); + const comp = emit.bin_file.comp; + const gpa = comp.gpa; + emit.err_msg = try ErrorMsg.create(gpa, emit.src_loc, format, args); return error.EmitFail; } @@ -537,8 +541,9 @@ fn isBranch(tag: Mir.Inst.Tag) bool { } fn lowerBranches(emit: *Emit) !void { + const comp = emit.bin_file.comp; + const gpa = comp.gpa; const mir_tags = emit.mir.instructions.items(.tag); - const allocator = emit.bin_file.allocator; // First pass: Note down all branches and their target // instructions, i.e. populate branch_types, @@ -552,7 +557,7 @@ fn lowerBranches(emit: *Emit) !void { const target_inst = emit.branchTarget(inst); // Remember this branch instruction - try emit.branch_types.put(allocator, inst, BranchType.default(tag)); + try emit.branch_types.put(gpa, inst, BranchType.default(tag)); // Forward branches require some extra stuff: We only // know their offset once we arrive at the target @@ -562,14 +567,14 @@ fn lowerBranches(emit: *Emit) !void { // etc. if (target_inst > inst) { // Remember the branch instruction index - try emit.code_offset_mapping.put(allocator, inst, 0); + try emit.code_offset_mapping.put(gpa, inst, 0); if (emit.branch_forward_origins.getPtr(target_inst)) |origin_list| { - try origin_list.append(allocator, inst); + try origin_list.append(gpa, inst); } else { var origin_list: std.ArrayListUnmanaged(Mir.Inst.Index) = .{}; - try origin_list.append(allocator, inst); - try emit.branch_forward_origins.put(allocator, target_inst, origin_list); + try origin_list.append(gpa, inst); + try emit.branch_forward_origins.put(gpa, target_inst, origin_list); } } @@ -579,7 +584,7 @@ fn lowerBranches(emit: *Emit) !void { // putNoClobber may not be used as the put operation // may clobber the entry when multiple branches branch // to the same target instruction - try emit.code_offset_mapping.put(allocator, target_inst, 0); + try emit.code_offset_mapping.put(gpa, target_inst, 0); } } diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index a9028768f714..da33ae521d49 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -766,7 +766,7 @@ pub fn deinit(func: *CodeGen) void { /// Sets `err_msg` on `CodeGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) InnerError { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const src = LazySrcLoc.nodeOffset(0); const src_loc = src.toSrcLoc(func.decl, mod); func.err_msg = try Module.ErrorMsg.create(func.gpa, src_loc, fmt, args); @@ -791,7 +791,7 @@ fn resolveInst(func: *CodeGen, ref: Air.Inst.Ref) InnerError!WValue { const gop = try func.branches.items[0].values.getOrPut(func.gpa, ref); assert(!gop.found_existing); - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const val = (try func.air.value(ref, mod)).?; const ty = func.typeOf(ref); if (!ty.hasRuntimeBitsIgnoreComptime(mod) and !ty.isInt(mod) and !ty.isError(mod)) { @@ -1101,7 +1101,7 @@ fn getResolvedInst(func: *CodeGen, ref: Air.Inst.Ref) *WValue { /// Creates one locals for a given `Type`. /// Returns a corresponding `Wvalue` with `local` as active tag fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const valtype = typeToValtype(ty, mod); switch (valtype) { .i32 => if (func.free_locals_i32.popOrNull()) |index| { @@ -1133,7 +1133,7 @@ fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue { /// Ensures a new local will be created. This is useful when it's useful /// to use a zero-initialized local. fn ensureAllocLocal(func: *CodeGen, ty: Type) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; try func.locals.append(func.gpa, genValtype(ty, mod)); const initial_index = func.local_index; func.local_index += 1; @@ -1210,18 +1210,23 @@ pub fn generate( debug_output: codegen.DebugInfoOutput, ) codegen.CodeGenError!codegen.Result { _ = src_loc; - const mod = bin_file.options.module.?; + const comp = bin_file.comp; + const gpa = comp.gpa; + const mod = comp.module.?; const func = mod.funcInfo(func_index); + const decl = mod.declPtr(func.owner_decl); + const namespace = mod.namespacePtr(decl.src_namespace); + const target = namespace.file_scope.mod.resolved_target.result; var code_gen: CodeGen = .{ - .gpa = bin_file.allocator, + .gpa = gpa, .air = air, .liveness = liveness, .code = code, .decl_index = func.owner_decl, - .decl = mod.declPtr(func.owner_decl), + .decl = decl, .err_msg = undefined, .locals = .{}, - .target = bin_file.options.target, + .target = target, .bin_file = bin_file.cast(link.File.Wasm).?, .debug_output = debug_output, .func_index = func_index, @@ -1237,7 +1242,7 @@ pub fn generate( } fn genFunc(func: *CodeGen) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ip = &mod.intern_pool; const fn_info = mod.typeToFunc(func.decl.ty).?; var func_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types.get(ip), Type.fromInterned(fn_info.return_type), mod); @@ -1348,7 +1353,7 @@ const CallWValues = struct { }; fn resolveCallingConventionValues(func: *CodeGen, fn_ty: Type) InnerError!CallWValues { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ip = &mod.intern_pool; const fn_info = mod.typeToFunc(fn_ty).?; const cc = fn_info.cc; @@ -1417,7 +1422,7 @@ fn lowerArg(func: *CodeGen, cc: std.builtin.CallingConvention, ty: Type, value: return func.lowerToStack(value); } - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_classes = abi.classifyType(ty, mod); assert(ty_classes[0] != .none); switch (ty.zigTypeTag(mod)) { @@ -1516,7 +1521,7 @@ fn restoreStackPointer(func: *CodeGen) !void { /// /// Asserts Type has codegenbits fn allocStack(func: *CodeGen, ty: Type) !WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; assert(ty.hasRuntimeBitsIgnoreComptime(mod)); if (func.initial_stack_value == .none) { try func.initializeStack(); @@ -1542,7 +1547,7 @@ fn allocStack(func: *CodeGen, ty: Type) !WValue { /// This is different from allocStack where this will use the pointer's alignment /// if it is set, to ensure the stack alignment will be set correctly. fn allocStackPtr(func: *CodeGen, inst: Air.Inst.Index) !WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ptr_ty = func.typeOfIndex(inst); const pointee_ty = ptr_ty.childType(mod); @@ -2072,7 +2077,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn genBody(func: *CodeGen, body: []const Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ip = &mod.intern_pool; for (body) |inst| { @@ -2093,7 +2098,7 @@ fn genBody(func: *CodeGen, body: []const Air.Inst.Index) InnerError!void { } fn airRet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand = try func.resolveInst(un_op); const fn_info = mod.typeToFunc(func.decl.ty).?; @@ -2136,7 +2141,7 @@ fn airRet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airRetPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const child_type = func.typeOfIndex(inst).childType(mod); const result = result: { @@ -2156,7 +2161,7 @@ fn airRetPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airRetLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand = try func.resolveInst(un_op); const ret_ty = func.typeOf(un_op).childType(mod); @@ -2183,7 +2188,7 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif const args = @as([]const Air.Inst.Ref, @ptrCast(func.air.extra[extra.end..][0..extra.data.args_len])); const ty = func.typeOf(pl_op.operand); - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ip = &mod.intern_pool; const fn_ty = switch (ty.zigTypeTag(mod)) { .Fn => ty, @@ -2295,7 +2300,7 @@ fn airAlloc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; if (safety) { // TODO if the value is undef, write 0xaa bytes to dest } else { @@ -2349,7 +2354,7 @@ fn airStore(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerError!void { assert(!(lhs != .stack and rhs == .stack)); - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const abi_size = ty.abiSize(mod); switch (ty.zigTypeTag(mod)) { .ErrorUnion => { @@ -2428,7 +2433,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE }, else => if (abi_size > 8) { return func.fail("TODO: `store` for type `{}` with abisize `{d}`", .{ - ty.fmt(func.bin_file.base.options.module.?), + ty.fmt(func.bin_file.base.comp.module.?), abi_size, }); }, @@ -2456,7 +2461,7 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE } fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try func.resolveInst(ty_op.operand); const ty = ty_op.ty.toType(); @@ -2499,7 +2504,7 @@ fn airLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { /// Loads an operand from the linear memory section. /// NOTE: Leaves the value on the stack. fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; // load local's value from memory by its stack position try func.emitWValue(operand); @@ -2536,7 +2541,7 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu } fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const arg_index = func.arg_index; const arg = func.args[arg_index]; const cc = mod.typeToFunc(func.decl.ty).?.cc; @@ -2555,7 +2560,7 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { if (arg_ty.zigTypeTag(mod) != .Int and arg_ty.zigTypeTag(mod) != .Float) { return func.fail( "TODO: Implement C-ABI argument for type '{}'", - .{arg_ty.fmt(func.bin_file.base.options.module.?)}, + .{arg_ty.fmt(func.bin_file.base.comp.module.?)}, ); } const result = try func.allocStack(arg_ty); @@ -2582,7 +2587,7 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const lhs = try func.resolveInst(bin_op.lhs); const rhs = try func.resolveInst(bin_op.rhs); @@ -2619,7 +2624,7 @@ fn airBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { /// Performs a binary operation on the given `WValue`'s /// NOTE: THis leaves the value on top of the stack. fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; assert(!(lhs != .stack and rhs == .stack)); if (ty.isAnyFloat()) { @@ -2633,7 +2638,7 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError! } else { return func.fail( "TODO: Implement binary operation for type: {}", - .{ty.fmt(func.bin_file.base.options.module.?)}, + .{ty.fmt(func.bin_file.base.comp.module.?)}, ); } } @@ -2652,7 +2657,7 @@ fn binOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError! } fn binOpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const int_info = ty.intInfo(mod); if (int_info.bits > 128) { return func.fail("TODO: Implement binary operation for big integers larger than 128 bits", .{}); @@ -2788,7 +2793,7 @@ const FloatOp = enum { }; fn airAbs(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try func.resolveInst(ty_op.operand); const ty = func.typeOf(ty_op.operand); @@ -2873,7 +2878,7 @@ fn airUnaryFloatOp(func: *CodeGen, inst: Air.Inst.Index, op: FloatOp) InnerError } fn floatOp(func: *CodeGen, float_op: FloatOp, ty: Type, args: []const WValue) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; if (ty.zigTypeTag(mod) == .Vector) { return func.fail("TODO: Implement floatOps for vectors", .{}); } @@ -2979,7 +2984,7 @@ fn floatNeg(func: *CodeGen, ty: Type, arg: WValue) InnerError!WValue { } fn airWrapBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const lhs = try func.resolveInst(bin_op.lhs); @@ -3030,7 +3035,7 @@ fn wrapBinOp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: Op) InnerEr /// Asserts `Type` is <= 128 bits. /// NOTE: When the Type is <= 64 bits, leaves the value on top of the stack. fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; assert(ty.abiSize(mod) <= 16); const bitsize = @as(u16, @intCast(ty.bitSize(mod))); const wasm_bits = toWasmBits(bitsize) orelse { @@ -3069,7 +3074,7 @@ fn wrapOperand(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue { } fn lowerParentPtr(func: *CodeGen, ptr_val: Value, offset: u32) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ptr = mod.intern_pool.indexToKey(ptr_val.ip_index).ptr; switch (ptr.addr) { .decl => |decl_index| { @@ -3132,7 +3137,7 @@ fn lowerParentPtr(func: *CodeGen, ptr_val: Value, offset: u32) InnerError!WValue } fn lowerParentPtrDecl(func: *CodeGen, ptr_val: Value, decl_index: InternPool.DeclIndex, offset: u32) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const decl = mod.declPtr(decl_index); try mod.markDeclAlive(decl); const ptr_ty = try mod.singleMutPtrType(decl.ty); @@ -3144,7 +3149,7 @@ fn lowerAnonDeclRef( anon_decl: InternPool.Key.Ptr.Addr.AnonDecl, offset: u32, ) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const decl_val = anon_decl.val; const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val)); @@ -3172,7 +3177,7 @@ fn lowerAnonDeclRef( } fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: InternPool.DeclIndex, offset: u32) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; if (tv.ty.isSlice(mod)) { return WValue{ .memory = try func.bin_file.lowerUnnamedConst(tv, decl_index) }; } @@ -3225,7 +3230,7 @@ fn toTwosComplement(value: anytype, bits: u7) std.meta.Int(.unsigned, @typeInfo( /// This function is intended to assert that `isByRef` returns `false` for `ty`. /// However such an assertion fails on the behavior tests currently. fn lowerConstant(func: *CodeGen, val: Value, ty: Type) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; // TODO: enable this assertion //assert(!isByRef(ty, mod)); const ip = &mod.intern_pool; @@ -3394,7 +3399,7 @@ fn storeSimdImmd(func: *CodeGen, value: [16]u8) !WValue { } fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ip = &mod.intern_pool; switch (ty.zigTypeTag(mod)) { .Bool, .ErrorSet => return WValue{ .imm32 = 0xaaaaaaaa }, @@ -3436,7 +3441,7 @@ fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue { /// It's illegal to provide a value with a type that cannot be represented /// as an integer value. fn valueAsI32(func: *const CodeGen, val: Value, ty: Type) i32 { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; switch (val.ip_index) { .none => {}, @@ -3472,7 +3477,7 @@ fn intStorageAsI32(storage: InternPool.Key.Int.Storage, mod: *Module) i32 { } fn airBlock(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const block_ty = ty_pl.ty.toType(); const wasm_block_ty = genBlockType(block_ty, mod); @@ -3593,7 +3598,7 @@ fn airCmp(func: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) In /// NOTE: This leaves the result on top of the stack, rather than a new local. fn cmp(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, op: std.math.CompareOperator) InnerError!WValue { assert(!(lhs != .stack and rhs == .stack)); - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; if (ty.zigTypeTag(mod) == .Optional and !ty.optionalReprIsPayload(mod)) { const payload_ty = ty.optionalChild(mod); if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { @@ -3711,7 +3716,7 @@ fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const errors_len = WValue{ .memory = sym_index }; try func.emitWValue(operand); - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const err_int_ty = try mod.errorIntType(); const errors_len_val = try func.load(errors_len, err_int_ty, 0); const result = try func.cmp(.stack, errors_len_val, err_int_ty, .lt); @@ -3720,7 +3725,7 @@ fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const br = func.air.instructions.items(.data)[@intFromEnum(inst)].br; const block = func.blocks.get(br.block_inst).?; @@ -3747,7 +3752,7 @@ fn airNot(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const operand = try func.resolveInst(ty_op.operand); const operand_ty = func.typeOf(ty_op.operand); - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const result = result: { if (operand_ty.zigTypeTag(mod) == .Bool) { @@ -3818,7 +3823,7 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const bitcast_result = try func.bitcast(wanted_ty, given_ty, operand); break :result try bitcast_result.toLocal(func, wanted_ty); } - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; if (isByRef(given_ty, mod) and !isByRef(wanted_ty, mod)) { const loaded_memory = try func.load(operand, wanted_ty, 0); break :result try loaded_memory.toLocal(func, wanted_ty); @@ -3834,7 +3839,7 @@ fn airBitcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; // if we bitcast a float to or from an integer we must use the 'reinterpret' instruction if (!(wanted_ty.isAnyFloat() or given_ty.isAnyFloat())) return operand; if (wanted_ty.ip_index == .f16_type or given_ty.ip_index == .f16_type) return operand; @@ -3852,7 +3857,7 @@ fn bitcast(func: *CodeGen, wanted_ty: Type, given_ty: Type, operand: WValue) Inn } fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = func.air.extraData(Air.StructField, ty_pl.payload); @@ -3864,7 +3869,7 @@ fn airStructFieldPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airStructFieldPtrIndex(func: *CodeGen, inst: Air.Inst.Index, index: u32) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const struct_ptr = try func.resolveInst(ty_op.operand); const struct_ptr_ty = func.typeOf(ty_op.operand); @@ -3883,7 +3888,7 @@ fn structFieldPtr( struct_ty: Type, index: u32, ) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const result_ty = func.typeOfIndex(inst); const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(mod); @@ -3914,7 +3919,7 @@ fn structFieldPtr( } fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ip = &mod.intern_pool; const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const struct_field = func.air.extraData(Air.StructField, ty_pl.payload).data; @@ -4013,7 +4018,7 @@ fn airStructFieldVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; // result type is always 'noreturn' const blocktype = wasm.block_empty; const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; @@ -4193,7 +4198,7 @@ fn airSwitchBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand = try func.resolveInst(un_op); const err_union_ty = func.typeOf(un_op); @@ -4228,7 +4233,7 @@ fn airIsErr(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode) InnerErro } fn airUnwrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try func.resolveInst(ty_op.operand); @@ -4256,7 +4261,7 @@ fn airUnwrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: boo } fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try func.resolveInst(ty_op.operand); @@ -4280,7 +4285,7 @@ fn airUnwrapErrUnionError(func: *CodeGen, inst: Air.Inst.Index, op_is_ptr: bool) } fn airWrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try func.resolveInst(ty_op.operand); @@ -4310,7 +4315,7 @@ fn airWrapErrUnionPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void } fn airWrapErrUnionErr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try func.resolveInst(ty_op.operand); @@ -4342,7 +4347,7 @@ fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const ty = ty_op.ty.toType(); const operand = try func.resolveInst(ty_op.operand); const operand_ty = func.typeOf(ty_op.operand); - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; if (ty.zigTypeTag(mod) == .Vector or operand_ty.zigTypeTag(mod) == .Vector) { return func.fail("todo Wasm intcast for vectors", .{}); } @@ -4365,7 +4370,7 @@ fn airIntcast(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { /// Asserts type's bitsize <= 128 /// NOTE: May leave the result on the top of the stack. fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const given_bitsize = @as(u16, @intCast(given.bitSize(mod))); const wanted_bitsize = @as(u16, @intCast(wanted.bitSize(mod))); assert(given_bitsize <= 128); @@ -4433,7 +4438,7 @@ fn intcast(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro } fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: enum { value, ptr }) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand = try func.resolveInst(un_op); @@ -4447,7 +4452,7 @@ fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: /// For a given type and operand, checks if it's considered `null`. /// NOTE: Leaves the result on the stack fn isNull(func: *CodeGen, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; try func.emitWValue(operand); const payload_ty = optional_ty.optionalChild(mod); if (!optional_ty.optionalReprIsPayload(mod)) { @@ -4475,7 +4480,7 @@ fn isNull(func: *CodeGen, operand: WValue, optional_ty: Type, opcode: wasm.Opcod } fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const opt_ty = func.typeOf(ty_op.operand); const payload_ty = func.typeOfIndex(inst); @@ -4498,7 +4503,7 @@ fn airOptionalPayload(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airOptionalPayloadPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try func.resolveInst(ty_op.operand); const opt_ty = func.typeOf(ty_op.operand).childType(mod); @@ -4515,7 +4520,7 @@ fn airOptionalPayloadPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airOptionalPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try func.resolveInst(ty_op.operand); const opt_ty = func.typeOf(ty_op.operand).childType(mod); @@ -4543,7 +4548,7 @@ fn airOptionalPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi fn airWrapOptional(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const payload_ty = func.typeOf(ty_op.operand); - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const result = result: { if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { @@ -4600,7 +4605,7 @@ fn airSliceLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const slice_ty = func.typeOf(bin_op.lhs); @@ -4630,7 +4635,7 @@ fn airSliceElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airSliceElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data; @@ -4683,7 +4688,7 @@ fn airTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { /// Truncates a given operand to a given type, discarding any overflown bits. /// NOTE: Resulting value is left on the stack. fn trunc(func: *CodeGen, operand: WValue, wanted_ty: Type, given_ty: Type) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const given_bits = @as(u16, @intCast(given_ty.bitSize(mod))); if (toWasmBits(given_bits) == null) { return func.fail("TODO: Implement wasm integer truncation for integer bitsize: {d}", .{given_bits}); @@ -4707,7 +4712,7 @@ fn airIntFromBool(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try func.resolveInst(ty_op.operand); @@ -4730,7 +4735,7 @@ fn airArrayToSlice(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airIntFromPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const un_op = func.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand = try func.resolveInst(un_op); const ptr_ty = func.typeOf(un_op); @@ -4745,7 +4750,7 @@ fn airIntFromPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ptr_ty = func.typeOf(bin_op.lhs); @@ -4782,7 +4787,7 @@ fn airPtrElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airPtrElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data; @@ -4812,7 +4817,7 @@ fn airPtrElemPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = func.air.extraData(Air.Bin, ty_pl.payload).data; @@ -4840,7 +4845,7 @@ fn airPtrBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { } fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; if (safety) { // TODO if the value is undef, write 0xaa bytes to dest } else { @@ -4873,7 +4878,7 @@ fn airMemset(func: *CodeGen, inst: Air.Inst.Index, safety: bool) InnerError!void /// this to wasm's memset instruction. When the feature is not present, /// we implement it manually. fn memset(func: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const abi_size = @as(u32, @intCast(elem_ty.abiSize(mod))); // When bulk_memory is enabled, we lower it to wasm's memset instruction. @@ -4962,7 +4967,7 @@ fn memset(func: *CodeGen, elem_ty: Type, ptr: WValue, len: WValue, value: WValue } fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const array_ty = func.typeOf(bin_op.lhs); @@ -5031,7 +5036,7 @@ fn airArrayElemVal(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airIntFromFloat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try func.resolveInst(ty_op.operand); @@ -5076,7 +5081,7 @@ fn airIntFromFloat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airFloatFromInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try func.resolveInst(ty_op.operand); @@ -5122,7 +5127,7 @@ fn airFloatFromInt(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airSplat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try func.resolveInst(ty_op.operand); const ty = func.typeOfIndex(inst); @@ -5201,7 +5206,7 @@ fn airSelect(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airShuffle(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const inst_ty = func.typeOfIndex(inst); const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = func.air.extraData(Air.Shuffle, ty_pl.payload).data; @@ -5270,7 +5275,7 @@ fn airReduce(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ip = &mod.intern_pool; const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const result_ty = func.typeOfIndex(inst); @@ -5400,7 +5405,7 @@ fn airAggregateInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airUnionInit(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ip = &mod.intern_pool; const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = func.air.extraData(Air.UnionInit, ty_pl.payload).data; @@ -5499,7 +5504,7 @@ fn airWasmMemoryGrow(func: *CodeGen, inst: Air.Inst.Index) !void { } fn cmpOptionals(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; assert(operand_ty.hasRuntimeBitsIgnoreComptime(mod)); assert(op == .eq or op == .neq); const payload_ty = operand_ty.optionalChild(mod); @@ -5535,7 +5540,7 @@ fn cmpOptionals(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: /// NOTE: Leaves the result of the comparison on top of the stack. /// TODO: Lower this to compiler_rt call when bitsize > 128 fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; assert(operand_ty.abiSize(mod) >= 16); assert(!(lhs != .stack and rhs == .stack)); if (operand_ty.bitSize(mod) > 128) { @@ -5577,7 +5582,7 @@ fn cmpBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, operand_ty: Type, op: std } fn airSetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const un_ty = func.typeOf(bin_op.lhs).childType(mod); const tag_ty = func.typeOf(bin_op.rhs); @@ -5601,7 +5606,7 @@ fn airSetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airGetUnionTag(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const un_ty = func.typeOf(ty_op.operand); @@ -5706,7 +5711,7 @@ fn fptrunc(func: *CodeGen, operand: WValue, given: Type, wanted: Type) InnerErro } fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const err_set_ty = func.typeOf(ty_op.operand).childType(mod); @@ -5732,7 +5737,7 @@ fn airErrUnionPayloadPtrSet(func: *CodeGen, inst: Air.Inst.Index) InnerError!voi } fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = func.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; @@ -5753,7 +5758,7 @@ fn airFieldParentPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn sliceOrArrayPtr(func: *CodeGen, ptr: WValue, ptr_ty: Type) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; if (ptr_ty.isSlice(mod)) { return func.slicePtr(ptr); } else { @@ -5762,7 +5767,7 @@ fn sliceOrArrayPtr(func: *CodeGen, ptr: WValue, ptr_ty: Type) InnerError!WValue } fn airMemcpy(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const dst = try func.resolveInst(bin_op.lhs); const dst_ty = func.typeOf(bin_op.lhs); @@ -5802,7 +5807,7 @@ fn airRetAddr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airPopcount(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try func.resolveInst(ty_op.operand); @@ -5863,7 +5868,7 @@ fn airErrorName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { // to make a copy of the ptr+value but can point towards them directly. const error_table_symbol = try func.bin_file.getErrorTableSymbol(); const name_ty = Type.slice_const_u8_sentinel_0; - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const abi_size = name_ty.abiSize(mod); const error_name_value: WValue = .{ .memory = error_table_symbol }; // emitting this will create a relocation @@ -5903,7 +5908,7 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro const lhs_op = try func.resolveInst(extra.lhs); const rhs_op = try func.resolveInst(extra.rhs); const lhs_ty = func.typeOf(extra.lhs); - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; if (lhs_ty.zigTypeTag(mod) == .Vector) { return func.fail("TODO: Implement overflow arithmetic for vectors", .{}); @@ -5975,7 +5980,7 @@ fn airAddSubWithOverflow(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerErro } fn addSubWithOverflowBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, result_ty: Type, op: Op) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; assert(op == .add or op == .sub); const int_info = ty.intInfo(mod); const is_signed = int_info.signedness == .signed; @@ -6040,7 +6045,7 @@ fn addSubWithOverflowBigInt(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, } fn airShlWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = func.air.extraData(Air.Bin, ty_pl.payload).data; @@ -6103,7 +6108,7 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const lhs = try func.resolveInst(extra.lhs); const rhs = try func.resolveInst(extra.rhs); const lhs_ty = func.typeOf(extra.lhs); - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; if (lhs_ty.zigTypeTag(mod) == .Vector) { return func.fail("TODO: Implement overflow arithmetic for vectors", .{}); @@ -6273,7 +6278,7 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { assert(op == .max or op == .min); - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const target = mod.getTarget(); const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; @@ -6317,7 +6322,7 @@ fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { } fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const bin_op = func.air.extraData(Air.Bin, pl_op.payload).data; @@ -6351,7 +6356,7 @@ fn airMulAdd(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const ty = func.typeOf(ty_op.operand); @@ -6404,7 +6409,7 @@ fn airClz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const ty = func.typeOf(ty_op.operand); @@ -6471,7 +6476,7 @@ fn airCtz(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void { if (func.debug_output != .dwarf) return func.finishAir(inst, .none, &.{}); - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const ty = func.typeOf(pl_op.operand); const operand = try func.resolveInst(pl_op.operand); @@ -6517,7 +6522,7 @@ fn airTry(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airTryPtr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = func.air.extraData(Air.TryPtr, ty_pl.payload); const err_union_ptr = try func.resolveInst(extra.data.ptr); @@ -6535,7 +6540,7 @@ fn lowerTry( err_union_ty: Type, operand_is_ptr: bool, ) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; if (operand_is_ptr) { return func.fail("TODO: lowerTry for pointers", .{}); } @@ -6584,7 +6589,7 @@ fn lowerTry( } fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const ty = func.typeOfIndex(inst); @@ -6655,7 +6660,7 @@ fn airByteSwap(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ty = func.typeOfIndex(inst); @@ -6670,7 +6675,7 @@ fn airDiv(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airDivTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ty = func.typeOfIndex(inst); @@ -6693,7 +6698,7 @@ fn airDivTrunc(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty = func.typeOfIndex(inst); const lhs = try func.resolveInst(bin_op.lhs); const rhs = try func.resolveInst(bin_op.rhs); @@ -6806,7 +6811,7 @@ fn airDivFloor(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn divSigned(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const int_bits = ty.intInfo(mod).bits; const wasm_bits = toWasmBits(int_bits) orelse { return func.fail("TODO: Implement signed division for integers with bitsize '{d}'", .{int_bits}); @@ -6836,7 +6841,7 @@ fn divSigned(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type) InnerError!WVal fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty = func.typeOfIndex(inst); const lhs = try func.resolveInst(bin_op.lhs); const rhs = try func.resolveInst(bin_op.rhs); @@ -6883,7 +6888,7 @@ fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { /// The result will be sign extended to 32 bits if N <= 32 or 64 bits if N <= 64. /// Support for integers wider than 64 bits has not yet been implemented. fn signExtendInt(func: *CodeGen, operand: WValue, ty: Type) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const int_bits = ty.intInfo(mod).bits; const wasm_bits = toWasmBits(int_bits) orelse { return func.fail("TODO: signExtendInt for signed integers larger than '{d}' bits", .{int_bits}); @@ -6919,7 +6924,7 @@ fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { assert(op == .add or op == .sub); const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty = func.typeOfIndex(inst); const lhs = try func.resolveInst(bin_op.lhs); const rhs = try func.resolveInst(bin_op.rhs); @@ -6967,7 +6972,7 @@ fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void { } fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op: Op) InnerError!WValue { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const int_info = ty.intInfo(mod); const wasm_bits = toWasmBits(int_info.bits).?; const is_wasm_bits = wasm_bits == int_info.bits; @@ -7034,7 +7039,7 @@ fn signedSat(func: *CodeGen, lhs_operand: WValue, rhs_operand: WValue, ty: Type, fn airShlSat(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty = func.typeOfIndex(inst); const int_info = ty.intInfo(mod); const is_signed = int_info.signedness == .signed; @@ -7155,7 +7160,7 @@ fn callIntrinsic( }; // Always pass over C-ABI - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; var func_type = try genFunctype(func.gpa, .C, param_types, return_type, mod); defer func_type.deinit(func.gpa); const func_type_index = try func.bin_file.putOrGetFuncType(func_type); @@ -7208,7 +7213,7 @@ fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const enum_decl_index = enum_ty.getOwnerDecl(mod); var arena_allocator = std.heap.ArenaAllocator.init(func.gpa); @@ -7364,7 +7369,7 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { } fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_op = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try func.resolveInst(ty_op.operand); @@ -7449,7 +7454,7 @@ inline fn useAtomicFeature(func: *const CodeGen) bool { } fn airCmpxchg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const ty_pl = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = func.air.extraData(Air.Cmpxchg, ty_pl.payload).data; @@ -7522,7 +7527,7 @@ fn airCmpxchg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airAtomicLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const atomic_load = func.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load; const ptr = try func.resolveInst(atomic_load.ptr); const ty = func.typeOfIndex(inst); @@ -7549,7 +7554,7 @@ fn airAtomicLoad(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const pl_op = func.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const extra = func.air.extraData(Air.AtomicRmw, pl_op.payload).data; @@ -7724,10 +7729,13 @@ fn airAtomicRmw(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airFence(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { + const zcu = func.bin_file.base.comp.module.?; // Only when the atomic feature is enabled, and we're not building // for a single-threaded build, can we emit the `fence` instruction. // In all other cases, we emit no instructions for a fence. - if (func.useAtomicFeature() and !func.bin_file.base.options.single_threaded) { + const func_namespace = zcu.namespacePtr(func.decl.src_namespace); + const single_threaded = func_namespace.file_scope.mod.single_threaded; + if (func.useAtomicFeature() and !single_threaded) { try func.addAtomicTag(.atomic_fence); } @@ -7735,7 +7743,7 @@ fn airFence(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn airAtomicStore(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ptr = try func.resolveInst(bin_op.lhs); @@ -7774,11 +7782,11 @@ fn airFrameAddress(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { } fn typeOf(func: *CodeGen, inst: Air.Inst.Ref) Type { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; return func.air.typeOf(inst, &mod.intern_pool); } fn typeOfIndex(func: *CodeGen, inst: Air.Inst.Index) Type { - const mod = func.bin_file.base.options.module.?; + const mod = func.bin_file.base.comp.module.?; return func.air.typeOfIndex(inst, &mod.intern_pool); } diff --git a/src/arch/wasm/Emit.zig b/src/arch/wasm/Emit.zig index d1b7114b1255..7e67a9828582 100644 --- a/src/arch/wasm/Emit.zig +++ b/src/arch/wasm/Emit.zig @@ -254,8 +254,10 @@ fn offset(self: Emit) u32 { fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { @setCold(true); std.debug.assert(emit.error_msg == null); - const mod = emit.bin_file.base.options.module.?; - emit.error_msg = try Module.ErrorMsg.create(emit.bin_file.base.allocator, mod.declPtr(emit.decl_index).srcLoc(mod), format, args); + const comp = emit.bin_file.base.comp; + const zcu = comp.module.?; + const gpa = comp.gpa; + emit.error_msg = try Module.ErrorMsg.create(gpa, zcu.declPtr(emit.decl_index).srcLoc(zcu), format, args); return error.EmitFail; } @@ -299,6 +301,8 @@ fn emitLabel(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void { } fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void { + const comp = emit.bin_file.base.comp; + const gpa = comp.gpa; const label = emit.mir.instructions.items(.data)[inst].label; try emit.code.append(@intFromEnum(tag)); var buf: [5]u8 = undefined; @@ -308,7 +312,7 @@ fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void { const atom_index = emit.bin_file.decls.get(emit.decl_index).?; const atom = emit.bin_file.getAtomPtr(atom_index); - try atom.relocs.append(emit.bin_file.base.allocator, .{ + try atom.relocs.append(gpa, .{ .index = label, .offset = global_offset, .relocation_type = .R_WASM_GLOBAL_INDEX_LEB, @@ -356,6 +360,8 @@ fn encodeMemArg(mem_arg: Mir.MemArg, writer: anytype) !void { } fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void { + const comp = emit.bin_file.base.comp; + const gpa = comp.gpa; const label = emit.mir.instructions.items(.data)[inst].label; try emit.code.append(std.wasm.opcode(.call)); const call_offset = emit.offset(); @@ -366,7 +372,7 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void { if (label != 0) { const atom_index = emit.bin_file.decls.get(emit.decl_index).?; const atom = emit.bin_file.getAtomPtr(atom_index); - try atom.relocs.append(emit.bin_file.base.allocator, .{ + try atom.relocs.append(gpa, .{ .offset = call_offset, .index = label, .relocation_type = .R_WASM_FUNCTION_INDEX_LEB, @@ -384,6 +390,8 @@ fn emitCallIndirect(emit: *Emit, inst: Mir.Inst.Index) !void { } fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void { + const comp = emit.bin_file.base.comp; + const gpa = comp.gpa; const symbol_index = emit.mir.instructions.items(.data)[inst].label; try emit.code.append(std.wasm.opcode(.i32_const)); const index_offset = emit.offset(); @@ -394,7 +402,7 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void { if (symbol_index != 0) { const atom_index = emit.bin_file.decls.get(emit.decl_index).?; const atom = emit.bin_file.getAtomPtr(atom_index); - try atom.relocs.append(emit.bin_file.base.allocator, .{ + try atom.relocs.append(gpa, .{ .offset = index_offset, .index = symbol_index, .relocation_type = .R_WASM_TABLE_INDEX_SLEB, @@ -406,7 +414,10 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void { const extra_index = emit.mir.instructions.items(.data)[inst].payload; const mem = emit.mir.extraData(Mir.Memory, extra_index).data; const mem_offset = emit.offset() + 1; - const is_wasm32 = emit.bin_file.base.options.target.cpu.arch == .wasm32; + const comp = emit.bin_file.base.comp; + const gpa = comp.gpa; + const target = comp.root_mod.resolved_target.result; + const is_wasm32 = target.cpu.arch == .wasm32; if (is_wasm32) { try emit.code.append(std.wasm.opcode(.i32_const)); var buf: [5]u8 = undefined; @@ -422,7 +433,7 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void { if (mem.pointer != 0) { const atom_index = emit.bin_file.decls.get(emit.decl_index).?; const atom = emit.bin_file.getAtomPtr(atom_index); - try atom.relocs.append(emit.bin_file.base.allocator, .{ + try atom.relocs.append(gpa, .{ .offset = mem_offset, .index = mem.pointer, .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64, diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 0a4c9844dcca..55e241cbd4c3 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -25,7 +25,9 @@ const Emit = @import("Emit.zig"); const Liveness = @import("../../Liveness.zig"); const Lower = @import("Lower.zig"); const Mir = @import("Mir.zig"); +const Package = @import("../../Package.zig"); const Module = @import("../../Module.zig"); +const Zcu = Module; const InternPool = @import("../../InternPool.zig"); const Alignment = InternPool.Alignment; const Target = std.Target; @@ -56,6 +58,7 @@ bin_file: *link.File, debug_output: DebugInfoOutput, target: *const std.Target, owner: Owner, +mod: *Package.Module, err_msg: ?*ErrorMsg, args: []MCValue, va_info: union { @@ -131,7 +134,7 @@ const Owner = union(enum) { fn getSymbolIndex(owner: Owner, ctx: *Self) !u32 { switch (owner) { .func_index => |func_index| { - const mod = ctx.bin_file.options.module.?; + const mod = ctx.bin_file.comp.module.?; const decl_index = mod.funcOwnerDeclIndex(func_index); if (ctx.bin_file.cast(link.File.Elf)) |elf_file| { return elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); @@ -795,22 +798,22 @@ pub fn generate( code: *std.ArrayList(u8), debug_output: DebugInfoOutput, ) CodeGenError!Result { - if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { - @panic("Attempted to compile for architecture that was disabled by build configuration"); - } - - const mod = bin_file.options.module.?; - const func = mod.funcInfo(func_index); - const fn_owner_decl = mod.declPtr(func.owner_decl); + const comp = bin_file.comp; + const gpa = comp.gpa; + const zcu = comp.module.?; + const func = zcu.funcInfo(func_index); + const fn_owner_decl = zcu.declPtr(func.owner_decl); assert(fn_owner_decl.has_tv); const fn_type = fn_owner_decl.ty; + const namespace = zcu.namespacePtr(fn_owner_decl.src_namespace); + const mod = namespace.file_scope.mod; - const gpa = bin_file.allocator; var function = Self{ .gpa = gpa, .air = air, .liveness = liveness, - .target = &bin_file.options.target, + .target = &mod.resolved_target.result, + .mod = mod, .bin_file = bin_file, .debug_output = debug_output, .owner = .{ .func_index = func_index }, @@ -841,7 +844,7 @@ pub fn generate( wip_mir_log.debug("{}:", .{function.fmtDecl(func.owner_decl)}); - const ip = &mod.intern_pool; + const ip = &zcu.intern_pool; try function.frame_allocs.resize(gpa, FrameIndex.named_count); function.frame_allocs.set( @@ -856,13 +859,13 @@ pub fn generate( FrameAlloc.init(.{ .size = 0, .alignment = .@"1" }), ); - const fn_info = mod.typeToFunc(fn_type).?; + const fn_info = zcu.typeToFunc(fn_type).?; const cc = abi.resolveCallingConvention(fn_info.cc, function.target.*); var call_info = function.resolveCallingConventionValues(fn_info, &.{}, .args_frame) catch |err| switch (err) { error.CodegenFail => return Result{ .fail = function.err_msg.? }, error.OutOfRegisters => return Result{ .fail = try ErrorMsg.create( - bin_file.allocator, + gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}, @@ -875,14 +878,14 @@ pub fn generate( function.args = call_info.args; function.ret_mcv = call_info.return_value; function.frame_allocs.set(@intFromEnum(FrameIndex.ret_addr), FrameAlloc.init(.{ - .size = Type.usize.abiSize(mod), - .alignment = Type.usize.abiAlignment(mod).min(call_info.stack_align), + .size = Type.usize.abiSize(zcu), + .alignment = Type.usize.abiAlignment(zcu).min(call_info.stack_align), })); function.frame_allocs.set(@intFromEnum(FrameIndex.base_ptr), FrameAlloc.init(.{ - .size = Type.usize.abiSize(mod), + .size = Type.usize.abiSize(zcu), .alignment = Alignment.min( call_info.stack_align, - Alignment.fromNonzeroByteUnits(bin_file.options.target.stackAlignment()), + Alignment.fromNonzeroByteUnits(function.target.stackAlignment()), ), })); function.frame_allocs.set( @@ -906,25 +909,28 @@ pub fn generate( function.gen() catch |err| switch (err) { error.CodegenFail => return Result{ .fail = function.err_msg.? }, error.OutOfRegisters => return Result{ - .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), }, else => |e| return e, }; var mir = Mir{ .instructions = function.mir_instructions.toOwnedSlice(), - .extra = try function.mir_extra.toOwnedSlice(bin_file.allocator), + .extra = try function.mir_extra.toOwnedSlice(gpa), .frame_locs = function.frame_locs.toOwnedSlice(), }; - defer mir.deinit(bin_file.allocator); + defer mir.deinit(gpa); var emit = Emit{ .lower = .{ .bin_file = bin_file, - .allocator = bin_file.allocator, + .allocator = gpa, .mir = mir, .cc = cc, .src_loc = src_loc, + .output_mode = comp.config.output_mode, + .link_mode = comp.config.link_mode, + .pic = mod.pic, }, .debug_output = debug_output, .code = code, @@ -942,7 +948,7 @@ pub fn generate( }; return Result{ .fail = try ErrorMsg.create( - bin_file.allocator, + gpa, src_loc, "{s} This is a bug in the Zig compiler.", .{msg}, @@ -966,12 +972,16 @@ pub fn generateLazy( code: *std.ArrayList(u8), debug_output: DebugInfoOutput, ) CodeGenError!Result { - const gpa = bin_file.allocator; + const comp = bin_file.comp; + const gpa = comp.gpa; + // This function is for generating global code, so we use the root module. + const mod = comp.root_mod; var function = Self{ .gpa = gpa, .air = undefined, .liveness = undefined, - .target = &bin_file.options.target, + .target = &mod.resolved_target.result, + .mod = mod, .bin_file = bin_file, .debug_output = debug_output, .owner = .{ .lazy_sym = lazy_sym }, @@ -993,25 +1003,28 @@ pub fn generateLazy( function.genLazy(lazy_sym) catch |err| switch (err) { error.CodegenFail => return Result{ .fail = function.err_msg.? }, error.OutOfRegisters => return Result{ - .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), + .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), }, else => |e| return e, }; var mir = Mir{ .instructions = function.mir_instructions.toOwnedSlice(), - .extra = try function.mir_extra.toOwnedSlice(bin_file.allocator), + .extra = try function.mir_extra.toOwnedSlice(gpa), .frame_locs = function.frame_locs.toOwnedSlice(), }; - defer mir.deinit(bin_file.allocator); + defer mir.deinit(gpa); var emit = Emit{ .lower = .{ .bin_file = bin_file, - .allocator = bin_file.allocator, + .allocator = gpa, .mir = mir, .cc = abi.resolveCallingConvention(.Unspecified, function.target.*), .src_loc = src_loc, + .output_mode = comp.config.output_mode, + .link_mode = comp.config.link_mode, + .pic = mod.pic, }, .debug_output = debug_output, .code = code, @@ -1029,7 +1042,7 @@ pub fn generateLazy( }; return Result{ .fail = try ErrorMsg.create( - bin_file.allocator, + gpa, src_loc, "{s} This is a bug in the Zig compiler.", .{msg}, @@ -1060,7 +1073,7 @@ fn formatDecl( } fn fmtDecl(self: *Self, decl_index: InternPool.DeclIndex) std.fmt.Formatter(formatDecl) { return .{ .data = .{ - .mod = self.bin_file.options.module.?, + .mod = self.bin_file.comp.module.?, .decl_index = decl_index, } }; } @@ -1077,7 +1090,7 @@ fn formatAir( ) @TypeOf(writer).Error!void { @import("../../print_air.zig").dumpInst( data.inst, - data.self.bin_file.options.module.?, + data.self.bin_file.comp.module.?, data.self.air, data.self.liveness, ); @@ -1096,6 +1109,8 @@ fn formatWipMir( _: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { + const comp = data.self.bin_file.comp; + const mod = comp.root_mod; var lower = Lower{ .bin_file = data.self.bin_file, .allocator = data.self.gpa, @@ -1106,6 +1121,9 @@ fn formatWipMir( }, .cc = .Unspecified, .src_loc = data.self.src_loc, + .output_mode = comp.config.output_mode, + .link_mode = comp.config.link_mode, + .pic = mod.pic, }; var first = true; for ((lower.lowerMir(data.inst) catch |err| switch (err) { @@ -1683,7 +1701,7 @@ fn asmMemoryRegisterImmediate( } fn gen(self: *Self) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const fn_info = mod.typeToFunc(self.fn_type).?; const cc = abi.resolveCallingConvention(fn_info.cc, self.target.*); if (cc != .Naked) { @@ -1885,7 +1903,7 @@ fn gen(self: *Self) InnerError!void { } fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ip = &mod.intern_pool; const air_tags = self.air.instructions.items(.tag); @@ -2167,7 +2185,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { } fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (lazy_sym.ty.zigTypeTag(mod)) { .Enum => { const enum_ty = lazy_sym.ty; @@ -2418,7 +2436,7 @@ fn allocFrameIndex(self: *Self, alloc: FrameAlloc) !FrameIndex { /// Use a pointer instruction as the basis for allocating stack memory. fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !FrameIndex { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ptr_ty = self.typeOfIndex(inst); const val_ty = ptr_ty.childType(mod); return self.allocFrameIndex(FrameAlloc.init(.{ @@ -2438,7 +2456,7 @@ fn allocTempRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool) !MCValue { } fn allocRegOrMemAdvanced(self: *Self, ty: Type, inst: ?Air.Inst.Index, reg_ok: bool) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size = math.cast(u32, ty.abiSize(mod)) orelse { return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(mod)}); }; @@ -2471,7 +2489,7 @@ fn allocRegOrMemAdvanced(self: *Self, ty: Type, inst: ?Air.Inst.Index, reg_ok: b } fn regClassForType(self: *Self, ty: Type) RegisterManager.RegisterBitSet { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; return switch (ty.zigTypeTag(mod)) { .Float => switch (ty.floatBits(self.target.*)) { 80 => abi.RegisterClass.x87, @@ -2884,7 +2902,7 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void { } fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = result: { const src_ty = self.typeOf(ty_op.operand); @@ -2975,7 +2993,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { } fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const dst_ty = self.typeOfIndex(inst); @@ -3102,7 +3120,7 @@ fn airIntFromBool(self: *Self, inst: Air.Inst.Index) !void { } fn airSlice(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; @@ -3131,7 +3149,7 @@ fn airUnOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { } fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const dst_mcv = try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs); @@ -3172,7 +3190,7 @@ fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void } fn activeIntBits(self: *Self, dst_air: Air.Inst.Ref) u16 { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const air_tag = self.air.instructions.items(.tag); const air_data = self.air.instructions.items(.data); @@ -3206,7 +3224,7 @@ fn activeIntBits(self: *Self, dst_air: Air.Inst.Ref) u16 { } fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const result = result: { const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; @@ -3432,7 +3450,7 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void { } fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ty = self.typeOf(bin_op.lhs); if (ty.zigTypeTag(mod) == .Vector or ty.abiSize(mod) > 8) return self.fail( @@ -3515,7 +3533,7 @@ fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { } fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ty = self.typeOf(bin_op.lhs); if (ty.zigTypeTag(mod) == .Vector or ty.abiSize(mod) > 8) return self.fail( @@ -3591,7 +3609,7 @@ fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { } fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ty = self.typeOf(bin_op.lhs); @@ -3731,7 +3749,7 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { } fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const result: MCValue = result: { @@ -3792,7 +3810,7 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { } fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const result: MCValue = result: { @@ -3871,7 +3889,7 @@ fn genSetFrameTruncatedOverflowCompare( src_mcv: MCValue, overflow_cc: ?Condition, ) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const src_lock = switch (src_mcv) { .register => |reg| self.register_manager.lockReg(reg), else => null, @@ -3935,7 +3953,7 @@ fn genSetFrameTruncatedOverflowCompare( } fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const tuple_ty = self.typeOfIndex(inst); @@ -4169,7 +4187,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { /// Clobbers .rax and .rdx registers. /// Quotient is saved in .rax and remainder in .rdx. fn genIntMulDivOpMir(self: *Self, tag: Mir.Inst.FixedTag, ty: Type, lhs: MCValue, rhs: MCValue) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size: u32 = @intCast(ty.abiSize(mod)); const bit_size: u32 = @intCast(self.regBitSize(ty)); if (abi_size > 8) { @@ -4219,7 +4237,7 @@ fn genIntMulDivOpMir(self: *Self, tag: Mir.Inst.FixedTag, ty: Type, lhs: MCValue /// Always returns a register. /// Clobbers .rax and .rdx registers. fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size: u32 = @intCast(ty.abiSize(mod)); const int_info = ty.intInfo(mod); const dividend = switch (lhs) { @@ -4271,7 +4289,7 @@ fn genInlineIntDivFloor(self: *Self, ty: Type, lhs: MCValue, rhs: MCValue) !MCVa } fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const air_tags = self.air.instructions.items(.tag); @@ -4546,7 +4564,7 @@ fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { } fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = result: { const pl_ty = self.typeOfIndex(inst); @@ -4592,7 +4610,7 @@ fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { } fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result = result: { const dst_ty = self.typeOfIndex(inst); @@ -4626,7 +4644,7 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { } fn airUnwrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const err_union_ty = self.typeOf(ty_op.operand); const err_ty = err_union_ty.errorUnionSet(mod); @@ -4678,7 +4696,7 @@ fn airUnwrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { // *(E!T) -> E fn airUnwrapErrUnionErrPtr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const src_ty = self.typeOf(ty_op.operand); @@ -4725,7 +4743,7 @@ fn airUnwrapErrUnionPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { } fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = result: { const src_ty = self.typeOf(ty_op.operand); @@ -4785,7 +4803,7 @@ fn genUnwrapErrUnionPayloadMir( err_union_ty: Type, err_union: MCValue, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const payload_ty = err_union_ty.errorUnionPayload(mod); const result: MCValue = result: { @@ -4833,7 +4851,7 @@ fn genUnwrapErrUnionPayloadPtrMir( ptr_ty: Type, ptr_mcv: MCValue, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const err_union_ty = ptr_ty.childType(mod); const payload_ty = err_union_ty.errorUnionPayload(mod); @@ -4867,7 +4885,7 @@ fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void { } fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = result: { const pl_ty = self.typeOf(ty_op.operand); @@ -4921,7 +4939,7 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { /// T to E!T fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const eu_ty = ty_op.ty.toType(); @@ -4944,7 +4962,7 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { /// E to E!T fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const eu_ty = ty_op.ty.toType(); @@ -5003,7 +5021,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { } fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const src_ty = self.typeOf(ty_op.operand); @@ -5071,7 +5089,7 @@ fn elemOffset(self: *Self, index_ty: Type, index: MCValue, elem_size: u64) !Regi } fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const slice_ty = self.typeOf(lhs); const slice_mcv = try self.resolveInst(lhs); const slice_mcv_lock: ?RegisterLock = switch (slice_mcv) { @@ -5107,7 +5125,7 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { } fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const result: MCValue = result: { @@ -5132,7 +5150,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { } fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const result: MCValue = result: { @@ -5246,7 +5264,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { } fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ptr_ty = self.typeOf(bin_op.lhs); @@ -5296,7 +5314,7 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { } fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; @@ -5341,7 +5359,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { } fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ptr_union_ty = self.typeOf(bin_op.lhs); const union_ty = ptr_union_ty.childType(mod); @@ -5385,7 +5403,7 @@ fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { } fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const tag_ty = self.typeOfIndex(inst); @@ -5439,7 +5457,7 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { } fn airClz(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result = result: { const dst_ty = self.typeOfIndex(inst); @@ -5598,7 +5616,7 @@ fn airClz(self: *Self, inst: Air.Inst.Index) !void { } fn airCtz(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result = result: { const dst_ty = self.typeOfIndex(inst); @@ -5716,7 +5734,7 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void { } fn airPopCount(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const result: MCValue = result: { try self.spillEflagsIfOccupied(); @@ -5779,7 +5797,7 @@ fn genPopCount( src_mcv: MCValue, dst_contains_src: bool, ) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const src_abi_size: u32 = @intCast(src_ty.abiSize(mod)); if (self.hasFeature(.popcnt)) return self.genBinOpMir( @@ -5871,7 +5889,7 @@ fn genByteSwap( src_mcv: MCValue, mem_ok: bool, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; if (src_ty.zigTypeTag(mod) == .Vector) return self.fail( @@ -5962,7 +5980,7 @@ fn genByteSwap( } fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const src_ty = self.typeOf(ty_op.operand); @@ -5984,7 +6002,7 @@ fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { } fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const src_ty = self.typeOf(ty_op.operand); @@ -6106,7 +6124,7 @@ fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { } fn floatSign(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, ty: Type) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; const result = result: { @@ -6282,7 +6300,7 @@ fn airRound(self: *Self, inst: Air.Inst.Index, mode: RoundMode) !void { } fn getRoundTag(self: *Self, ty: Type) ?Mir.Inst.FixedTag { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; return if (self.hasFeature(.sse4_1)) switch (ty.zigTypeTag(mod)) { .Float => switch (ty.floatBits(self.target.*)) { 32 => if (self.hasFeature(.avx)) .{ .v_ss, .round } else .{ ._ss, .round }, @@ -6314,7 +6332,7 @@ fn getRoundTag(self: *Self, ty: Type) ?Mir.Inst.FixedTag { } fn genRoundLibcall(self: *Self, ty: Type, src_mcv: MCValue, mode: RoundMode) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; if (self.getRoundTag(ty)) |_| return .none; if (ty.zigTypeTag(mod) != .Float) @@ -6338,7 +6356,7 @@ fn genRoundLibcall(self: *Self, ty: Type, src_mcv: MCValue, mode: RoundMode) !MC } fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: RoundMode) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const mir_tag = self.getRoundTag(ty) orelse { const result = try self.genRoundLibcall(ty, src_mcv, mode); return self.genSetReg(dst_reg, ty, result); @@ -6380,7 +6398,7 @@ fn genRound(self: *Self, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: Ro } fn airAbs(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const ty = self.typeOf(ty_op.operand); @@ -6520,7 +6538,7 @@ fn airAbs(self: *Self, inst: Air.Inst.Index) !void { } fn airSqrt(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const ty = self.typeOf(un_op); const abi_size: u32 = @intCast(ty.abiSize(mod)); @@ -6765,7 +6783,7 @@ fn reuseOperandAdvanced( } fn packedLoad(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ptr_info = ptr_ty.ptrInfo(mod); const val_ty = Type.fromInterned(ptr_info.child); @@ -6864,7 +6882,7 @@ fn packedLoad(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) Inn } fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const dst_ty = ptr_ty.childType(mod); if (!dst_ty.hasRuntimeBitsIgnoreComptime(mod)) return; switch (ptr_mcv) { @@ -6905,7 +6923,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerErro } fn airLoad(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const elem_ty = self.typeOfIndex(inst); const result: MCValue = result: { @@ -6962,7 +6980,7 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { } fn packedStore(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ptr_info = ptr_ty.ptrInfo(mod); const src_ty = Type.fromInterned(ptr_info.child); if (!src_ty.hasRuntimeBitsIgnoreComptime(mod)) return; @@ -7059,7 +7077,7 @@ fn packedStore(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) In } fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const src_ty = ptr_ty.childType(mod); if (!src_ty.hasRuntimeBitsIgnoreComptime(mod)) return; switch (ptr_mcv) { @@ -7100,7 +7118,7 @@ fn store(self: *Self, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerErr } fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; result: { @@ -7138,7 +7156,7 @@ fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { } fn fieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ptr_field_ty = self.typeOfIndex(inst); const ptr_container_ty = self.typeOf(operand); const ptr_container_ty_info = ptr_container_ty.ptrInfo(mod); @@ -7163,7 +7181,7 @@ fn fieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32 } fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; const result: MCValue = result: { @@ -7451,7 +7469,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { } fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; @@ -7470,7 +7488,7 @@ fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { } fn genUnOp(self: *Self, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air: Air.Inst.Ref) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const src_ty = self.typeOf(src_air); if (src_ty.zigTypeTag(mod) == .Vector) return self.fail("TODO implement genUnOp for {}", .{src_ty.fmt(mod)}); @@ -7558,7 +7576,7 @@ fn genUnOp(self: *Self, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air: } fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MCValue) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size: u32 = @intCast(dst_ty.abiSize(mod)); if (abi_size > 8) return self.fail("TODO implement {} for {}", .{ mir_tag, dst_ty.fmt(mod) }); switch (dst_mcv) { @@ -7605,7 +7623,7 @@ fn genShiftBinOpMir( lhs_mcv: MCValue, shift_mcv: MCValue, ) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const rhs_mcv: MCValue = rhs: { switch (shift_mcv) { .immediate => |imm| switch (imm) { @@ -7975,7 +7993,7 @@ fn genShiftBinOp( lhs_ty: Type, rhs_ty: Type, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; if (lhs_ty.zigTypeTag(mod) == .Vector) return self.fail("TODO implement genShiftBinOp for {}", .{ lhs_ty.fmt(mod), }); @@ -8041,7 +8059,7 @@ fn genMulDivBinOp( lhs_mcv: MCValue, rhs_mcv: MCValue, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; if (dst_ty.zigTypeTag(mod) == .Vector or dst_ty.zigTypeTag(mod) == .Float) return self.fail( "TODO implement genMulDivBinOp for {s} from {} to {}", .{ @tagName(tag), src_ty.fmt(mod), dst_ty.fmt(mod) }, @@ -8283,7 +8301,7 @@ fn genBinOp( lhs_air: Air.Inst.Ref, rhs_air: Air.Inst.Ref, ) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const lhs_ty = self.typeOf(lhs_air); const rhs_ty = self.typeOf(rhs_air); const abi_size: u32 = @intCast(lhs_ty.abiSize(mod)); @@ -10015,7 +10033,7 @@ fn genBinOpMir( dst_mcv: MCValue, src_mcv: MCValue, ) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size: u32 = @intCast(ty.abiSize(mod)); try self.spillEflagsIfOccupied(); switch (dst_mcv) { @@ -10435,7 +10453,7 @@ fn genBinOpMir( /// Performs multi-operand integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv. /// Does not support byte-size operands. fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size: u32 = @intCast(dst_ty.abiSize(mod)); try self.spillEflagsIfOccupied(); switch (dst_mcv) { @@ -10560,7 +10578,7 @@ fn genIntMulComplexOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: M } fn airArg(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; // skip zero-bit arguments as they don't have a corresponding arg instruction var arg_index = self.arg_index; while (self.args[arg_index] == .none) arg_index += 1; @@ -10593,7 +10611,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { } fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (self.debug_output) { .dwarf => |dw| { const loc: link.File.Dwarf.DeclState.DbgInfoLoc = switch (mcv) { @@ -10629,7 +10647,7 @@ fn genVarDbgInfo( mcv: MCValue, name: [:0]const u8, ) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const is_ptr = switch (tag) { .dbg_var_ptr => true, .dbg_var_val => false, @@ -10748,7 +10766,7 @@ fn genCall(self: *Self, info: union(enum) { callee: []const u8, }, }, arg_types: []const Type, args: []const MCValue) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const fn_ty = switch (info) { .air => |callee| fn_info: { @@ -10901,7 +10919,7 @@ fn genCall(self: *Self, info: union(enum) { if (self.bin_file.cast(link.File.Elf)) |elf_file| { const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl); const sym = elf_file.symbol(sym_index); - if (self.bin_file.options.pic) { + if (self.mod.pic) { const callee_reg: Register = switch (resolved_cc) { .SysV => callee: { if (!fn_info.is_var_args) break :callee .rax; @@ -10969,7 +10987,7 @@ fn genCall(self: *Self, info: union(enum) { } fn airRet(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const ret_ty = self.fn_type.fnReturnType(mod); @@ -11018,7 +11036,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { } fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ty = self.typeOf(bin_op.lhs); @@ -11412,7 +11430,7 @@ fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { } fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); @@ -11551,7 +11569,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { } fn genCondBrMir(self: *Self, ty: Type, mcv: MCValue) !Mir.Inst.Index { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size = ty.abiSize(mod); switch (mcv) { .eflags => |cc| { @@ -11624,7 +11642,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { } fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (opt_mcv) { .register_overflow => |ro| return .{ .eflags = ro.eflags.negate() }, else => {}, @@ -11732,7 +11750,7 @@ fn isNull(self: *Self, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MC } fn isNullPtr(self: *Self, inst: Air.Inst.Index, ptr_ty: Type, ptr_mcv: MCValue) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const opt_ty = ptr_ty.childType(mod); const pl_ty = opt_ty.optionalChild(mod); @@ -11768,7 +11786,7 @@ fn isNullPtr(self: *Self, inst: Air.Inst.Index, ptr_ty: Type, ptr_mcv: MCValue) } fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, eu_ty: Type, eu_mcv: MCValue) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const err_ty = eu_ty.errorUnionSet(mod); if (err_ty.errorSetIsEmpty(mod)) return MCValue{ .immediate = 0 }; // always false @@ -11815,7 +11833,7 @@ fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, eu_ty: Type, eu_mcv: MCValue) } fn isErrPtr(self: *Self, maybe_inst: ?Air.Inst.Index, ptr_ty: Type, ptr_mcv: MCValue) !MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const eu_ty = ptr_ty.childType(mod); const err_ty = eu_ty.errorUnionSet(mod); if (err_ty.errorSetIsEmpty(mod)) return MCValue{ .immediate = 0 }; // always false @@ -12097,7 +12115,7 @@ fn performReloc(self: *Self, reloc: Mir.Inst.Index) !void { } fn airBr(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const br = self.air.instructions.items(.data)[@intFromEnum(inst)].br; const block_ty = self.typeOfIndex(br.block_inst); @@ -12158,7 +12176,7 @@ fn airBr(self: *Self, inst: Air.Inst.Index) !void { } fn airAsm(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Asm, ty_pl.payload); const clobbers_len: u31 = @truncate(extra.data.flags); @@ -12865,7 +12883,7 @@ const MoveStrategy = union(enum) { } }; fn moveStrategy(self: *Self, ty: Type, class: Register.Class, aligned: bool) !MoveStrategy { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; switch (class) { .general_purpose, .segment => return .{ .move = .{ ._, .mov } }, .x87 => return .x87_load_store, @@ -13164,7 +13182,7 @@ fn moveStrategy(self: *Self, ty: Type, class: Register.Class, aligned: bool) !Mo } fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const src_lock = if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null; defer if (src_lock) |lock| self.register_manager.unlockReg(lock); @@ -13262,7 +13280,7 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError } fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size: u32 = @intCast(ty.abiSize(mod)); if (ty.bitSize(mod) > dst_reg.bitSize()) return self.fail("genSetReg called with a value larger than dst_reg", .{}); @@ -13561,7 +13579,7 @@ fn genSetReg(self: *Self, dst_reg: Register, ty: Type, src_mcv: MCValue) InnerEr } fn genSetMem(self: *Self, base: Memory.Base, disp: i32, ty: Type, src_mcv: MCValue) InnerError!void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size: u32 = @intCast(ty.abiSize(mod)); const dst_ptr_mcv: MCValue = switch (base) { .none => .{ .immediate = @bitCast(@as(i64, disp)) }, @@ -13814,7 +13832,7 @@ fn genLazySymbolRef( const sym_index = elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, lazy_sym) catch |err| return self.fail("{s} creating lazy symbol", .{@errorName(err)}); const sym = elf_file.symbol(sym_index); - if (self.bin_file.options.pic) { + if (self.mod.pic) { switch (tag) { .lea, .call => try self.genSetReg(reg, Type.usize, .{ .load_symbol = .{ .sym = sym.esym_index }, @@ -13922,7 +13940,7 @@ fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) !void { } fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const dst_ty = self.typeOfIndex(inst); const src_ty = self.typeOf(ty_op.operand); @@ -13980,7 +13998,7 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { } fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const slice_ty = self.typeOfIndex(inst); @@ -14003,7 +14021,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { } fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const dst_ty = self.typeOfIndex(inst); @@ -14082,7 +14100,7 @@ fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void { } fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const dst_ty = self.typeOfIndex(inst); @@ -14153,7 +14171,7 @@ fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void { } fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Cmpxchg, ty_pl.payload).data; @@ -14249,7 +14267,7 @@ fn atomicOp( rmw_op: ?std.builtin.AtomicRmwOp, order: std.builtin.AtomicOrder, ) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ptr_lock = switch (ptr_mcv) { .register => |reg| self.register_manager.lockReg(reg), else => null, @@ -14653,7 +14671,7 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr } fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; result: { @@ -14781,7 +14799,7 @@ fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void { } fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; try self.spillRegisters(&.{ .rdi, .rsi, .rcx }); @@ -14836,7 +14854,7 @@ fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { } fn airTagName(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const inst_ty = self.typeOfIndex(inst); const enum_ty = self.typeOf(un_op); @@ -14878,7 +14896,7 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void { } fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const err_ty = self.typeOf(un_op); @@ -14980,7 +14998,7 @@ fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { } fn airSplat(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const vector_ty = self.typeOfIndex(inst); const vector_len = vector_ty.vectorLen(mod); @@ -15332,7 +15350,7 @@ fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { } fn airReduce(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce; const result: MCValue = result: { @@ -15389,7 +15407,7 @@ fn airReduce(self: *Self, inst: Air.Inst.Index) !void { } fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const result_ty = self.typeOfIndex(inst); const len: usize = @intCast(result_ty.arrayLen(mod)); const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; @@ -15562,7 +15580,7 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { } fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ip = &mod.intern_pool; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; @@ -15613,7 +15631,7 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void { } fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const extra = self.air.extraData(Air.Bin, pl_op.payload).data; const ty = self.typeOfIndex(inst); @@ -15780,7 +15798,7 @@ fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { } fn airVaStart(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const va_list_ty = self.air.instructions.items(.data)[@intFromEnum(inst)].ty; const ptr_anyopaque_ty = try mod.singleMutPtrType(Type.anyopaque); @@ -15833,7 +15851,7 @@ fn airVaStart(self: *Self, inst: Air.Inst.Index) !void { } fn airVaArg(self: *Self, inst: Air.Inst.Index) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const ty = self.typeOfIndex(inst); const promote_ty = self.promoteVarArg(ty); @@ -16042,7 +16060,7 @@ fn airVaEnd(self: *Self, inst: Air.Inst.Index) !void { } fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ty = self.typeOf(ref); // If the type has no codegen bits, no need to store it. @@ -16057,7 +16075,7 @@ fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue { const const_mcv = try self.genTypedValue(.{ .ty = ty, .val = Value.fromInterned(ip_index) }); switch (const_mcv) { .lea_tlv => |tlv_sym| if (self.bin_file.cast(link.File.Elf)) |_| { - if (self.bin_file.options.pic) { + if (self.mod.pic) { try self.spillRegisters(&.{ .rdi, .rax }); } else { try self.spillRegisters(&.{.rax}); @@ -16116,7 +16134,7 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV } fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; return switch (try codegen.genTypedValue(self.bin_file, self.src_loc, arg_tv, self.owner.getDecl(mod))) { .mcv => |mcv| switch (mcv) { .none => .none, @@ -16156,7 +16174,7 @@ fn resolveCallingConventionValues( var_args: []const Type, stack_frame_base: FrameIndex, ) !CallMCValues { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const ip = &mod.intern_pool; const cc = fn_info.cc; const param_types = try self.gpa.alloc(Type, fn_info.param_types.len + var_args.len); @@ -16413,14 +16431,16 @@ fn resolveCallingConventionValues( fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(self.err_msg == null); - self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); + const gpa = self.gpa; + self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args); return error.CodegenFail; } fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError { @setCold(true); assert(self.err_msg == null); - self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); + const gpa = self.gpa; + self.err_msg = try ErrorMsg.create(gpa, self.src_loc, format, args); return error.CodegenFail; } @@ -16468,7 +16488,7 @@ fn registerAlias(reg: Register, size_bytes: u32) Register { } fn memSize(self: *Self, ty: Type) Memory.Size { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; return switch (ty.zigTypeTag(mod)) { .Float => Memory.Size.fromBitSize(ty.floatBits(self.target.*)), else => Memory.Size.fromSize(@intCast(ty.abiSize(mod))), @@ -16476,7 +16496,7 @@ fn memSize(self: *Self, ty: Type) Memory.Size { } fn splitType(self: *Self, ty: Type) ![2]Type { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const classes = mem.sliceTo(&abi.classifySystemV(ty, mod, .other), .none); var parts: [2]Type = undefined; if (classes.len == 2) for (&parts, classes, 0..) |*part, class, part_i| { @@ -16505,7 +16525,7 @@ fn splitType(self: *Self, ty: Type) ![2]Type { /// Truncates the value in the register in place. /// Clobbers any remaining bits. fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const int_info = if (ty.isAbiInt(mod)) ty.intInfo(mod) else std.builtin.Type.Int{ .signedness = .unsigned, .bits = @intCast(ty.bitSize(mod)), @@ -16550,7 +16570,7 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { } fn regBitSize(self: *Self, ty: Type) u64 { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const abi_size = ty.abiSize(mod); return switch (ty.zigTypeTag(mod)) { else => switch (abi_size) { @@ -16569,7 +16589,7 @@ fn regBitSize(self: *Self, ty: Type) u64 { } fn regExtraBits(self: *Self, ty: Type) u64 { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; return self.regBitSize(ty) - ty.bitSize(mod); } @@ -16584,12 +16604,12 @@ fn hasAllFeatures(self: *Self, features: anytype) bool { } fn typeOf(self: *Self, inst: Air.Inst.Ref) Type { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; return self.air.typeOf(inst, &mod.intern_pool); } fn typeOfIndex(self: *Self, inst: Air.Inst.Index) Type { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; return self.air.typeOfIndex(inst, &mod.intern_pool); } @@ -16641,7 +16661,7 @@ fn floatLibcAbiSuffix(ty: Type) []const u8 { } fn promoteInt(self: *Self, ty: Type) Type { - const mod = self.bin_file.options.module.?; + const mod = self.bin_file.comp.module.?; const int_info: InternPool.Key.IntType = switch (ty.toIntern()) { .bool_type => .{ .signedness = .unsigned, .bits = 1 }, else => if (ty.isAbiInt(mod)) ty.intInfo(mod) else return ty, diff --git a/src/arch/x86_64/Emit.zig b/src/arch/x86_64/Emit.zig index b3a5316104fb..e8668e82f4a7 100644 --- a/src/arch/x86_64/Emit.zig +++ b/src/arch/x86_64/Emit.zig @@ -103,10 +103,10 @@ pub fn emitMir(emit: *Emit) Error!void { }); }, .linker_reloc => |data| if (emit.lower.bin_file.cast(link.File.Elf)) |elf_file| { - const is_obj_or_static_lib = switch (emit.lower.bin_file.options.output_mode) { + const is_obj_or_static_lib = switch (emit.lower.output_mode) { .Exe => false, .Obj => true, - .Lib => emit.lower.bin_file.options.link_mode == .Static, + .Lib => emit.lower.link_mode == .Static, }; const atom = elf_file.symbol(data.atom_index).atom(elf_file).?; const sym_index = elf_file.zigObjectPtr().?.symbol(data.sym_index); @@ -114,7 +114,7 @@ pub fn emitMir(emit: *Emit) Error!void { if (sym.flags.needs_zig_got and !is_obj_or_static_lib) { _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); } - if (emit.lower.bin_file.options.pic) { + if (emit.lower.pic) { const r_type: u32 = if (sym.flags.needs_zig_got and !is_obj_or_static_lib) link.File.Elf.R_X86_64_ZIG_GOTPCREL else if (sym.flags.needs_got) diff --git a/src/arch/x86_64/Lower.zig b/src/arch/x86_64/Lower.zig index 0c309991f6b0..cc5ae7712bb1 100644 --- a/src/arch/x86_64/Lower.zig +++ b/src/arch/x86_64/Lower.zig @@ -1,6 +1,9 @@ //! This file contains the functionality for lowering x86_64 MIR to Instructions bin_file: *link.File, +output_mode: std.builtin.OutputMode, +link_mode: std.builtin.LinkMode, +pic: bool, allocator: Allocator, mir: Mir, cc: std.builtin.CallingConvention, @@ -336,10 +339,10 @@ fn isTls(sym: bits.Symbol, ctx: *link.File) bool { } fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) Error!void { - const is_obj_or_static_lib = switch (lower.bin_file.options.output_mode) { + const is_obj_or_static_lib = switch (lower.output_mode) { .Exe => false, .Obj => true, - .Lib => lower.bin_file.options.link_mode == .Static, + .Lib => lower.link_mode == .Static, }; const emit_prefix = prefix; @@ -358,7 +361,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) if (isTls(sym, lower.bin_file)) { // TODO handle extern TLS vars, i.e., emit GD model - if (lower.bin_file.options.pic) { + if (lower.pic) { // Here, we currently assume local dynamic TLS vars, and so // we emit LD model. _ = lower.reloc(.{ .linker_tlsld = sym }); @@ -403,7 +406,7 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand) } _ = lower.reloc(.{ .linker_reloc = sym }); - break :op if (lower.bin_file.options.pic) switch (mnemonic) { + break :op if (lower.pic) switch (mnemonic) { .lea => { break :op .{ .mem = Memory.rip(mem_op.sib.ptr_size, 0) }; }, diff --git a/src/codegen.zig b/src/codegen.zig index b464a9f36535..1ac8626a7959 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -45,7 +45,7 @@ pub const DebugInfoOutput = union(enum) { }; pub fn generateFunction( - bin_file: *link.File, + lf: *link.File, src_loc: Module.SrcLoc, func_index: InternPool.Index, air: Air, @@ -53,33 +53,43 @@ pub fn generateFunction( code: *std.ArrayList(u8), debug_output: DebugInfoOutput, ) CodeGenError!Result { - switch (bin_file.options.target.cpu.arch) { + const zcu = lf.comp.module.?; + const func = zcu.funcInfo(func_index); + const decl = zcu.declPtr(func.owner_decl); + const namespace = zcu.namespacePtr(decl.src_namespace); + const target = namespace.file_scope.mod.resolved_target.result; + switch (target.cpu.arch) { .arm, .armeb, - => return @import("arch/arm/CodeGen.zig").generate(bin_file, src_loc, func_index, air, liveness, code, debug_output), + => return @import("arch/arm/CodeGen.zig").generate(lf, src_loc, func_index, air, liveness, code, debug_output), .aarch64, .aarch64_be, .aarch64_32, - => return @import("arch/aarch64/CodeGen.zig").generate(bin_file, src_loc, func_index, air, liveness, code, debug_output), - .riscv64 => return @import("arch/riscv64/CodeGen.zig").generate(bin_file, src_loc, func_index, air, liveness, code, debug_output), - .sparc64 => return @import("arch/sparc64/CodeGen.zig").generate(bin_file, src_loc, func_index, air, liveness, code, debug_output), - .x86_64 => return @import("arch/x86_64/CodeGen.zig").generate(bin_file, src_loc, func_index, air, liveness, code, debug_output), + => return @import("arch/aarch64/CodeGen.zig").generate(lf, src_loc, func_index, air, liveness, code, debug_output), + .riscv64 => return @import("arch/riscv64/CodeGen.zig").generate(lf, src_loc, func_index, air, liveness, code, debug_output), + .sparc64 => return @import("arch/sparc64/CodeGen.zig").generate(lf, src_loc, func_index, air, liveness, code, debug_output), + .x86_64 => return @import("arch/x86_64/CodeGen.zig").generate(lf, src_loc, func_index, air, liveness, code, debug_output), .wasm32, .wasm64, - => return @import("arch/wasm/CodeGen.zig").generate(bin_file, src_loc, func_index, air, liveness, code, debug_output), + => return @import("arch/wasm/CodeGen.zig").generate(lf, src_loc, func_index, air, liveness, code, debug_output), else => unreachable, } } pub fn generateLazyFunction( - bin_file: *link.File, + lf: *link.File, src_loc: Module.SrcLoc, lazy_sym: link.File.LazySymbol, code: *std.ArrayList(u8), debug_output: DebugInfoOutput, ) CodeGenError!Result { - switch (bin_file.options.target.cpu.arch) { - .x86_64 => return @import("arch/x86_64/CodeGen.zig").generateLazy(bin_file, src_loc, lazy_sym, code, debug_output), + const zcu = lf.comp.module.?; + const decl_index = lazy_sym.ty.getOwnerDecl(zcu); + const decl = zcu.declPtr(decl_index); + const namespace = zcu.namespacePtr(decl.src_namespace); + const target = namespace.file_scope.mod.resolved_target.result; + switch (target.cpu.arch) { + .x86_64 => return @import("arch/x86_64/CodeGen.zig").generateLazy(lf, src_loc, lazy_sym, code, debug_output), else => unreachable, } } @@ -107,13 +117,15 @@ pub fn generateLazySymbol( const tracy = trace(@src()); defer tracy.end(); - const target = bin_file.options.target; + const comp = bin_file.comp; + const zcu = comp.module.?; + const target = comp.root_mod.resolved_target.result; const endian = target.cpu.arch.endian(); + const gpa = comp.gpa; - const mod = bin_file.options.module.?; log.debug("generateLazySymbol: kind = {s}, ty = {}", .{ @tagName(lazy_sym.kind), - lazy_sym.ty.fmt(mod), + lazy_sym.ty.fmt(zcu), }); if (lazy_sym.kind == .code) { @@ -121,14 +133,14 @@ pub fn generateLazySymbol( return generateLazyFunction(bin_file, src_loc, lazy_sym, code, debug_output); } - if (lazy_sym.ty.isAnyError(mod)) { + if (lazy_sym.ty.isAnyError(zcu)) { alignment.* = .@"4"; - const err_names = mod.global_error_set.keys(); + const err_names = zcu.global_error_set.keys(); mem.writeInt(u32, try code.addManyAsArray(4), @as(u32, @intCast(err_names.len)), endian); var offset = code.items.len; try code.resize((1 + err_names.len + 1) * 4); for (err_names) |err_name_nts| { - const err_name = mod.intern_pool.stringToSlice(err_name_nts); + const err_name = zcu.intern_pool.stringToSlice(err_name_nts); mem.writeInt(u32, code.items[offset..][0..4], @as(u32, @intCast(code.items.len)), endian); offset += 4; try code.ensureUnusedCapacity(err_name.len + 1); @@ -137,20 +149,20 @@ pub fn generateLazySymbol( } mem.writeInt(u32, code.items[offset..][0..4], @as(u32, @intCast(code.items.len)), endian); return Result.ok; - } else if (lazy_sym.ty.zigTypeTag(mod) == .Enum) { + } else if (lazy_sym.ty.zigTypeTag(zcu) == .Enum) { alignment.* = .@"1"; - for (lazy_sym.ty.enumFields(mod)) |tag_name_ip| { - const tag_name = mod.intern_pool.stringToSlice(tag_name_ip); + for (lazy_sym.ty.enumFields(zcu)) |tag_name_ip| { + const tag_name = zcu.intern_pool.stringToSlice(tag_name_ip); try code.ensureUnusedCapacity(tag_name.len + 1); code.appendSliceAssumeCapacity(tag_name); code.appendAssumeCapacity(0); } return Result.ok; } else return .{ .fail = try ErrorMsg.create( - bin_file.allocator, + gpa, src_loc, "TODO implement generateLazySymbol for {s} {}", - .{ @tagName(lazy_sym.kind), lazy_sym.ty.fmt(mod) }, + .{ @tagName(lazy_sym.kind), lazy_sym.ty.fmt(zcu) }, ) }; } @@ -165,7 +177,7 @@ pub fn generateSymbol( const tracy = trace(@src()); defer tracy.end(); - const mod = bin_file.options.module.?; + const mod = bin_file.comp.module.?; const ip = &mod.intern_pool; const typed_value = arg_tv; @@ -662,7 +674,7 @@ fn lowerParentPtr( debug_output: DebugInfoOutput, reloc_info: RelocInfo, ) CodeGenError!Result { - const mod = bin_file.options.module.?; + const mod = bin_file.comp.module.?; const ptr = mod.intern_pool.indexToKey(parent_ptr).ptr; assert(ptr.len == .none); return switch (ptr.addr) { @@ -757,7 +769,7 @@ const RelocInfo = struct { }; fn lowerAnonDeclRef( - bin_file: *link.File, + lf: *link.File, src_loc: Module.SrcLoc, anon_decl: InternPool.Key.Ptr.Addr.AnonDecl, code: *std.ArrayList(u8), @@ -765,27 +777,27 @@ fn lowerAnonDeclRef( reloc_info: RelocInfo, ) CodeGenError!Result { _ = debug_output; - const target = bin_file.options.target; - const mod = bin_file.options.module.?; + const zcu = lf.comp.module.?; + const target = lf.comp.root_mod.resolved_target.result; const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); const decl_val = anon_decl.val; - const decl_ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val)); - log.debug("lowerAnonDecl: ty = {}", .{decl_ty.fmt(mod)}); - const is_fn_body = decl_ty.zigTypeTag(mod) == .Fn; - if (!is_fn_body and !decl_ty.hasRuntimeBits(mod)) { + const decl_ty = Type.fromInterned(zcu.intern_pool.typeOf(decl_val)); + log.debug("lowerAnonDecl: ty = {}", .{decl_ty.fmt(zcu)}); + const is_fn_body = decl_ty.zigTypeTag(zcu) == .Fn; + if (!is_fn_body and !decl_ty.hasRuntimeBits(zcu)) { try code.appendNTimes(0xaa, ptr_width_bytes); return Result.ok; } - const decl_align = mod.intern_pool.indexToKey(anon_decl.orig_ty).ptr_type.flags.alignment; - const res = try bin_file.lowerAnonDecl(decl_val, decl_align, src_loc); + const decl_align = zcu.intern_pool.indexToKey(anon_decl.orig_ty).ptr_type.flags.alignment; + const res = try lf.lowerAnonDecl(decl_val, decl_align, src_loc); switch (res) { .ok => {}, .fail => |em| return .{ .fail = em }, } - const vaddr = try bin_file.getAnonDeclVAddr(decl_val, .{ + const vaddr = try lf.getAnonDeclVAddr(decl_val, .{ .parent_atom_index = reloc_info.parent_atom_index, .offset = code.items.len, .addend = reloc_info.addend orelse 0, @@ -802,7 +814,7 @@ fn lowerAnonDeclRef( } fn lowerDeclRef( - bin_file: *link.File, + lf: *link.File, src_loc: Module.SrcLoc, decl_index: InternPool.DeclIndex, code: *std.ArrayList(u8), @@ -811,20 +823,21 @@ fn lowerDeclRef( ) CodeGenError!Result { _ = src_loc; _ = debug_output; - const target = bin_file.options.target; - const mod = bin_file.options.module.?; + const zcu = lf.comp.module.?; + const decl = zcu.declPtr(decl_index); + const namespace = zcu.namespacePtr(decl.src_namespace); + const target = namespace.file_scope.mod.resolved_target.result; const ptr_width = target.ptrBitWidth(); - const decl = mod.declPtr(decl_index); - const is_fn_body = decl.ty.zigTypeTag(mod) == .Fn; - if (!is_fn_body and !decl.ty.hasRuntimeBits(mod)) { + const is_fn_body = decl.ty.zigTypeTag(zcu) == .Fn; + if (!is_fn_body and !decl.ty.hasRuntimeBits(zcu)) { try code.appendNTimes(0xaa, @divExact(ptr_width, 8)); return Result.ok; } - try mod.markDeclAlive(decl); + try zcu.markDeclAlive(decl); - const vaddr = try bin_file.getDeclVAddr(decl_index, .{ + const vaddr = try lf.getDeclVAddr(decl_index, .{ .parent_atom_index = reloc_info.parent_atom_index, .offset = code.items.len, .addend = reloc_info.addend orelse 0, @@ -897,27 +910,29 @@ pub const GenResult = union(enum) { }; fn genDeclRef( - bin_file: *link.File, + lf: *link.File, src_loc: Module.SrcLoc, tv: TypedValue, ptr_decl_index: InternPool.DeclIndex, ) CodeGenError!GenResult { - const mod = bin_file.options.module.?; - log.debug("genDeclRef: ty = {}, val = {}", .{ tv.ty.fmt(mod), tv.val.fmtValue(tv.ty, mod) }); + const zcu = lf.comp.module.?; + log.debug("genDeclRef: ty = {}, val = {}", .{ tv.ty.fmt(zcu), tv.val.fmtValue(tv.ty, zcu) }); + + const ptr_decl = zcu.declPtr(ptr_decl_index); + const namespace = zcu.namespacePtr(ptr_decl.src_namespace); + const target = namespace.file_scope.mod.resolved_target.result; - const target = bin_file.options.target; const ptr_bits = target.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); - const ptr_decl = mod.declPtr(ptr_decl_index); - const decl_index = switch (mod.intern_pool.indexToKey(try ptr_decl.internValue(mod))) { + const decl_index = switch (zcu.intern_pool.indexToKey(try ptr_decl.internValue(zcu))) { .func => |func| func.owner_decl, .extern_func => |extern_func| extern_func.decl, else => ptr_decl_index, }; - const decl = mod.declPtr(decl_index); + const decl = zcu.declPtr(decl_index); - if (!decl.ty.isFnOrHasRuntimeBitsIgnoreComptime(mod)) { + if (!decl.ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) { const imm: u64 = switch (ptr_bytes) { 1 => 0xaa, 2 => 0xaaaa, @@ -928,29 +943,34 @@ fn genDeclRef( return GenResult.mcv(.{ .immediate = imm }); } + const comp = lf.comp; + const gpa = comp.gpa; + // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`? - if (tv.ty.castPtrToFn(mod)) |fn_ty| { - if (mod.typeToFunc(fn_ty).?.is_generic) { - return GenResult.mcv(.{ .immediate = fn_ty.abiAlignment(mod).toByteUnitsOptional().? }); + if (tv.ty.castPtrToFn(zcu)) |fn_ty| { + if (zcu.typeToFunc(fn_ty).?.is_generic) { + return GenResult.mcv(.{ .immediate = fn_ty.abiAlignment(zcu).toByteUnitsOptional().? }); } - } else if (tv.ty.zigTypeTag(mod) == .Pointer) { - const elem_ty = tv.ty.elemType2(mod); - if (!elem_ty.hasRuntimeBits(mod)) { - return GenResult.mcv(.{ .immediate = elem_ty.abiAlignment(mod).toByteUnitsOptional().? }); + } else if (tv.ty.zigTypeTag(zcu) == .Pointer) { + const elem_ty = tv.ty.elemType2(zcu); + if (!elem_ty.hasRuntimeBits(zcu)) { + return GenResult.mcv(.{ .immediate = elem_ty.abiAlignment(zcu).toByteUnitsOptional().? }); } } - try mod.markDeclAlive(decl); + try zcu.markDeclAlive(decl); - const is_threadlocal = tv.val.isPtrToThreadLocal(mod) and !bin_file.options.single_threaded; - const is_extern = decl.isExtern(mod); + const decl_namespace = zcu.namespacePtr(decl.src_namespace); + const single_threaded = decl_namespace.file_scope.mod.single_threaded; + const is_threadlocal = tv.val.isPtrToThreadLocal(zcu) and !single_threaded; + const is_extern = decl.isExtern(zcu); - if (bin_file.cast(link.File.Elf)) |elf_file| { + if (lf.cast(link.File.Elf)) |elf_file| { if (is_extern) { - const name = mod.intern_pool.stringToSlice(decl.name); + const name = zcu.intern_pool.stringToSlice(decl.name); // TODO audit this - const lib_name = if (decl.getOwnedVariable(mod)) |ov| - mod.intern_pool.stringToSliceUnwrap(ov.lib_name) + const lib_name = if (decl.getOwnedVariable(zcu)) |ov| + zcu.intern_pool.stringToSliceUnwrap(ov.lib_name) else null; const sym_index = try elf_file.getGlobalSymbol(name, lib_name); @@ -963,12 +983,12 @@ fn genDeclRef( return GenResult.mcv(.{ .load_tlv = sym.esym_index }); } return GenResult.mcv(.{ .load_symbol = sym.esym_index }); - } else if (bin_file.cast(link.File.MachO)) |macho_file| { + } else if (lf.cast(link.File.MachO)) |macho_file| { if (is_extern) { // TODO make this part of getGlobalSymbol - const name = mod.intern_pool.stringToSlice(decl.name); - const sym_name = try std.fmt.allocPrint(bin_file.allocator, "_{s}", .{name}); - defer bin_file.allocator.free(sym_name); + const name = zcu.intern_pool.stringToSlice(decl.name); + const sym_name = try std.fmt.allocPrint(gpa, "_{s}", .{name}); + defer gpa.free(sym_name); const global_index = try macho_file.addUndefined(sym_name, .{ .add_got = true }); return GenResult.mcv(.{ .load_got = link.File.MachO.global_symbol_bit | global_index }); } @@ -978,110 +998,118 @@ fn genDeclRef( return GenResult.mcv(.{ .load_tlv = sym_index }); } return GenResult.mcv(.{ .load_got = sym_index }); - } else if (bin_file.cast(link.File.Coff)) |coff_file| { + } else if (lf.cast(link.File.Coff)) |coff_file| { if (is_extern) { - const name = mod.intern_pool.stringToSlice(decl.name); + const name = zcu.intern_pool.stringToSlice(decl.name); // TODO audit this - const lib_name = if (decl.getOwnedVariable(mod)) |ov| - mod.intern_pool.stringToSliceUnwrap(ov.lib_name) + const lib_name = if (decl.getOwnedVariable(zcu)) |ov| + zcu.intern_pool.stringToSliceUnwrap(ov.lib_name) else null; const global_index = try coff_file.getGlobalSymbol(name, lib_name); - try coff_file.need_got_table.put(bin_file.allocator, global_index, {}); // needs GOT + try coff_file.need_got_table.put(gpa, global_index, {}); // needs GOT return GenResult.mcv(.{ .load_got = link.File.Coff.global_symbol_bit | global_index }); } const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index); const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; return GenResult.mcv(.{ .load_got = sym_index }); - } else if (bin_file.cast(link.File.Plan9)) |p9| { + } else if (lf.cast(link.File.Plan9)) |p9| { const atom_index = try p9.seeDecl(decl_index); const atom = p9.getAtom(atom_index); return GenResult.mcv(.{ .memory = atom.getOffsetTableAddress(p9) }); } else { - return GenResult.fail(bin_file.allocator, src_loc, "TODO genDeclRef for target {}", .{target}); + return GenResult.fail(gpa, src_loc, "TODO genDeclRef for target {}", .{target}); } } fn genUnnamedConst( - bin_file: *link.File, + lf: *link.File, src_loc: Module.SrcLoc, tv: TypedValue, owner_decl_index: InternPool.DeclIndex, ) CodeGenError!GenResult { - const mod = bin_file.options.module.?; - log.debug("genUnnamedConst: ty = {}, val = {}", .{ tv.ty.fmt(mod), tv.val.fmtValue(tv.ty, mod) }); + const zcu = lf.comp.module.?; + const gpa = lf.comp.gpa; + log.debug("genUnnamedConst: ty = {}, val = {}", .{ tv.ty.fmt(zcu), tv.val.fmtValue(tv.ty, zcu) }); - const target = bin_file.options.target; - const local_sym_index = bin_file.lowerUnnamedConst(tv, owner_decl_index) catch |err| { - return GenResult.fail(bin_file.allocator, src_loc, "lowering unnamed constant failed: {s}", .{@errorName(err)}); + const local_sym_index = lf.lowerUnnamedConst(tv, owner_decl_index) catch |err| { + return GenResult.fail(gpa, src_loc, "lowering unnamed constant failed: {s}", .{@errorName(err)}); }; - if (bin_file.cast(link.File.Elf)) |elf_file| { - const local = elf_file.symbol(local_sym_index); - return GenResult.mcv(.{ .load_symbol = local.esym_index }); - } else if (bin_file.cast(link.File.MachO)) |_| { - return GenResult.mcv(.{ .load_direct = local_sym_index }); - } else if (bin_file.cast(link.File.Coff)) |_| { - return GenResult.mcv(.{ .load_direct = local_sym_index }); - } else if (bin_file.cast(link.File.Plan9)) |_| { - const atom_index = local_sym_index; // plan9 returns the atom_index - return GenResult.mcv(.{ .load_direct = atom_index }); - } else { - return GenResult.fail(bin_file.allocator, src_loc, "TODO genUnnamedConst for target {}", .{target}); + switch (lf.tag) { + .elf => { + const elf_file = lf.cast(link.File.Elf).?; + const local = elf_file.symbol(local_sym_index); + return GenResult.mcv(.{ .load_symbol = local.esym_index }); + }, + .macho, .coff => { + return GenResult.mcv(.{ .load_direct = local_sym_index }); + }, + .plan9 => { + const atom_index = local_sym_index; // plan9 returns the atom_index + return GenResult.mcv(.{ .load_direct = atom_index }); + }, + + .c => return GenResult.fail(gpa, src_loc, "TODO genUnnamedConst for -ofmt=c", .{}), + .wasm => return GenResult.fail(gpa, src_loc, "TODO genUnnamedConst for wasm", .{}), + .spirv => return GenResult.fail(gpa, src_loc, "TODO genUnnamedConst for spirv", .{}), + .nvptx => return GenResult.fail(gpa, src_loc, "TODO genUnnamedConst for nvptx", .{}), } } pub fn genTypedValue( - bin_file: *link.File, + lf: *link.File, src_loc: Module.SrcLoc, arg_tv: TypedValue, owner_decl_index: InternPool.DeclIndex, ) CodeGenError!GenResult { - const mod = bin_file.options.module.?; + const zcu = lf.comp.module.?; const typed_value = arg_tv; log.debug("genTypedValue: ty = {}, val = {}", .{ - typed_value.ty.fmt(mod), - typed_value.val.fmtValue(typed_value.ty, mod), + typed_value.ty.fmt(zcu), + typed_value.val.fmtValue(typed_value.ty, zcu), }); - if (typed_value.val.isUndef(mod)) + if (typed_value.val.isUndef(zcu)) return GenResult.mcv(.undef); - const target = bin_file.options.target; + const owner_decl = zcu.declPtr(owner_decl_index); + const namespace = zcu.namespacePtr(owner_decl.src_namespace); + const target = namespace.file_scope.mod.resolved_target.result; const ptr_bits = target.ptrBitWidth(); - if (!typed_value.ty.isSlice(mod)) switch (mod.intern_pool.indexToKey(typed_value.val.toIntern())) { + if (!typed_value.ty.isSlice(zcu)) switch (zcu.intern_pool.indexToKey(typed_value.val.toIntern())) { .ptr => |ptr| switch (ptr.addr) { - .decl => |decl| return genDeclRef(bin_file, src_loc, typed_value, decl), - .mut_decl => |mut_decl| return genDeclRef(bin_file, src_loc, typed_value, mut_decl.decl), + .decl => |decl| return genDeclRef(lf, src_loc, typed_value, decl), + .mut_decl => |mut_decl| return genDeclRef(lf, src_loc, typed_value, mut_decl.decl), else => {}, }, else => {}, }; - switch (typed_value.ty.zigTypeTag(mod)) { + switch (typed_value.ty.zigTypeTag(zcu)) { .Void => return GenResult.mcv(.none), - .Pointer => switch (typed_value.ty.ptrSize(mod)) { + .Pointer => switch (typed_value.ty.ptrSize(zcu)) { .Slice => {}, else => switch (typed_value.val.toIntern()) { .null_value => { return GenResult.mcv(.{ .immediate = 0 }); }, .none => {}, - else => switch (mod.intern_pool.indexToKey(typed_value.val.toIntern())) { + else => switch (zcu.intern_pool.indexToKey(typed_value.val.toIntern())) { .int => { - return GenResult.mcv(.{ .immediate = typed_value.val.toUnsignedInt(mod) }); + return GenResult.mcv(.{ .immediate = typed_value.val.toUnsignedInt(zcu) }); }, else => {}, }, }, }, .Int => { - const info = typed_value.ty.intInfo(mod); + const info = typed_value.ty.intInfo(zcu); if (info.bits <= ptr_bits) { const unsigned = switch (info.signedness) { - .signed => @as(u64, @bitCast(typed_value.val.toSignedInt(mod))), - .unsigned => typed_value.val.toUnsignedInt(mod), + .signed => @as(u64, @bitCast(typed_value.val.toSignedInt(zcu))), + .unsigned => typed_value.val.toUnsignedInt(zcu), }; return GenResult.mcv(.{ .immediate = unsigned }); } @@ -1090,45 +1118,45 @@ pub fn genTypedValue( return GenResult.mcv(.{ .immediate = @intFromBool(typed_value.val.toBool()) }); }, .Optional => { - if (typed_value.ty.isPtrLikeOptional(mod)) { - return genTypedValue(bin_file, src_loc, .{ - .ty = typed_value.ty.optionalChild(mod), - .val = typed_value.val.optionalValue(mod) orelse return GenResult.mcv(.{ .immediate = 0 }), + if (typed_value.ty.isPtrLikeOptional(zcu)) { + return genTypedValue(lf, src_loc, .{ + .ty = typed_value.ty.optionalChild(zcu), + .val = typed_value.val.optionalValue(zcu) orelse return GenResult.mcv(.{ .immediate = 0 }), }, owner_decl_index); - } else if (typed_value.ty.abiSize(mod) == 1) { - return GenResult.mcv(.{ .immediate = @intFromBool(!typed_value.val.isNull(mod)) }); + } else if (typed_value.ty.abiSize(zcu) == 1) { + return GenResult.mcv(.{ .immediate = @intFromBool(!typed_value.val.isNull(zcu)) }); } }, .Enum => { - const enum_tag = mod.intern_pool.indexToKey(typed_value.val.toIntern()).enum_tag; - const int_tag_ty = mod.intern_pool.typeOf(enum_tag.int); - return genTypedValue(bin_file, src_loc, .{ + const enum_tag = zcu.intern_pool.indexToKey(typed_value.val.toIntern()).enum_tag; + const int_tag_ty = zcu.intern_pool.typeOf(enum_tag.int); + return genTypedValue(lf, src_loc, .{ .ty = Type.fromInterned(int_tag_ty), .val = Value.fromInterned(enum_tag.int), }, owner_decl_index); }, .ErrorSet => { - const err_name = mod.intern_pool.indexToKey(typed_value.val.toIntern()).err.name; - const error_index = mod.global_error_set.getIndex(err_name).?; + const err_name = zcu.intern_pool.indexToKey(typed_value.val.toIntern()).err.name; + const error_index = zcu.global_error_set.getIndex(err_name).?; return GenResult.mcv(.{ .immediate = error_index }); }, .ErrorUnion => { - const err_type = typed_value.ty.errorUnionSet(mod); - const payload_type = typed_value.ty.errorUnionPayload(mod); - if (!payload_type.hasRuntimeBitsIgnoreComptime(mod)) { + const err_type = typed_value.ty.errorUnionSet(zcu); + const payload_type = typed_value.ty.errorUnionPayload(zcu); + if (!payload_type.hasRuntimeBitsIgnoreComptime(zcu)) { // We use the error type directly as the type. - const err_int_ty = try mod.errorIntType(); - switch (mod.intern_pool.indexToKey(typed_value.val.toIntern()).error_union.val) { - .err_name => |err_name| return genTypedValue(bin_file, src_loc, .{ + const err_int_ty = try zcu.errorIntType(); + switch (zcu.intern_pool.indexToKey(typed_value.val.toIntern()).error_union.val) { + .err_name => |err_name| return genTypedValue(lf, src_loc, .{ .ty = err_type, - .val = Value.fromInterned((try mod.intern(.{ .err = .{ + .val = Value.fromInterned((try zcu.intern(.{ .err = .{ .ty = err_type.toIntern(), .name = err_name, } }))), }, owner_decl_index), - .payload => return genTypedValue(bin_file, src_loc, .{ + .payload => return genTypedValue(lf, src_loc, .{ .ty = err_int_ty, - .val = try mod.intValue(err_int_ty, 0), + .val = try zcu.intValue(err_int_ty, 0), }, owner_decl_index), } } @@ -1146,7 +1174,7 @@ pub fn genTypedValue( else => {}, } - return genUnnamedConst(bin_file, src_loc, typed_value, owner_decl_index); + return genUnnamedConst(lf, src_loc, typed_value, owner_decl_index); } pub fn errUnionPayloadOffset(payload_ty: Type, mod: *Module) u64 { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 70eab9489cc4..91c4faffcc27 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -15,6 +15,7 @@ const link = @import("../link.zig"); const Compilation = @import("../Compilation.zig"); const build_options = @import("build_options"); const Module = @import("../Module.zig"); +const Zcu = Module; const InternPool = @import("../InternPool.zig"); const Package = @import("../Package.zig"); const TypedValue = @import("../TypedValue.zig"); @@ -821,7 +822,7 @@ pub const Object = struct { type_map: TypeMap, di_type_map: DITypeMap, /// The LLVM global table which holds the names corresponding to Zig errors. - /// Note that the values are not added until flushModule, when all errors in + /// Note that the values are not added until `emit`, when all errors in /// the compilation are known. error_name_table: Builder.Variable.Index, /// This map is usually very close to empty. It tracks only the cases when a @@ -849,27 +850,25 @@ pub const Object = struct { pub const TypeMap = std.AutoHashMapUnmanaged(InternPool.Index, Builder.Type); - /// This is an ArrayHashMap as opposed to a HashMap because in `flushModule` we + /// This is an ArrayHashMap as opposed to a HashMap because in `emit` we /// 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, comp: *Compilation) !*Object { + if (build_options.only_c) unreachable; + const gpa = comp.gpa; + const target = comp.root_mod.resolved_target.result; + const llvm_target_triple = try targetTriple(arena, target); + const strip = comp.root_mod.strip; + const optimize_mode = comp.root_mod.optimize_mode; + const pic = comp.root_mod.pic; var builder = try Builder.init(.{ .allocator = gpa, - .use_lib_llvm = options.use_lib_llvm, - .strip = options.strip or !options.use_lib_llvm, // TODO - .name = options.root_name, - .target = options.target, + .use_lib_llvm = comp.config.use_lib_llvm, + .strip = strip or !comp.config.use_lib_llvm, // TODO + .name = comp.root_name, + .target = target, .triple = llvm_target_triple, }); errdefer builder.deinit(); @@ -877,10 +876,11 @@ pub const Object = struct { var target_machine: if (build_options.have_llvm) *llvm.TargetMachine else void = undefined; var target_data: if (build_options.have_llvm) *llvm.TargetData else void = undefined; if (builder.useLibLlvm()) { - if (!options.strip) { - switch (options.target.ofmt) { - .coff => builder.llvm.module.?.addModuleCodeViewFlag(), - else => builder.llvm.module.?.addModuleDebugInfoFlag(options.dwarf_format == std.dwarf.Format.@"64"), + debug_info: { + switch (comp.config.debug_format) { + .strip => break :debug_info, + .code_view => builder.llvm.module.?.addModuleCodeViewFlag(), + .dwarf => |f| builder.llvm.module.?.addModuleDebugInfoFlag(f == .@"64"), } builder.llvm.di_builder = builder.llvm.module.?.createDIBuilder(true); @@ -898,26 +898,29 @@ pub const Object = struct { // very location dependent. // TODO: the only concern I have with this is WASI as either host or target, should // we leave the paths as relative then? + // TODO: This is totally wrong. In dwarf, paths are encoded as relative to + // a particular directory, and then the directory path is specified elsewhere. + // In the compiler frontend we have it stored correctly in this + // way already, but here we throw all that sweet information + // into the garbage can by converting into absolute paths. What + // a terrible tragedy. 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, ""); + if (comp.module) |zcu| m: { + const d = try zcu.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); + const realpath = std.fs.realpathAlloc(arena, d) catch break :blk d; + break :blk try arena.dupeZ(u8, realpath); } - const cwd = try std.process.getCwd(&buf); - break :blk try builder.gpa.dupeZ(u8, cwd); + const cwd = try std.process.getCwdAlloc(arena); + break :blk try arena.dupeZ(u8, cwd); }; - defer builder.gpa.free(compile_unit_dir_z); builder.llvm.di_compile_unit = builder.llvm.di_builder.?.createCompileUnit( DW.LANG.C99, - builder.llvm.di_builder.?.createFile(options.root_name, compile_unit_dir_z), + builder.llvm.di_builder.?.createFile(comp.root_name, compile_unit_dir_z), producer.slice(&builder).?, - options.optimize_mode != .Debug, + optimize_mode != .Debug, "", // flags 0, // runtime version "", // split name @@ -926,19 +929,19 @@ pub const Object = struct { ); } - const opt_level: llvm.CodeGenOptLevel = if (options.optimize_mode == .Debug) + const opt_level: llvm.CodeGenOptLevel = if (optimize_mode == .Debug) .None else .Aggressive; - const reloc_mode: llvm.RelocMode = if (options.pic) + const reloc_mode: llvm.RelocMode = if (pic) .PIC - else if (options.link_mode == .Dynamic) + else if (comp.config.link_mode == .Dynamic) llvm.RelocMode.DynamicNoPIC else .Static; - const code_model: llvm.CodeModel = switch (options.machine_code_model) { + const code_model: llvm.CodeModel = switch (comp.root_mod.code_model) { .default => .Default, .tiny => .Tiny, .small => .Small, @@ -953,15 +956,15 @@ pub const Object = struct { target_machine = llvm.TargetMachine.create( builder.llvm.target.?, builder.target_triple.slice(&builder).?, - if (options.target.cpu.model.llvm_name) |s| s.ptr else null, - options.llvm_cpu_features, + if (target.cpu.model.llvm_name) |s| s.ptr else null, + comp.root_mod.resolved_target.llvm_cpu_features.?, opt_level, reloc_mode, code_model, - options.function_sections, - options.data_sections, + comp.function_sections, + comp.data_sections, float_abi, - if (target_util.llvmMachineAbi(options.target)) |s| s.ptr else null, + if (target_util.llvmMachineAbi(target)) |s| s.ptr else null, ); errdefer target_machine.dispose(); @@ -970,15 +973,15 @@ pub const Object = struct { builder.llvm.module.?.setModuleDataLayout(target_data); - if (options.pic) builder.llvm.module.?.setModulePICLevel(); - if (options.pie) builder.llvm.module.?.setModulePIELevel(); + if (pic) builder.llvm.module.?.setModulePICLevel(); + if (comp.config.pie) builder.llvm.module.?.setModulePIELevel(); if (code_model != .Default) builder.llvm.module.?.setModuleCodeModel(code_model); - if (options.opt_bisect_limit >= 0) { - builder.llvm.context.setOptBisectLimit(std.math.lossyCast(c_int, options.opt_bisect_limit)); + if (comp.llvm_opt_bisect_limit >= 0) { + builder.llvm.context.setOptBisectLimit(comp.llvm_opt_bisect_limit); } - builder.data_layout = try builder.fmt("{}", .{DataLayoutBuilder{ .target = options.target }}); + builder.data_layout = try builder.fmt("{}", .{DataLayoutBuilder{ .target = target }}); if (std.debug.runtime_safety) { const rep = target_data.stringRep(); defer llvm.disposeMessage(rep); @@ -989,16 +992,17 @@ pub const Object = struct { } } - return .{ + const obj = try arena.create(Object); + obj.* = .{ .gpa = gpa, .builder = builder, - .module = options.module.?, + .module = comp.module.?, .di_map = .{}, .di_builder = if (builder.useLibLlvm()) builder.llvm.di_builder else null, // TODO .di_compile_unit = if (builder.useLibLlvm()) builder.llvm.di_compile_unit else null, .target_machine = target_machine, .target_data = target_data, - .target = options.target, + .target = target, .decl_map = .{}, .anon_decl_map = .{}, .named_enum_map = .{}, @@ -1009,9 +1013,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,22 +1034,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, - cache_directory: Compilation.Directory, - ) !?[*:0]u8 { - const loc = opt_loc orelse return null; - const directory = loc.directory orelse cache_directory; - const slice = try directory.joinZ(arena, &[_][]const u8{loc.basename}); - return slice.ptr; - } - fn genErrorNameTable(o: *Object) Allocator.Error!void { // If o.error_name_table is null, then it was not referenced by any instructions. if (o.error_name_table == .none) return; @@ -1182,12 +1172,22 @@ pub const Object = struct { } } - pub fn flushModule(self: *Object, comp: *Compilation, prog_node: *std.Progress.Node) !void { - var sub_prog_node = prog_node.start("LLVM Emit Object", 0); - sub_prog_node.activate(); - sub_prog_node.context.refresh(); - defer sub_prog_node.end(); + pub const EmitOptions = struct { + pre_ir_path: ?[]const u8, + pre_bc_path: ?[]const u8, + bin_path: ?[*:0]const u8, + asm_path: ?[*:0]const u8, + post_ir_path: ?[*:0]const u8, + post_bc_path: ?[*:0]const u8, + + is_debug: bool, + is_small: bool, + time_report: bool, + sanitize_thread: bool, + lto: bool, + }; + pub fn emit(self: *Object, options: EmitOptions) !void { try self.resolveExportExternCollisions(); try self.genErrorNameTable(); try self.genCmpLtErrorsLenFunction(); @@ -1213,7 +1213,7 @@ pub const Object = struct { dib.finalize(); } - if (comp.verbose_llvm_ir) |path| { + if (options.pre_ir_path) |path| { if (std.mem.eql(u8, path, "-")) { self.builder.dump(); } else { @@ -1221,91 +1221,72 @@ pub const Object = struct { } } - if (comp.verbose_llvm_bc) |path| _ = try self.builder.writeBitcodeToFile(path); - - var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - - const mod = comp.bin_file.options.module.?; - const cache_dir = mod.zig_cache_artifact_directory; + if (options.pre_bc_path) |path| _ = try self.builder.writeBitcodeToFile(path); if (std.debug.runtime_safety and !try self.builder.verify()) { - if (try locPath(arena, comp.emit_llvm_ir, cache_dir)) |emit_llvm_ir_path| - _ = self.builder.printToFileZ(emit_llvm_ir_path); @panic("LLVM module verification failed"); } - var emit_bin_path: ?[*:0]const u8 = if (comp.bin_file.options.emit) |emit| - try emit.basenamePath(arena, try arena.dupeZ(u8, comp.bin_file.intermediary_basename.?)) - else - null; - - const emit_asm_path = try locPath(arena, comp.emit_asm, cache_dir); - var emit_llvm_ir_path = try locPath(arena, comp.emit_llvm_ir, cache_dir); - const emit_llvm_bc_path = try locPath(arena, comp.emit_llvm_bc, cache_dir); - - const emit_asm_msg = emit_asm_path orelse "(none)"; - const emit_bin_msg = emit_bin_path orelse "(none)"; - const emit_llvm_ir_msg = emit_llvm_ir_path orelse "(none)"; - const emit_llvm_bc_msg = emit_llvm_bc_path orelse "(none)"; + const emit_asm_msg = options.asm_path orelse "(none)"; + const emit_bin_msg = options.bin_path orelse "(none)"; + const post_llvm_ir_msg = options.post_ir_path orelse "(none)"; + const post_llvm_bc_msg = options.post_bc_path orelse "(none)"; log.debug("emit LLVM object asm={s} bin={s} ir={s} bc={s}", .{ - emit_asm_msg, emit_bin_msg, emit_llvm_ir_msg, emit_llvm_bc_msg, + emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg, }); - if (emit_asm_path == null and emit_bin_path == null and - emit_llvm_ir_path == null and emit_llvm_bc_path == null) return; + if (options.asm_path == null and options.bin_path == null and + options.post_ir_path == null and options.post_bc_path == null) return; - if (!self.builder.useLibLlvm()) { - log.err("emitting without libllvm not implemented", .{}); - return error.FailedToEmit; - } + if (!self.builder.useLibLlvm()) unreachable; // caught in Compilation.Config.resolve // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. // So we call the entire pipeline multiple times if this is requested. var error_message: [*:0]const u8 = undefined; - if (emit_asm_path != null and emit_bin_path != null) { + var emit_bin_path = options.bin_path; + var post_ir_path = options.post_ir_path; + if (options.asm_path != null and options.bin_path != null) { if (self.target_machine.emitToFile( self.builder.llvm.module.?, &error_message, - comp.bin_file.options.optimize_mode == .Debug, - comp.bin_file.options.optimize_mode == .ReleaseSmall, - comp.time_report, - comp.bin_file.options.tsan, - comp.bin_file.options.lto, + options.is_debug, + options.is_small, + options.time_report, + options.sanitize_thread, + options.lto, null, emit_bin_path, - emit_llvm_ir_path, + post_ir_path, null, )) { defer llvm.disposeMessage(error_message); log.err("LLVM failed to emit bin={s} ir={s}: {s}", .{ - emit_bin_msg, emit_llvm_ir_msg, error_message, + emit_bin_msg, post_llvm_ir_msg, error_message, }); return error.FailedToEmit; } emit_bin_path = null; - emit_llvm_ir_path = null; + post_ir_path = null; } if (self.target_machine.emitToFile( self.builder.llvm.module.?, &error_message, - comp.bin_file.options.optimize_mode == .Debug, - comp.bin_file.options.optimize_mode == .ReleaseSmall, - comp.time_report, - comp.bin_file.options.tsan, - comp.bin_file.options.lto, - emit_asm_path, + options.is_debug, + options.is_small, + options.time_report, + options.sanitize_thread, + options.lto, + options.asm_path, emit_bin_path, - emit_llvm_ir_path, - emit_llvm_bc_path, + post_ir_path, + options.post_bc_path, )) { defer llvm.disposeMessage(error_message); log.err("LLVM failed to emit asm={s} bin={s} ir={s} bc={s}: {s}", .{ - emit_asm_msg, emit_bin_msg, emit_llvm_ir_msg, emit_llvm_bc_msg, + emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg, error_message, }); return error.FailedToEmit; @@ -1314,17 +1295,19 @@ pub const Object = struct { pub fn updateFunc( o: *Object, - mod: *Module, + zcu: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness, ) !void { - const func = mod.funcInfo(func_index); + const func = zcu.funcInfo(func_index); const decl_index = func.owner_decl; - const decl = mod.declPtr(decl_index); - const fn_info = mod.typeToFunc(decl.ty).?; - const target = mod.getTarget(); - const ip = &mod.intern_pool; + const decl = zcu.declPtr(decl_index); + const namespace = zcu.namespacePtr(decl.src_namespace); + const owner_mod = namespace.file_scope.mod; + const fn_info = zcu.typeToFunc(decl.ty).?; + const target = zcu.getTarget(); + const ip = &zcu.intern_pool; var dg: DeclGen = .{ .object = o, @@ -1359,7 +1342,7 @@ pub const Object = struct { } // TODO: disable this if safety is off for the function scope - const ssp_buf_size = mod.comp.bin_file.options.stack_protector; + const ssp_buf_size = owner_mod.stack_protector; if (ssp_buf_size != 0) { try attributes.addFnAttr(.sspstrong, &o.builder); try attributes.addFnAttr(.{ .string = .{ @@ -1369,7 +1352,7 @@ pub const Object = struct { } // TODO: disable this if safety is off for the function scope - if (mod.comp.bin_file.options.stack_check) { + if (owner_mod.stack_check) { try attributes.addFnAttr(.{ .string = .{ .kind = try o.builder.string("probe-stack"), .value = try o.builder.string("__zig_probe_stack"), @@ -1392,20 +1375,22 @@ pub const Object = struct { var llvm_arg_i: u32 = 0; // This gets the LLVM values from the function and stores them in `dg.args`. - const sret = firstParamSRet(fn_info, mod); + const sret = firstParamSRet(fn_info, zcu); const ret_ptr: Builder.Value = if (sret) param: { const param = wip.arg(llvm_arg_i); llvm_arg_i += 1; break :param param; } else .none; - if (ccAbiPromoteInt(fn_info.cc, mod, Type.fromInterned(fn_info.return_type))) |s| switch (s) { + if (ccAbiPromoteInt(fn_info.cc, zcu, Type.fromInterned(fn_info.return_type))) |s| switch (s) { .signed => try attributes.addRetAttr(.signext, &o.builder), .unsigned => try attributes.addRetAttr(.zeroext, &o.builder), }; - const err_return_tracing = Type.fromInterned(fn_info.return_type).isError(mod) and - mod.comp.bin_file.options.error_return_tracing; + const comp = zcu.comp; + + const err_return_tracing = Type.fromInterned(fn_info.return_type).isError(zcu) and + comp.config.any_error_tracing; const err_ret_trace: Builder.Value = if (err_return_tracing) param: { const param = wip.arg(llvm_arg_i); @@ -1433,8 +1418,8 @@ pub const Object = struct { const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[param_index]); const param = wip.arg(llvm_arg_i); - if (isByRef(param_ty, mod)) { - const alignment = param_ty.abiAlignment(mod).toLlvm(); + if (isByRef(param_ty, zcu)) { + const alignment = param_ty.abiAlignment(zcu).toLlvm(); const param_llvm_ty = param.typeOfWip(&wip); const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, alignment, target); _ = try wip.store(.normal, param, arg_ptr, alignment); @@ -1450,12 +1435,12 @@ pub const Object = struct { const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); const param_llvm_ty = try o.lowerType(param_ty); const param = wip.arg(llvm_arg_i); - const alignment = param_ty.abiAlignment(mod).toLlvm(); + const alignment = param_ty.abiAlignment(zcu).toLlvm(); try o.addByRefParamAttrs(&attributes, llvm_arg_i, alignment, it.byval_attr, param_llvm_ty); llvm_arg_i += 1; - if (isByRef(param_ty, mod)) { + if (isByRef(param_ty, zcu)) { args.appendAssumeCapacity(param); } else { args.appendAssumeCapacity(try wip.load(.normal, param_llvm_ty, param, alignment, "")); @@ -1465,12 +1450,12 @@ pub const Object = struct { const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); const param_llvm_ty = try o.lowerType(param_ty); const param = wip.arg(llvm_arg_i); - const alignment = param_ty.abiAlignment(mod).toLlvm(); + const alignment = param_ty.abiAlignment(zcu).toLlvm(); try attributes.addParamAttr(llvm_arg_i, .noundef, &o.builder); llvm_arg_i += 1; - if (isByRef(param_ty, mod)) { + if (isByRef(param_ty, zcu)) { args.appendAssumeCapacity(param); } else { args.appendAssumeCapacity(try wip.load(.normal, param_llvm_ty, param, alignment, "")); @@ -1483,11 +1468,11 @@ pub const Object = struct { llvm_arg_i += 1; const param_llvm_ty = try o.lowerType(param_ty); - const alignment = param_ty.abiAlignment(mod).toLlvm(); + const alignment = param_ty.abiAlignment(zcu).toLlvm(); const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, alignment, target); _ = try wip.store(.normal, param, arg_ptr, alignment); - args.appendAssumeCapacity(if (isByRef(param_ty, mod)) + args.appendAssumeCapacity(if (isByRef(param_ty, zcu)) arg_ptr else try wip.load(.normal, param_llvm_ty, arg_ptr, alignment, "")); @@ -1495,14 +1480,14 @@ pub const Object = struct { .slice => { assert(!it.byval_attr); const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); - const ptr_info = param_ty.ptrInfo(mod); + const ptr_info = param_ty.ptrInfo(zcu); if (math.cast(u5, it.zig_index - 1)) |i| { if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) { try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder); } } - if (param_ty.zigTypeTag(mod) != .Optional) { + if (param_ty.zigTypeTag(zcu) != .Optional) { try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); } if (ptr_info.flags.is_const) { @@ -1511,7 +1496,7 @@ pub const Object = struct { const elem_align = (if (ptr_info.flags.alignment != .none) @as(InternPool.Alignment, ptr_info.flags.alignment) else - Type.fromInterned(ptr_info.child).abiAlignment(mod).max(.@"1")).toLlvm(); + Type.fromInterned(ptr_info.child).abiAlignment(zcu).max(.@"1")).toLlvm(); try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align }, &o.builder); const ptr_param = wip.arg(llvm_arg_i); llvm_arg_i += 1; @@ -1528,7 +1513,7 @@ pub const Object = struct { const field_types = it.types_buffer[0..it.types_len]; const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); const param_llvm_ty = try o.lowerType(param_ty); - const param_alignment = param_ty.abiAlignment(mod).toLlvm(); + const param_alignment = param_ty.abiAlignment(zcu).toLlvm(); const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, param_alignment, target); const llvm_ty = try o.builder.structType(.normal, field_types); for (0..field_types.len) |field_i| { @@ -1540,7 +1525,7 @@ pub const Object = struct { _ = try wip.store(.normal, param, field_ptr, alignment); } - const is_by_ref = isByRef(param_ty, mod); + const is_by_ref = isByRef(param_ty, zcu); args.appendAssumeCapacity(if (is_by_ref) arg_ptr else @@ -1558,11 +1543,11 @@ pub const Object = struct { const param = wip.arg(llvm_arg_i); llvm_arg_i += 1; - const alignment = param_ty.abiAlignment(mod).toLlvm(); + const alignment = param_ty.abiAlignment(zcu).toLlvm(); const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, alignment, target); _ = try wip.store(.normal, param, arg_ptr, alignment); - args.appendAssumeCapacity(if (isByRef(param_ty, mod)) + args.appendAssumeCapacity(if (isByRef(param_ty, zcu)) arg_ptr else try wip.load(.normal, param_llvm_ty, arg_ptr, alignment, "")); @@ -1573,11 +1558,11 @@ pub const Object = struct { const param = wip.arg(llvm_arg_i); llvm_arg_i += 1; - const alignment = param_ty.abiAlignment(mod).toLlvm(); + const alignment = param_ty.abiAlignment(zcu).toLlvm(); const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, alignment, target); _ = try wip.store(.normal, param, arg_ptr, alignment); - args.appendAssumeCapacity(if (isByRef(param_ty, mod)) + args.appendAssumeCapacity(if (isByRef(param_ty, zcu)) arg_ptr else try wip.load(.normal, param_llvm_ty, arg_ptr, alignment, "")); @@ -1592,11 +1577,11 @@ pub const Object = struct { var di_scope: ?if (build_options.have_llvm) *llvm.DIScope else noreturn = null; if (o.di_builder) |dib| { - di_file = try o.getDIFile(gpa, mod.namespacePtr(decl.src_namespace).file_scope); + di_file = try o.getDIFile(gpa, namespace.file_scope); const line_number = decl.src_line + 1; - const is_internal_linkage = decl.val.getExternFunc(mod) == null and - !mod.decl_exports.contains(decl_index); + const is_internal_linkage = decl.val.getExternFunc(zcu) == null and + !zcu.decl_exports.contains(decl_index); const noret_bit: c_uint = if (fn_info.return_type == .noreturn_type) llvm.DIFlags.NoReturn else @@ -1613,7 +1598,7 @@ pub const Object = struct { true, // is definition line_number + func.lbrace_line, // scope line llvm.DIFlags.StaticMember | noret_bit, - mod.comp.bin_file.options.optimize_mode != .Debug, + owner_mod.optimize_mode != .Debug, null, // decl_subprogram ); try o.di_map.put(gpa, decl, subprogram.toNode()); @@ -1634,7 +1619,7 @@ pub const Object = struct { .arg_index = 0, .func_inst_table = .{}, .blocks = .{}, - .sync_scope = if (mod.comp.bin_file.options.single_threaded) .singlethread else .system, + .sync_scope = if (owner_mod.single_threaded) .singlethread else .system, .di_scope = di_scope, .di_file = di_file, .base_line = dg.decl.src_line, @@ -1648,7 +1633,7 @@ pub const Object = struct { fg.genBody(air.getMainBody()) catch |err| switch (err) { error.CodegenFail => { decl.analysis = .codegen_failure; - try mod.failed_decls.put(mod.gpa, decl_index, dg.err_msg.?); + try zcu.failed_decls.put(zcu.gpa, decl_index, dg.err_msg.?); dg.err_msg = null; return; }, @@ -1657,7 +1642,7 @@ pub const Object = struct { try fg.wip.finish(); - try o.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index)); + try o.updateExports(zcu, .{ .decl_index = decl_index }, zcu.getDeclExports(decl_index)); } pub fn updateDecl(self: *Object, module: *Module, decl_index: InternPool.DeclIndex) !void { @@ -1695,6 +1680,7 @@ pub const Object = struct { // because we call `updateExports` at the end of `updateFunc` and `updateDecl`. const global_index = self.decl_map.get(decl_index) orelse return; const decl = mod.declPtr(decl_index); + const comp = mod.comp; if (decl.isExtern(mod)) { const decl_name = decl_name: { const decl_name = mod.intern_pool.stringToSlice(decl.name); @@ -1719,7 +1705,8 @@ pub const Object = struct { try global_index.rename(decl_name, &self.builder); global_index.setLinkage(.external, &self.builder); global_index.setUnnamedAddr(.default, &self.builder); - if (mod.wantDllExports()) global_index.setDllStorageClass(.default, &self.builder); + if (comp.config.dll_export_fns) + global_index.setDllStorageClass(.default, &self.builder); if (self.di_map.get(decl)) |di_node| { const decl_name_slice = decl_name.slice(&self.builder).?; if (try decl.isFunction(mod)) { @@ -1785,11 +1772,14 @@ pub const Object = struct { ); try global_index.rename(fqn, &self.builder); global_index.setLinkage(.internal, &self.builder); - if (mod.wantDllExports()) global_index.setDllStorageClass(.default, &self.builder); + if (comp.config.dll_export_fns) + global_index.setDllStorageClass(.default, &self.builder); global_index.setUnnamedAddr(.unnamed_addr, &self.builder); if (decl.val.getVariable(mod)) |decl_var| { + const decl_namespace = mod.namespacePtr(decl.src_namespace); + const single_threaded = decl_namespace.file_scope.mod.single_threaded; global_index.ptrConst(&self.builder).kind.variable.setThreadLocal( - if (decl_var.is_threadlocal and !mod.comp.bin_file.options.single_threaded) + if (decl_var.is_threadlocal and !single_threaded) .generaldynamic else .default, @@ -1842,7 +1832,9 @@ pub const Object = struct { exports: []const *Module.Export, ) link.File.UpdateExportsError!void { global_index.setUnnamedAddr(.default, &o.builder); - if (mod.wantDllExports()) global_index.setDllStorageClass(.dllexport, &o.builder); + const comp = mod.comp; + if (comp.config.dll_export_fns) + global_index.setDllStorageClass(.dllexport, &o.builder); global_index.setLinkage(switch (exports[0].opts.linkage) { .Internal => unreachable, .Strong => .external, @@ -2821,7 +2813,7 @@ pub const Object = struct { } if (Type.fromInterned(fn_info.return_type).isError(mod) and - o.module.comp.bin_file.options.error_return_tracing) + o.module.comp.config.any_error_tracing) { const ptr_ty = try mod.singleMutPtrType(try o.getStackTraceType()); try param_di_types.append(try o.lowerDebugType(ptr_ty, .full)); @@ -2899,7 +2891,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"); @@ -2934,22 +2926,24 @@ pub const Object = struct { o: *Object, decl_index: InternPool.DeclIndex, ) Allocator.Error!Builder.Function.Index { - const mod = o.module; - const ip = &mod.intern_pool; + const zcu = o.module; + const ip = &zcu.intern_pool; const gpa = o.gpa; - const decl = mod.declPtr(decl_index); + const decl = zcu.declPtr(decl_index); + const namespace = zcu.namespacePtr(decl.src_namespace); + const owner_mod = namespace.file_scope.mod; const zig_fn_type = decl.ty; const gop = try o.decl_map.getOrPut(gpa, decl_index); if (gop.found_existing) return gop.value_ptr.ptr(&o.builder).kind.function; assert(decl.has_tv); - const fn_info = mod.typeToFunc(zig_fn_type).?; - const target = mod.getTarget(); - const sret = firstParamSRet(fn_info, mod); + const fn_info = zcu.typeToFunc(zig_fn_type).?; + const target = owner_mod.resolved_target.result; + const sret = firstParamSRet(fn_info, zcu); const function_index = try o.builder.addFunction( try o.lowerType(zig_fn_type), - try o.builder.string(ip.stringToSlice(try decl.getFullyQualifiedName(mod))), + try o.builder.string(ip.stringToSlice(try decl.getFullyQualifiedName(zcu))), toLlvmAddressSpace(decl.@"addrspace", target), ); gop.value_ptr.* = function_index.ptrConst(&o.builder).global; @@ -2957,7 +2951,7 @@ pub const Object = struct { var attributes: Builder.FunctionAttributes.Wip = .{}; defer attributes.deinit(&o.builder); - const is_extern = decl.isExtern(mod); + const is_extern = decl.isExtern(zcu); if (!is_extern) { function_index.setLinkage(.internal, &o.builder); function_index.setUnnamedAddr(.unnamed_addr, &o.builder); @@ -2967,7 +2961,7 @@ pub const Object = struct { .kind = try o.builder.string("wasm-import-name"), .value = try o.builder.string(ip.stringToSlice(decl.name)), } }, &o.builder); - if (ip.stringToSliceUnwrap(decl.getOwnedExternFunc(mod).?.lib_name)) |lib_name| { + if (ip.stringToSliceUnwrap(decl.getOwnedExternFunc(zcu).?.lib_name)) |lib_name| { if (!std.mem.eql(u8, lib_name, "c")) try attributes.addFnAttr(.{ .string = .{ .kind = try o.builder.string("wasm-import-module"), .value = try o.builder.string(lib_name), @@ -2988,8 +2982,8 @@ pub const Object = struct { llvm_arg_i += 1; } - const err_return_tracing = Type.fromInterned(fn_info.return_type).isError(mod) and - mod.comp.bin_file.options.error_return_tracing; + const err_return_tracing = Type.fromInterned(fn_info.return_type).isError(zcu) and + zcu.comp.config.any_error_tracing; if (err_return_tracing) { try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); @@ -3010,7 +3004,7 @@ pub const Object = struct { function_index.setAlignment(fn_info.alignment.toLlvm(), &o.builder); // Function attributes that are independent of analysis results of the function body. - try o.addCommonFnAttributes(&attributes); + try o.addCommonFnAttributes(&attributes, owner_mod); if (fn_info.return_type == .noreturn_type) try attributes.addFnAttr(.noreturn, &o.builder); @@ -3023,14 +3017,14 @@ pub const Object = struct { .byval => { const param_index = it.zig_index - 1; const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[param_index]); - if (!isByRef(param_ty, mod)) { + if (!isByRef(param_ty, zcu)) { try o.addByValParamAttrs(&attributes, param_ty, param_index, fn_info, it.llvm_index - 1); } }, .byref => { const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); const param_llvm_ty = try o.lowerType(param_ty); - const alignment = param_ty.abiAlignment(mod); + const alignment = param_ty.abiAlignment(zcu); try o.addByRefParamAttrs(&attributes, it.llvm_index - 1, alignment.toLlvm(), it.byval_attr, param_llvm_ty); }, .byref_mut => try attributes.addParamAttr(it.llvm_index - 1, .noundef, &o.builder), @@ -3056,13 +3050,14 @@ pub const Object = struct { fn addCommonFnAttributes( o: *Object, attributes: *Builder.FunctionAttributes.Wip, + owner_mod: *Package.Module, ) Allocator.Error!void { const comp = o.module.comp; - if (!comp.bin_file.options.red_zone) { + if (!owner_mod.red_zone) { try attributes.addFnAttr(.noredzone, &o.builder); } - if (comp.bin_file.options.omit_frame_pointer) { + if (owner_mod.omit_frame_pointer) { try attributes.addFnAttr(.{ .string = .{ .kind = try o.builder.string("frame-pointer"), .value = try o.builder.string("none"), @@ -3074,12 +3069,10 @@ pub const Object = struct { } }, &o.builder); } try attributes.addFnAttr(.nounwind, &o.builder); - if (comp.unwind_tables) { + if (owner_mod.unwind_tables) { try attributes.addFnAttr(.{ .uwtable = Builder.Attribute.UwTable.default }, &o.builder); } - if (comp.bin_file.options.skip_linker_dependencies or - comp.bin_file.options.no_builtin) - { + if (comp.skip_linker_dependencies or comp.no_builtin) { // The intent here is for compiler-rt and libc functions to not generate // infinite recursion. For example, if we are compiling the memcpy function, // and llvm detects that the body is equivalent to memcpy, it may replace the @@ -3087,26 +3080,27 @@ pub const Object = struct { // overflow instead of performing memcpy. try attributes.addFnAttr(.nobuiltin, &o.builder); } - if (comp.bin_file.options.optimize_mode == .ReleaseSmall) { + if (owner_mod.optimize_mode == .ReleaseSmall) { try attributes.addFnAttr(.minsize, &o.builder); try attributes.addFnAttr(.optsize, &o.builder); } - if (comp.bin_file.options.tsan) { + if (owner_mod.sanitize_thread) { try attributes.addFnAttr(.sanitize_thread, &o.builder); } - if (comp.getTarget().cpu.model.llvm_name) |s| { + const target = owner_mod.resolved_target.result; + if (target.cpu.model.llvm_name) |s| { try attributes.addFnAttr(.{ .string = .{ .kind = try o.builder.string("target-cpu"), .value = try o.builder.string(s), } }, &o.builder); } - if (comp.bin_file.options.llvm_cpu_features) |s| { + if (owner_mod.resolved_target.llvm_cpu_features) |s| { try attributes.addFnAttr(.{ .string = .{ .kind = try o.builder.string("target-features"), .value = try o.builder.string(std.mem.span(s)), } }, &o.builder); } - if (comp.getTarget().cpu.arch.isBpf()) { + if (target.cpu.arch.isBpf()) { try attributes.addFnAttr(.{ .string = .{ .kind = try o.builder.string("no-builtins"), .value = .empty, @@ -3176,7 +3170,8 @@ pub const Object = struct { variable_index.setLinkage(.external, &o.builder); variable_index.setUnnamedAddr(.default, &o.builder); if (decl.val.getVariable(mod)) |decl_var| { - const single_threaded = mod.comp.bin_file.options.single_threaded; + const decl_namespace = mod.namespacePtr(decl.src_namespace); + const single_threaded = decl_namespace.file_scope.mod.single_threaded; variable_index.setThreadLocal( if (decl_var.is_threadlocal and !single_threaded) .generaldynamic else .default, &o.builder, @@ -3679,7 +3674,7 @@ pub const Object = struct { } if (Type.fromInterned(fn_info.return_type).isError(mod) and - mod.comp.bin_file.options.error_return_tracing) + mod.comp.config.any_error_tracing) { const ptr_ty = try mod.singleMutPtrType(try o.getStackTraceType()); try llvm_params.append(o.gpa, try o.lowerType(ptr_ty)); @@ -4648,6 +4643,100 @@ pub const Object = struct { .field_index = @intCast(field_index), }); } + + fn getCmpLtErrorsLenFunction(o: *Object) !Builder.Function.Index { + const name = try o.builder.string(lt_errors_fn_name); + if (o.builder.getGlobal(name)) |llvm_fn| return llvm_fn.ptrConst(&o.builder).kind.function; + + const zcu = o.module; + const target = zcu.root_mod.resolved_target.result; + const function_index = try o.builder.addFunction( + try o.builder.fnType(.i1, &.{try o.errorIntType()}, .normal), + name, + toLlvmAddressSpace(.generic, target), + ); + + var attributes: Builder.FunctionAttributes.Wip = .{}; + defer attributes.deinit(&o.builder); + try o.addCommonFnAttributes(&attributes, zcu.root_mod); + + function_index.setLinkage(.internal, &o.builder); + function_index.setCallConv(.fastcc, &o.builder); + function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); + return function_index; + } + + fn getEnumTagNameFunction(o: *Object, enum_ty: Type) !Builder.Function.Index { + const zcu = o.module; + const ip = &zcu.intern_pool; + const enum_type = ip.indexToKey(enum_ty.toIntern()).enum_type; + + // TODO: detect when the type changes and re-emit this function. + const gop = try o.decl_map.getOrPut(o.gpa, enum_type.decl); + if (gop.found_existing) return gop.value_ptr.ptrConst(&o.builder).kind.function; + errdefer assert(o.decl_map.remove(enum_type.decl)); + + const usize_ty = try o.lowerType(Type.usize); + const ret_ty = try o.lowerType(Type.slice_const_u8_sentinel_0); + const fqn = try zcu.declPtr(enum_type.decl).getFullyQualifiedName(zcu); + const target = zcu.root_mod.resolved_target.result; + const function_index = try o.builder.addFunction( + try o.builder.fnType(ret_ty, &.{try o.lowerType(Type.fromInterned(enum_type.tag_ty))}, .normal), + try o.builder.fmt("__zig_tag_name_{}", .{fqn.fmt(ip)}), + toLlvmAddressSpace(.generic, target), + ); + + var attributes: Builder.FunctionAttributes.Wip = .{}; + defer attributes.deinit(&o.builder); + try o.addCommonFnAttributes(&attributes, zcu.root_mod); + + function_index.setLinkage(.internal, &o.builder); + function_index.setCallConv(.fastcc, &o.builder); + function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); + gop.value_ptr.* = function_index.ptrConst(&o.builder).global; + + var wip = try Builder.WipFunction.init(&o.builder, function_index); + defer wip.deinit(); + wip.cursor = .{ .block = try wip.block(0, "Entry") }; + + const bad_value_block = try wip.block(1, "BadValue"); + const tag_int_value = wip.arg(0); + var wip_switch = + try wip.@"switch"(tag_int_value, bad_value_block, @intCast(enum_type.names.len)); + defer wip_switch.finish(&wip); + + for (0..enum_type.names.len) |field_index| { + const name = try o.builder.string(ip.stringToSlice(enum_type.names.get(ip)[field_index])); + const name_init = try o.builder.stringNullConst(name); + const name_variable_index = + try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); + try name_variable_index.setInitializer(name_init, &o.builder); + name_variable_index.setLinkage(.private, &o.builder); + name_variable_index.setMutability(.constant, &o.builder); + name_variable_index.setUnnamedAddr(.unnamed_addr, &o.builder); + name_variable_index.setAlignment(comptime Builder.Alignment.fromByteUnits(1), &o.builder); + + const name_val = try o.builder.structValue(ret_ty, &.{ + name_variable_index.toConst(&o.builder), + try o.builder.intConst(usize_ty, name.slice(&o.builder).?.len), + }); + + const return_block = try wip.block(1, "Name"); + const this_tag_int_value = try o.lowerValue( + (try zcu.enumValueFieldIndex(enum_ty, @intCast(field_index))).toIntern(), + ); + try wip_switch.addCase(this_tag_int_value, return_block, &wip); + + wip.cursor = .{ .block = return_block }; + _ = try wip.ret(name_val); + } + + wip.cursor = .{ .block = bad_value_block }; + _ = try wip.@"unreachable"(); + + try wip.finish(); + return function_index; + } }; pub const DeclGen = struct { @@ -4656,6 +4745,13 @@ pub const DeclGen = struct { decl_index: InternPool.DeclIndex, err_msg: ?*Module.ErrorMsg, + fn ownerModule(dg: DeclGen) *Package.Module { + const o = dg.object; + const zcu = o.module; + const namespace = zcu.namespacePtr(dg.decl.src_namespace); + return namespace.file_scope.mod; + } + fn todo(dg: *DeclGen, comptime format: []const u8, args: anytype) Error { @setCold(true); assert(dg.err_msg == null); @@ -5144,7 +5240,7 @@ pub const FuncGen = struct { }; const err_return_tracing = return_type.isError(mod) and - o.module.comp.bin_file.options.error_return_tracing; + o.module.comp.config.any_error_tracing; if (err_return_tracing) { assert(self.err_ret_trace != .none); try llvm_args.append(self.err_ret_trace); @@ -5616,7 +5712,7 @@ pub const FuncGen = struct { const o = self.dg.object; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand = try self.resolveInst(un_op); - const llvm_fn = try self.getCmpLtErrorsLenFunction(); + const llvm_fn = try o.getCmpLtErrorsLenFunction(); return self.wip.call( .normal, .fastcc, @@ -6549,11 +6645,13 @@ pub const FuncGen = struct { const dib = o.di_builder orelse return .none; const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; - const mod = o.module; - const func = mod.funcInfo(ty_fn.func); + const zcu = o.module; + const func = zcu.funcInfo(ty_fn.func); const decl_index = func.owner_decl; - const decl = mod.declPtr(decl_index); - const di_file = try o.getDIFile(self.gpa, mod.namespacePtr(decl.src_namespace).file_scope); + const decl = zcu.declPtr(decl_index); + const namespace = zcu.namespacePtr(decl.src_namespace); + const owner_mod = namespace.file_scope.mod; + const di_file = try o.getDIFile(self.gpa, zcu.namespacePtr(decl.src_namespace).file_scope); self.di_file = di_file; const line_number = decl.src_line + 1; const cur_debug_location = self.wip.llvm.builder.getCurrentDebugLocation2(); @@ -6564,18 +6662,18 @@ pub const FuncGen = struct { .base_line = self.base_line, }); - const fqn = try decl.getFullyQualifiedName(mod); + const fqn = try decl.getFullyQualifiedName(zcu); - const is_internal_linkage = !mod.decl_exports.contains(decl_index); - const fn_ty = try mod.funcType(.{ + const is_internal_linkage = !zcu.decl_exports.contains(decl_index); + const fn_ty = try zcu.funcType(.{ .param_types = &.{}, .return_type = .void_type, }); const fn_di_ty = try o.lowerDebugType(fn_ty, .full); const subprogram = dib.createFunction( di_file.toScope(), - mod.intern_pool.stringToSlice(decl.name), - mod.intern_pool.stringToSlice(fqn), + zcu.intern_pool.stringToSlice(decl.name), + zcu.intern_pool.stringToSlice(fqn), di_file, line_number, fn_di_ty, @@ -6583,7 +6681,7 @@ pub const FuncGen = struct { true, // is definition line_number + func.lbrace_line, // scope line llvm.DIFlags.StaticMember, - mod.comp.bin_file.options.optimize_mode != .Debug, + owner_mod.optimize_mode != .Debug, null, // decl_subprogram ); @@ -6678,11 +6776,12 @@ pub const FuncGen = struct { null; const debug_loc = llvm.getDebugLoc(self.prev_dbg_line, self.prev_dbg_column, self.di_scope.?, inlined_at); const insert_block = self.wip.cursor.block.toLlvm(&self.wip); - const mod = o.module; - if (isByRef(operand_ty, mod)) { + const zcu = o.module; + const owner_mod = self.dg.ownerModule(); + if (isByRef(operand_ty, zcu)) { _ = dib.insertDeclareAtEnd(operand.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); - } else if (o.module.comp.bin_file.options.optimize_mode == .Debug) { - const alignment = operand_ty.abiAlignment(mod).toLlvm(); + } else if (owner_mod.optimize_mode == .Debug) { + const alignment = operand_ty.abiAlignment(zcu).toLlvm(); const alloca = try self.buildAlloca(operand.typeOfWip(&self.wip), alignment); _ = try self.wip.store(.normal, operand, alloca, alignment); _ = dib.insertDeclareAtEnd(alloca.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); @@ -8731,9 +8830,10 @@ pub const FuncGen = struct { const debug_loc = llvm.getDebugLoc(lbrace_line, lbrace_col, self.di_scope.?, null); const insert_block = self.wip.cursor.block.toLlvm(&self.wip); + const owner_mod = self.dg.ownerModule(); if (isByRef(inst_ty, mod)) { _ = dib.insertDeclareAtEnd(arg_val.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); - } else if (o.module.comp.bin_file.options.optimize_mode == .Debug) { + } else if (owner_mod.optimize_mode == .Debug) { const alignment = inst_ty.abiAlignment(mod).toLlvm(); const alloca = try self.buildAlloca(arg_val.typeOfWip(&self.wip), alignment); _ = try self.wip.store(.normal, arg_val, alloca, alignment); @@ -8823,7 +8923,8 @@ pub const FuncGen = struct { len, if (ptr_ty.isVolatilePtr(mod)) .@"volatile" else .normal, ); - if (safety and mod.comp.bin_file.options.valgrind) { + const owner_mod = self.dg.ownerModule(); + if (safety and owner_mod.valgrind) { try self.valgrindMarkUndef(dest_ptr, len); } return .none; @@ -9139,7 +9240,8 @@ pub const FuncGen = struct { } else { _ = try self.wip.callMemSet(dest_ptr, dest_ptr_align, fill_byte, len, access_kind); } - if (safety and mod.comp.bin_file.options.valgrind) { + const owner_mod = self.dg.ownerModule(); + if (safety and owner_mod.valgrind) { try self.valgrindMarkUndef(dest_ptr, len); } return .none; @@ -9490,24 +9592,25 @@ pub const FuncGen = struct { fn getIsNamedEnumValueFunction(self: *FuncGen, enum_ty: Type) !Builder.Function.Index { const o = self.dg.object; - const mod = o.module; - const enum_type = mod.intern_pool.indexToKey(enum_ty.toIntern()).enum_type; + const zcu = o.module; + const enum_type = zcu.intern_pool.indexToKey(enum_ty.toIntern()).enum_type; // TODO: detect when the type changes and re-emit this function. const gop = try o.named_enum_map.getOrPut(o.gpa, enum_type.decl); if (gop.found_existing) return gop.value_ptr.*; errdefer assert(o.named_enum_map.remove(enum_type.decl)); - const fqn = try mod.declPtr(enum_type.decl).getFullyQualifiedName(mod); + const fqn = try zcu.declPtr(enum_type.decl).getFullyQualifiedName(zcu); + const target = zcu.root_mod.resolved_target.result; const function_index = try o.builder.addFunction( try o.builder.fnType(.i1, &.{try o.lowerType(Type.fromInterned(enum_type.tag_ty))}, .normal), - try o.builder.fmt("__zig_is_named_enum_value_{}", .{fqn.fmt(&mod.intern_pool)}), - toLlvmAddressSpace(.generic, mod.getTarget()), + try o.builder.fmt("__zig_is_named_enum_value_{}", .{fqn.fmt(&zcu.intern_pool)}), + toLlvmAddressSpace(.generic, target), ); var attributes: Builder.FunctionAttributes.Wip = .{}; defer attributes.deinit(&o.builder); - try o.addCommonFnAttributes(&attributes); + try o.addCommonFnAttributes(&attributes, zcu.root_mod); function_index.setLinkage(.internal, &o.builder); function_index.setCallConv(.fastcc, &o.builder); @@ -9526,7 +9629,7 @@ pub const FuncGen = struct { for (0..enum_type.names.len) |field_index| { const this_tag_int_value = try o.lowerValue( - (try mod.enumValueFieldIndex(enum_ty, @intCast(field_index))).toIntern(), + (try zcu.enumValueFieldIndex(enum_ty, @intCast(field_index))).toIntern(), ); try wip_switch.addCase(this_tag_int_value, named_block, &wip); } @@ -9546,7 +9649,7 @@ pub const FuncGen = struct { const operand = try self.resolveInst(un_op); const enum_ty = self.typeOf(un_op); - const llvm_fn = try self.getEnumTagNameFunction(enum_ty); + const llvm_fn = try o.getEnumTagNameFunction(enum_ty); return self.wip.call( .normal, .fastcc, @@ -9558,100 +9661,6 @@ pub const FuncGen = struct { ); } - fn getEnumTagNameFunction(self: *FuncGen, enum_ty: Type) !Builder.Function.Index { - const o = self.dg.object; - const mod = o.module; - const ip = &mod.intern_pool; - const enum_type = ip.indexToKey(enum_ty.toIntern()).enum_type; - - // TODO: detect when the type changes and re-emit this function. - const gop = try o.decl_map.getOrPut(o.gpa, enum_type.decl); - if (gop.found_existing) return gop.value_ptr.ptrConst(&o.builder).kind.function; - errdefer assert(o.decl_map.remove(enum_type.decl)); - - const usize_ty = try o.lowerType(Type.usize); - const ret_ty = try o.lowerType(Type.slice_const_u8_sentinel_0); - const fqn = try mod.declPtr(enum_type.decl).getFullyQualifiedName(mod); - const function_index = try o.builder.addFunction( - try o.builder.fnType(ret_ty, &.{try o.lowerType(Type.fromInterned(enum_type.tag_ty))}, .normal), - try o.builder.fmt("__zig_tag_name_{}", .{fqn.fmt(ip)}), - toLlvmAddressSpace(.generic, mod.getTarget()), - ); - - var attributes: Builder.FunctionAttributes.Wip = .{}; - defer attributes.deinit(&o.builder); - try o.addCommonFnAttributes(&attributes); - - function_index.setLinkage(.internal, &o.builder); - function_index.setCallConv(.fastcc, &o.builder); - function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); - gop.value_ptr.* = function_index.ptrConst(&o.builder).global; - - var wip = try Builder.WipFunction.init(&o.builder, function_index); - defer wip.deinit(); - wip.cursor = .{ .block = try wip.block(0, "Entry") }; - - const bad_value_block = try wip.block(1, "BadValue"); - const tag_int_value = wip.arg(0); - var wip_switch = - try wip.@"switch"(tag_int_value, bad_value_block, @intCast(enum_type.names.len)); - defer wip_switch.finish(&wip); - - for (0..enum_type.names.len) |field_index| { - const name = try o.builder.string(ip.stringToSlice(enum_type.names.get(ip)[field_index])); - const name_init = try o.builder.stringNullConst(name); - const name_variable_index = - try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); - try name_variable_index.setInitializer(name_init, &o.builder); - name_variable_index.setLinkage(.private, &o.builder); - name_variable_index.setMutability(.constant, &o.builder); - name_variable_index.setUnnamedAddr(.unnamed_addr, &o.builder); - name_variable_index.setAlignment(comptime Builder.Alignment.fromByteUnits(1), &o.builder); - - const name_val = try o.builder.structValue(ret_ty, &.{ - name_variable_index.toConst(&o.builder), - try o.builder.intConst(usize_ty, name.slice(&o.builder).?.len), - }); - - const return_block = try wip.block(1, "Name"); - const this_tag_int_value = try o.lowerValue( - (try mod.enumValueFieldIndex(enum_ty, @intCast(field_index))).toIntern(), - ); - try wip_switch.addCase(this_tag_int_value, return_block, &wip); - - wip.cursor = .{ .block = return_block }; - _ = try wip.ret(name_val); - } - - wip.cursor = .{ .block = bad_value_block }; - _ = try wip.@"unreachable"(); - - try wip.finish(); - return function_index; - } - - fn getCmpLtErrorsLenFunction(self: *FuncGen) !Builder.Function.Index { - const o = self.dg.object; - - const name = try o.builder.string(lt_errors_fn_name); - if (o.builder.getGlobal(name)) |llvm_fn| return llvm_fn.ptrConst(&o.builder).kind.function; - - const function_index = try o.builder.addFunction( - try o.builder.fnType(.i1, &.{try o.errorIntType()}, .normal), - name, - toLlvmAddressSpace(.generic, o.module.getTarget()), - ); - - var attributes: Builder.FunctionAttributes.Wip = .{}; - defer attributes.deinit(&o.builder); - try o.addCommonFnAttributes(&attributes); - - function_index.setLinkage(.internal, &o.builder); - function_index.setCallConv(.fastcc, &o.builder); - function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); - return function_index; - } - fn airErrorName(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { const o = self.dg.object; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index fb1f2bb827bd..6b2587e3cb87 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -191,13 +191,9 @@ pub const Object = struct { air: Air, liveness: Liveness, ) !void { - const target = mod.getTarget(); - // We always want a structured control flow in shaders. This option is only relevant - // for OpenCL kernels. - const want_structured_cfg = switch (target.os.tag) { - .opencl => mod.comp.bin_file.options.want_structured_cfg orelse false, - else => true, - }; + const decl = mod.declPtr(decl_index); + const namespace = mod.namespacePtr(decl.src_namespace); + const structured_cfg = namespace.file_scope.mod.structured_cfg; var decl_gen = DeclGen{ .gpa = self.gpa, @@ -208,7 +204,7 @@ pub const Object = struct { .air = air, .liveness = liveness, .type_map = &self.type_map, - .control_flow = switch (want_structured_cfg) { + .control_flow = switch (structured_cfg) { true => .{ .structured = .{} }, false => .{ .unstructured = .{} }, }, diff --git a/src/glibc.zig b/src/glibc.zig index d719dab491f9..00d76f32fb0d 100644 --- a/src/glibc.zig +++ b/src/glibc.zig @@ -12,7 +12,7 @@ const Compilation = @import("Compilation.zig"); const build_options = @import("build_options"); const trace = @import("tracy.zig").trace; const Cache = std.Build.Cache; -const Package = @import("Package.zig"); +const Module = @import("Package/Module.zig"); pub const Lib = struct { name: []const u8, @@ -170,7 +170,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const target = comp.getTarget(); + const target = comp.root_mod.resolved_target.result; const target_ver = target.os.version_range.linux.glibc; const start_old_init_fini = target_ver.order(.{ .major = 2, .minor = 33, .patch = 0 }) != .gt; @@ -196,12 +196,14 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "-DASSEMBLER", "-Wa,--noexecstack", }); - return comp.build_crt_file("crti", .Obj, .@"glibc crti.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try start_asm_path(comp, arena, "crti.S"), .cache_exempt_flags = args.items, + .owner = comp.root_mod, }, - }); + }; + return comp.build_crt_file("crti", .Obj, .@"glibc crti.o", prog_node, &files); }, .crtn_o => { var args = std.ArrayList([]const u8).init(arena); @@ -215,12 +217,14 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "-DASSEMBLER", "-Wa,--noexecstack", }); - return comp.build_crt_file("crtn", .Obj, .@"glibc crtn.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try start_asm_path(comp, arena, "crtn.S"), .cache_exempt_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("crtn", .Obj, .@"glibc crtn.o", prog_node, &files); }, .scrt1_o => { const start_o: Compilation.CSourceFile = blk: { @@ -244,6 +248,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr break :blk .{ .src_path = try start_asm_path(comp, arena, src_path), .cache_exempt_flags = args.items, + .owner = undefined, }; }; const abi_note_o: Compilation.CSourceFile = blk: { @@ -263,11 +268,11 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr break :blk .{ .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "abi-note.S"), .cache_exempt_flags = args.items, + .owner = undefined, }; }; - return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", prog_node, &.{ - start_o, abi_note_o, - }); + var files = [_]Compilation.CSourceFile{ start_o, abi_note_o }; + return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", prog_node, &files); }, .libc_nonshared_a => { const s = path.sep_str; @@ -364,6 +369,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr files_buf[files_index] = .{ .src_path = try lib_path(comp, arena, dep.path), .cache_exempt_flags = args.items, + .owner = undefined, }; files_index += 1; } @@ -1061,40 +1067,70 @@ fn buildSharedLib( const version: Version = .{ .major = lib.sover, .minor = 0, .patch = 0 }; const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?); const soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else basename; - const map_file_path = try path.join(arena, &[_][]const u8{ bin_directory.path.?, all_map_basename }); + const map_file_path = try path.join(arena, &.{ bin_directory.path.?, all_map_basename }); + + const optimize_mode = comp.compilerRtOptMode(); + const strip = comp.compilerRtStrip(); + const config = try Compilation.Config.resolve(.{ + .output_mode = .Lib, + .link_mode = .Dynamic, + .resolved_target = comp.root_mod.resolved_target, + .is_test = false, + .have_zcu = false, + .emit_bin = true, + .root_optimize_mode = optimize_mode, + .root_strip = strip, + .link_libc = false, + }); + + const root_mod = try Module.create(arena, .{ + .global_cache_directory = comp.global_cache_directory, + .paths = .{ + .root = .{ .root_dir = comp.zig_lib_directory }, + .root_src_path = "", + }, + .fully_qualified_name = "root", + .inherited = .{ + .resolved_target = comp.root_mod.resolved_target, + .strip = strip, + .stack_check = false, + .stack_protector = 0, + .sanitize_c = false, + .sanitize_thread = false, + .red_zone = comp.root_mod.red_zone, + .omit_frame_pointer = comp.root_mod.omit_frame_pointer, + .valgrind = false, + .optimize_mode = optimize_mode, + .structured_cfg = comp.root_mod.structured_cfg, + }, + .global = config, + .cc_argv = &.{}, + .parent = null, + .builtin_mod = null, + }); + const c_source_files = [1]Compilation.CSourceFile{ .{ - .src_path = try path.join(arena, &[_][]const u8{ bin_directory.path.?, asm_file_basename }), + .src_path = try path.join(arena, &.{ bin_directory.path.?, asm_file_basename }), + .owner = root_mod, }, }; - const sub_compilation = try Compilation.create(comp.gpa, .{ + + const sub_compilation = try Compilation.create(comp.gpa, arena, .{ .local_cache_directory = zig_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, - .cache_mode = .whole, - .target = comp.getTarget(), - .root_name = lib.name, - .main_mod = null, - .output_mode = .Lib, - .link_mode = .Dynamic, .thread_pool = comp.thread_pool, - .libc_installation = comp.bin_file.options.libc_installation, + .self_exe_path = comp.self_exe_path, + .cache_mode = .incremental, + .config = config, + .root_mod = root_mod, + .root_name = lib.name, + .libc_installation = comp.libc_installation, .emit_bin = emit_bin, - .optimize_mode = comp.compilerRtOptMode(), - .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, .emit_h = null, - .strip = comp.compilerRtStrip(), - .is_native_os = false, - .is_native_abi = false, - .self_exe_path = comp.self_exe_path, .verbose_cc = comp.verbose_cc, - .verbose_link = comp.bin_file.options.verbose_link, + .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, .verbose_llvm_ir = comp.verbose_llvm_ir, .verbose_llvm_bc = comp.verbose_llvm_bc, diff --git a/src/libc_installation.zig b/src/libc_installation.zig index 48b505d9359b..0901194dd83a 100644 --- a/src/libc_installation.zig +++ b/src/libc_installation.zig @@ -41,7 +41,7 @@ pub const LibCInstallation = struct { pub fn parse( allocator: Allocator, libc_file: []const u8, - target: std.zig.CrossTarget, + target: std.Target, ) !LibCInstallation { var self: LibCInstallation = .{}; @@ -95,24 +95,23 @@ pub const LibCInstallation = struct { return error.ParseError; } - const os_tag = target.getOsTag(); + const os_tag = target.os.tag; if (self.crt_dir == null and !target.isDarwin()) { log.err("crt_dir may not be empty for {s}\n", .{@tagName(os_tag)}); return error.ParseError; } - const abi = target.getAbi(); - if (self.msvc_lib_dir == null and target.isWindows() and abi == .msvc) { + if (self.msvc_lib_dir == null and os_tag == .windows and target.abi == .msvc) { log.err("msvc_lib_dir may not be empty for {s}-{s}\n", .{ @tagName(os_tag), - @tagName(abi), + @tagName(target.abi), }); return error.ParseError; } - if (self.kernel32_lib_dir == null and target.isWindows() and abi == .msvc) { + if (self.kernel32_lib_dir == null and os_tag == .windows and target.abi == .msvc) { log.err("kernel32_lib_dir may not be empty for {s}-{s}\n", .{ @tagName(os_tag), - @tagName(abi), + @tagName(target.abi), }); return error.ParseError; } diff --git a/src/libcxx.zig b/src/libcxx.zig index 19c1d25b8c78..8e28c2174d4a 100644 --- a/src/libcxx.zig +++ b/src/libcxx.zig @@ -6,6 +6,7 @@ const target_util = @import("target.zig"); const Compilation = @import("Compilation.zig"); const build_options = @import("build_options"); const trace = @import("tracy.zig").trace; +const Module = @import("Package/Module.zig"); pub const AbiVersion = enum(u2) { @"1" = 1, @@ -115,7 +116,7 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) !void { const root_name = "c++"; const output_mode = .Lib; const link_mode = .Static; - const target = comp.getTarget(); + const target = comp.root_mod.resolved_target.result; const basename = try std.zig.binNameAlloc(arena, .{ .root_name = root_name, .target = target, @@ -137,6 +138,50 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) !void { const abi_namespace_arg = try std.fmt.allocPrint(arena, "-D_LIBCPP_ABI_NAMESPACE=__{d}", .{ @intFromEnum(comp.libcxx_abi_version), }); + + const optimize_mode = comp.compilerRtOptMode(); + const strip = comp.compilerRtStrip(); + + const config = try Compilation.Config.resolve(.{ + .output_mode = output_mode, + .link_mode = link_mode, + .resolved_target = comp.root_mod.resolved_target, + .is_test = false, + .have_zcu = false, + .emit_bin = true, + .root_optimize_mode = optimize_mode, + .root_strip = strip, + .link_libc = true, + .lto = comp.config.lto, + }); + + const root_mod = try Module.create(arena, .{ + .global_cache_directory = comp.global_cache_directory, + .paths = .{ + .root = .{ .root_dir = comp.zig_lib_directory }, + .root_src_path = "", + }, + .fully_qualified_name = "root", + .inherited = .{ + .resolved_target = comp.root_mod.resolved_target, + .strip = strip, + .stack_check = false, + .stack_protector = 0, + .sanitize_c = false, + .sanitize_thread = comp.config.any_sanitize_thread, + .red_zone = comp.root_mod.red_zone, + .omit_frame_pointer = comp.root_mod.omit_frame_pointer, + .valgrind = false, + .optimize_mode = optimize_mode, + .structured_cfg = comp.root_mod.structured_cfg, + .pic = comp.root_mod.pic, + }, + .global = config, + .cc_argv = &.{}, + .parent = null, + .builtin_mod = null, + }); + var c_source_files = try std.ArrayList(Compilation.CSourceFile).initCapacity(arena, libcxx_files.len); for (libcxx_files) |cxx_src| { @@ -154,7 +199,7 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) !void { continue; if (std.mem.startsWith(u8, cxx_src, "src/support/ibm/") and target.os.tag != .zos) continue; - if (comp.bin_file.options.single_threaded) { + if (!comp.config.any_non_single_threaded) { if (std.mem.startsWith(u8, cxx_src, "src/support/win32/thread_win32.cpp")) { continue; } @@ -223,49 +268,32 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) !void { .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", cxx_src }), .extra_flags = cflags.items, .cache_exempt_flags = cache_exempt_flags.items, + .owner = root_mod, }); } - const sub_compilation = try Compilation.create(comp.gpa, .{ + const sub_compilation = try Compilation.create(comp.gpa, arena, .{ .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, + .self_exe_path = comp.self_exe_path, .cache_mode = .whole, - .target = target, + .config = config, + .root_mod = root_mod, .root_name = root_name, - .main_mod = null, - .output_mode = output_mode, .thread_pool = comp.thread_pool, - .libc_installation = comp.bin_file.options.libc_installation, + .libc_installation = comp.libc_installation, .emit_bin = emit_bin, - .optimize_mode = comp.compilerRtOptMode(), - .link_mode = link_mode, - .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 = comp.bin_file.options.tsan, - .want_pic = comp.bin_file.options.pic, - .want_pie = null, - .want_lto = comp.bin_file.options.lto, - .function_sections = comp.bin_file.options.function_sections, .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, .c_source_files = c_source_files.items, .verbose_cc = comp.verbose_cc, - .verbose_link = comp.bin_file.options.verbose_link, + .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, .verbose_llvm_ir = comp.verbose_llvm_ir, .verbose_llvm_bc = comp.verbose_llvm_bc, .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, - .link_libc = true, .skip_linker_dependencies = true, }); defer sub_compilation.destroy(); @@ -273,12 +301,7 @@ pub fn buildLibCXX(comp: *Compilation, prog_node: *std.Progress.Node) !void { try comp.updateSubCompilation(sub_compilation, .libcxx, prog_node); assert(comp.libcxx_static_lib == null); - comp.libcxx_static_lib = Compilation.CRTFile{ - .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ - sub_compilation.bin_file.options.emit.?.sub_path, - }), - .lock = sub_compilation.bin_file.toOwnedLock(), - }; + comp.libcxx_static_lib = try sub_compilation.toCrtFile(); } pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) !void { @@ -296,7 +319,7 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) !void { const root_name = "c++abi"; const output_mode = .Lib; const link_mode = .Static; - const target = comp.getTarget(); + const target = comp.root_mod.resolved_target.result; const basename = try std.zig.binNameAlloc(arena, .{ .root_name = root_name, .target = target, @@ -318,6 +341,53 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) !void { const abi_namespace_arg = try std.fmt.allocPrint(arena, "-D_LIBCPP_ABI_NAMESPACE=__{d}", .{ @intFromEnum(comp.libcxx_abi_version), }); + + const optimize_mode = comp.compilerRtOptMode(); + const strip = comp.compilerRtStrip(); + const unwind_tables = true; + + const config = try Compilation.Config.resolve(.{ + .output_mode = output_mode, + .link_mode = link_mode, + .resolved_target = comp.root_mod.resolved_target, + .is_test = false, + .have_zcu = false, + .emit_bin = true, + .root_optimize_mode = optimize_mode, + .root_strip = strip, + .link_libc = true, + .any_unwind_tables = unwind_tables, + .lto = comp.config.lto, + }); + + const root_mod = try Module.create(arena, .{ + .global_cache_directory = comp.global_cache_directory, + .paths = .{ + .root = .{ .root_dir = comp.zig_lib_directory }, + .root_src_path = "", + }, + .fully_qualified_name = "root", + .inherited = .{ + .resolved_target = comp.root_mod.resolved_target, + .strip = strip, + .stack_check = false, + .stack_protector = 0, + .sanitize_c = false, + .sanitize_thread = comp.config.any_sanitize_thread, + .red_zone = comp.root_mod.red_zone, + .omit_frame_pointer = comp.root_mod.omit_frame_pointer, + .valgrind = false, + .optimize_mode = optimize_mode, + .structured_cfg = comp.root_mod.structured_cfg, + .unwind_tables = unwind_tables, + .pic = comp.root_mod.pic, + }, + .global = config, + .cc_argv = &.{}, + .parent = null, + .builtin_mod = null, + }); + var c_source_files = try std.ArrayList(Compilation.CSourceFile).initCapacity(arena, libcxxabi_files.len); for (libcxxabi_files) |cxxabi_src| { @@ -332,7 +402,7 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) !void { } // WASM targets are single threaded. - if (comp.bin_file.options.single_threaded) { + if (!comp.config.any_non_single_threaded) { if (std.mem.startsWith(u8, cxxabi_src, "src/cxa_thread_atexit.cpp")) { continue; } @@ -365,7 +435,6 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) !void { } try cflags.append("-nostdinc++"); try cflags.append("-fstrict-aliasing"); - try cflags.append("-funwind-tables"); try cflags.append("-std=c++20"); // These depend on only the zig lib directory file path, which is @@ -386,49 +455,32 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) !void { .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", cxxabi_src }), .extra_flags = cflags.items, .cache_exempt_flags = cache_exempt_flags.items, + .owner = root_mod, }); } - const sub_compilation = try Compilation.create(comp.gpa, .{ + const sub_compilation = try Compilation.create(comp.gpa, arena, .{ .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, + .self_exe_path = comp.self_exe_path, .cache_mode = .whole, - .target = target, + .config = config, + .root_mod = root_mod, .root_name = root_name, - .main_mod = null, - .output_mode = output_mode, .thread_pool = comp.thread_pool, - .libc_installation = comp.bin_file.options.libc_installation, + .libc_installation = comp.libc_installation, .emit_bin = emit_bin, - .optimize_mode = comp.compilerRtOptMode(), - .link_mode = link_mode, - .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 = comp.bin_file.options.tsan, - .want_pic = comp.bin_file.options.pic, - .want_pie = null, - .want_lto = comp.bin_file.options.lto, - .function_sections = comp.bin_file.options.function_sections, .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, .c_source_files = c_source_files.items, .verbose_cc = comp.verbose_cc, - .verbose_link = comp.bin_file.options.verbose_link, + .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, .verbose_llvm_ir = comp.verbose_llvm_ir, .verbose_llvm_bc = comp.verbose_llvm_bc, .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, - .link_libc = true, .skip_linker_dependencies = true, }); defer sub_compilation.destroy(); @@ -436,10 +488,5 @@ pub fn buildLibCXXABI(comp: *Compilation, prog_node: *std.Progress.Node) !void { try comp.updateSubCompilation(sub_compilation, .libcxxabi, prog_node); assert(comp.libcxxabi_static_lib == null); - comp.libcxxabi_static_lib = Compilation.CRTFile{ - .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ - sub_compilation.bin_file.options.emit.?.sub_path, - }), - .lock = sub_compilation.bin_file.toOwnedLock(), - }; + comp.libcxxabi_static_lib = try sub_compilation.toCrtFile(); } diff --git a/src/libtsan.zig b/src/libtsan.zig index 2489eb21afd3..14a7db088873 100644 --- a/src/libtsan.zig +++ b/src/libtsan.zig @@ -4,6 +4,7 @@ const assert = std.debug.assert; const Compilation = @import("Compilation.zig"); const build_options = @import("build_options"); const trace = @import("tracy.zig").trace; +const Module = @import("Package/Module.zig"); pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { if (!build_options.have_llvm) { @@ -33,6 +34,52 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { .basename = basename, }; + const optimize_mode = comp.compilerRtOptMode(); + const strip = comp.compilerRtStrip(); + + const config = try Compilation.Config.resolve(.{ + .output_mode = output_mode, + .link_mode = link_mode, + .resolved_target = comp.root_mod.resolved_target, + .is_test = false, + .have_zcu = false, + .emit_bin = true, + .root_optimize_mode = optimize_mode, + .root_strip = strip, + .link_libc = true, + }); + + const common_flags = [_][]const u8{ + "-DTSAN_CONTAINS_UBSAN=0", + }; + + const root_mod = try Module.create(arena, .{ + .global_cache_directory = comp.global_cache_directory, + .paths = .{ + .root = .{ .root_dir = comp.zig_lib_directory }, + .root_src_path = "", + }, + .fully_qualified_name = "root", + .inherited = .{ + .resolved_target = comp.root_mod.resolved_target, + .strip = strip, + .stack_check = false, + .stack_protector = 0, + .sanitize_c = false, + .sanitize_thread = false, + .red_zone = comp.root_mod.red_zone, + .omit_frame_pointer = comp.root_mod.omit_frame_pointer, + .valgrind = false, + .optimize_mode = optimize_mode, + .structured_cfg = comp.root_mod.structured_cfg, + .pic = true, + }, + .global = config, + .cc_argv = &common_flags, + .parent = null, + .builtin_mod = null, + }); + var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); try c_source_files.ensureUnusedCapacity(tsan_sources.len); @@ -49,8 +96,9 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { try cflags.append("-fno-rtti"); c_source_files.appendAssumeCapacity(.{ - .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "tsan", tsan_src }), + .src_path = try comp.zig_lib_directory.join(arena, &.{ "tsan", tsan_src }), .extra_flags = cflags.items, + .owner = root_mod, }); } @@ -73,6 +121,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { c_source_files.appendAssumeCapacity(.{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "tsan", tsan_src }), .extra_flags = cflags.items, + .owner = root_mod, }); } { @@ -93,6 +142,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { c_source_files.appendAssumeCapacity(.{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "tsan", asm_source }), .extra_flags = cflags.items, + .owner = root_mod, }); } @@ -116,10 +166,11 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { "tsan", "sanitizer_common", common_src, }), .extra_flags = cflags.items, + .owner = root_mod, }); } - const to_c_or_not_to_c_sources = if (comp.bin_file.options.link_libc) + const to_c_or_not_to_c_sources = if (comp.config.link_libc) &sanitizer_libcdep_sources else &sanitizer_nolibc_sources; @@ -140,6 +191,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { "tsan", "sanitizer_common", c_src, }), .extra_flags = cflags.items, + .owner = root_mod, }); } @@ -160,6 +212,7 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { "tsan", "sanitizer_common", c_src, }), .extra_flags = cflags.items, + .owner = root_mod, }); } @@ -188,63 +241,40 @@ pub fn buildTsan(comp: *Compilation, prog_node: *std.Progress.Node) !void { "tsan", "interception", c_src, }), .extra_flags = cflags.items, + .owner = root_mod, }); } - const common_flags = [_][]const u8{ - "-DTSAN_CONTAINS_UBSAN=0", - }; - - const sub_compilation = try Compilation.create(comp.gpa, .{ + const sub_compilation = try Compilation.create(comp.gpa, arena, .{ .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, + .thread_pool = comp.thread_pool, + .self_exe_path = comp.self_exe_path, .cache_mode = .whole, - .target = target, + .config = config, + .root_mod = root_mod, .root_name = root_name, - .main_mod = null, - .output_mode = output_mode, - .thread_pool = comp.thread_pool, - .libc_installation = comp.bin_file.options.libc_installation, + .libc_installation = comp.libc_installation, .emit_bin = emit_bin, - .optimize_mode = comp.compilerRtOptMode(), - .link_mode = link_mode, - .want_sanitize_c = false, - .want_stack_check = false, - .want_stack_protector = 0, - .want_valgrind = false, - .want_tsan = false, - .want_pic = true, - .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, .c_source_files = c_source_files.items, .verbose_cc = comp.verbose_cc, - .verbose_link = comp.bin_file.options.verbose_link, + .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, .verbose_llvm_ir = comp.verbose_llvm_ir, .verbose_llvm_bc = comp.verbose_llvm_bc, .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, - .link_libc = true, .skip_linker_dependencies = true, - .clang_argv = &common_flags, }); defer sub_compilation.destroy(); try comp.updateSubCompilation(sub_compilation, .libtsan, prog_node); assert(comp.tsan_static_lib == null); - comp.tsan_static_lib = Compilation.CRTFile{ - .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ - sub_compilation.bin_file.options.emit.?.sub_path, - }), - .lock = sub_compilation.bin_file.toOwnedLock(), - }; + comp.tsan_static_lib = try sub_compilation.toCrtFile(); } const tsan_sources = [_][]const u8{ diff --git a/src/libunwind.zig b/src/libunwind.zig index f0330a4e5157..5f0613c3619a 100644 --- a/src/libunwind.zig +++ b/src/libunwind.zig @@ -4,6 +4,7 @@ const assert = std.debug.assert; const target_util = @import("target.zig"); const Compilation = @import("Compilation.zig"); +const Module = @import("Package/Module.zig"); const build_options = @import("build_options"); const trace = @import("tracy.zig").trace; @@ -19,10 +20,49 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void { defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const root_name = "unwind"; const output_mode = .Lib; + const config = try Compilation.Config.resolve(.{ + .output_mode = .Lib, + .resolved_target = comp.root_mod.resolved_target, + .is_test = false, + .have_zcu = false, + .emit_bin = true, + .root_optimize_mode = comp.compilerRtOptMode(), + .root_strip = comp.compilerRtStrip(), + .link_libc = true, + // Disable LTO to avoid https://github.com/llvm/llvm-project/issues/56825 + .lto = false, + }); + const root_mod = try Module.create(arena, .{ + .global_cache_directory = comp.global_cache_directory, + .paths = .{ + .root = .{ .root_dir = comp.zig_lib_directory }, + .root_src_path = "", + }, + .fully_qualified_name = "root", + .inherited = .{ + .resolved_target = comp.root_mod.resolved_target, + .strip = comp.compilerRtStrip(), + .stack_check = false, + .stack_protector = 0, + .red_zone = comp.root_mod.red_zone, + .omit_frame_pointer = comp.root_mod.omit_frame_pointer, + .valgrind = false, + .sanitize_c = false, + .sanitize_thread = false, + .unwind_tables = false, + .pic = comp.root_mod.pic, + .optimize_mode = comp.compilerRtOptMode(), + }, + .global = config, + .cc_argv = &.{}, + .parent = null, + .builtin_mod = null, + }); + + const root_name = "unwind"; const link_mode = .Static; - const target = comp.getTarget(); + const target = comp.root_mod.resolved_target.result; const basename = try std.zig.binNameAlloc(arena, .{ .root_name = root_name, .target = target, @@ -64,10 +104,10 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void { // defines will be correct. try cflags.append("-D_LIBUNWIND_IS_NATIVE_ONLY"); - if (comp.bin_file.options.optimize_mode == .Debug) { + if (comp.root_mod.optimize_mode == .Debug) { try cflags.append("-D_DEBUG"); } - if (comp.bin_file.options.single_threaded) { + if (!comp.config.any_non_single_threaded) { try cflags.append("-D_LIBUNWIND_HAS_NO_THREADS"); } if (target.cpu.arch.isARM() and target.abi.floatAbi() == .hard) { @@ -80,49 +120,32 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void { c_source_files[i] = .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{unwind_src}), .extra_flags = cflags.items, + .owner = root_mod, }; } - const sub_compilation = try Compilation.create(comp.gpa, .{ + const sub_compilation = try Compilation.create(comp.gpa, arena, .{ + .self_exe_path = comp.self_exe_path, .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, .zig_lib_directory = comp.zig_lib_directory, + .config = config, + .root_mod = root_mod, .cache_mode = .whole, - .target = target, .root_name = root_name, .main_mod = null, - .output_mode = output_mode, .thread_pool = comp.thread_pool, - .libc_installation = comp.bin_file.options.libc_installation, + .libc_installation = comp.libc_installation, .emit_bin = emit_bin, - .optimize_mode = comp.compilerRtOptMode(), - .link_mode = link_mode, - .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_pic = comp.bin_file.options.pic, - .want_pie = null, - // Disable LTO to avoid https://github.com/llvm/llvm-project/issues/56825 - .want_lto = false, - .function_sections = comp.bin_file.options.function_sections, - .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, + .function_sections = comp.function_sections, .c_source_files = &c_source_files, .verbose_cc = comp.verbose_cc, - .verbose_link = comp.bin_file.options.verbose_link, + .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, .verbose_llvm_ir = comp.verbose_llvm_ir, .verbose_llvm_bc = comp.verbose_llvm_bc, .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, - .link_libc = true, .skip_linker_dependencies = true, }); defer sub_compilation.destroy(); @@ -130,13 +153,7 @@ pub fn buildStaticLib(comp: *Compilation, prog_node: *std.Progress.Node) !void { try comp.updateSubCompilation(sub_compilation, .libunwind, prog_node); assert(comp.libunwind_static_lib == null); - - comp.libunwind_static_lib = Compilation.CRTFile{ - .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ - sub_compilation.bin_file.options.emit.?.sub_path, - }), - .lock = sub_compilation.bin_file.toOwnedLock(), - }; + comp.libunwind_static_lib = try sub_compilation.toCrtFile(); } const unwind_src_list = [_][]const u8{ diff --git a/src/link.zig b/src/link.zig index 12b9c58d9bb1..5c296b49abb5 100644 --- a/src/link.zig +++ b/src/link.zig @@ -10,7 +10,6 @@ const wasi_libc = @import("wasi_libc.zig"); const Air = @import("Air.zig"); const Allocator = std.mem.Allocator; -const BuildId = std.Build.CompileStep.BuildId; const Cache = std.Build.Cache; const Compilation = @import("Compilation.zig"); const LibCInstallation = @import("libc_installation.zig").LibCInstallation; @@ -19,6 +18,7 @@ const Module = @import("Module.zig"); const InternPool = @import("InternPool.zig"); const Type = @import("type.zig").Type; const TypedValue = @import("TypedValue.zig"); +const LlvmObject = @import("codegen/llvm.zig").Object; /// When adding a new field, remember to update `hashAddSystemLibs`. /// These are *always* dynamically linked. Static libraries will be @@ -33,17 +33,6 @@ pub const SystemLib = struct { path: ?[]const u8, }; -/// When adding a new field, remember to update `hashAddFrameworks`. -pub const Framework = struct { - needed: bool = false, - weak: bool = false, - path: []const u8, -}; - -pub const SortSection = enum { name, alignment }; - -pub const CacheMode = enum { incremental, whole }; - pub fn hashAddSystemLibs( man: *Cache.Manifest, hm: std.StringArrayHashMapUnmanaged(SystemLib), @@ -57,355 +46,172 @@ pub fn hashAddSystemLibs( } } -pub fn hashAddFrameworks(man: *Cache.Manifest, hm: []const Framework) !void { - for (hm) |value| { - man.hash.add(value.needed); - man.hash.add(value.weak); - _ = try man.addFile(value.path, null); - } -} - 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, - dynamic_linker: ?[]const u8, - /// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin) - sysroot: ?[]const u8, - /// Used for calculating how much space to reserve for symbols in case the binary file - /// does not already have a symbol table. - symbol_count_hint: u64 = 32, - /// Used for calculating how much space to reserve for executable program code in case - /// the binary file does not already have such a section. - program_code_size_hint: u64 = 256 * 1024, - entry_addr: ?u64 = null, - entry: ?[]const u8, - stack_size_override: ?u64, - image_base_override: ?u64, - /// 0 means no stack protector - /// other value means stack protector with that buffer size. - stack_protector: u32, - cache_mode: CacheMode, - include_compiler_rt: bool, - /// Set to `true` to omit debug info. - strip: bool, - /// If this is true then this link code is responsible for outputting an object - /// file and then using LLD to link it together with the link options and other objects. - /// Otherwise (depending on `use_llvm`) this link code directly outputs and updates the final binary. - use_lld: bool, - /// If this is true then this link code is responsible for making an LLVM IR Module, - /// outputting it to an object file, and then linking that together with link options and - /// other objects. - /// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary. - use_llvm: bool, - use_lib_llvm: bool, - link_libc: bool, - link_libcpp: bool, - link_libunwind: bool, - darwin_sdk_layout: ?DarwinSdkLayout, - function_sections: bool, - data_sections: bool, - no_builtin: bool, - eh_frame_hdr: bool, - emit_relocs: bool, - rdynamic: bool, - z_nodelete: bool, - z_notext: bool, - z_defs: bool, - z_origin: bool, - z_nocopyreloc: bool, - z_now: bool, - z_relro: bool, - z_common_page_size: ?u64, - z_max_page_size: ?u64, - tsaware: bool, - nxcompat: bool, - dynamicbase: bool, - linker_optimization: u8, - compress_debug_sections: CompressDebugSections, - bind_global_refs_locally: bool, - import_memory: bool, - export_memory: bool, - import_symbols: bool, - import_table: bool, - export_table: bool, - initial_memory: ?u64, - max_memory: ?u64, - shared_memory: bool, - export_symbol_names: []const []const u8, - global_base: ?u64, - is_native_os: bool, - is_native_abi: bool, - pic: bool, - pie: bool, - lto: bool, - valgrind: bool, - tsan: bool, - stack_check: bool, - red_zone: bool, - omit_frame_pointer: bool, - single_threaded: bool, - verbose_link: bool, - dll_export_fns: bool, - error_return_tracing: bool, - skip_linker_dependencies: bool, - each_lib_rpath: bool, - build_id: 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, + zcu_object_sub_path: ?[]const u8 = null, + disable_lld_caching: bool, + gc_sections: bool, + print_gc_sections: bool, + build_id: std.zig.BuildId, + rpath_list: []const []const u8, + allow_shlib_undefined: bool, + stack_size: u64, /// Prevents other processes from clobbering files in the output directory /// of this linking operation. lock: ?Cache.Lock = null, - child_pid: ?std.ChildProcess.Id = null, + pub const OpenOptions = struct { + symbol_count_hint: u64 = 32, + program_code_size_hint: u64 = 256 * 1024, + + /// This may depend on what symbols are found during the linking process. + entry: Entry, + /// Virtual address of the entry point procedure relative to image base. + entry_addr: ?u64, + stack_size: ?u64, + image_base: ?u64, + emit_relocs: 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, + compress_debug_sections: Elf.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, + each_lib_rpath: bool, + build_id: std.zig.BuildId, + disable_lld_caching: bool, + hash_style: Elf.HashStyle, + sort_section: ?Elf.SortSection, + major_subsystem_version: ?u16, + minor_subsystem_version: ?u16, + gc_sections: ?bool, + allow_shlib_undefined: ?bool, + subsystem: ?std.Target.SubSystem, + linker_script: ?[]const u8, + version_script: ?[]const u8, + soname: ?[]const u8, + print_gc_sections: bool, + print_icf_sections: bool, + print_map: bool, + + /// 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, + + // 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, + frameworks: []const MachO.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, + + pub const Entry = union(enum) { + default, + disabled, + enabled, + named: []const u8, + }; + }; + /// 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, - }; + /// `arena` is used for allocations with the same lifetime as the created File. + pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: OpenOptions, + ) !*File { + const tag = Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt); + switch (tag) { + .c => { + const ptr = try C.open(arena, comp, emit, options); + return &ptr.base; + }, + inline else => |t| { + if (build_options.only_c) unreachable; + const ptr = try t.Type().open(arena, comp, emit, options); + return &ptr.base; + }, } - 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; + pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: OpenOptions, + ) !*File { + const tag = Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt); + switch (tag) { + .c => { + const ptr = try C.createEmpty(arena, comp, emit, options); + return &ptr.base; + }, + inline else => |t| { + if (build_options.only_c) unreachable; + const ptr = try t.Type().createEmpty(arena, comp, emit, options); + return &ptr.base; + }, } - - return file; } pub fn cast(base: *File, comptime T: type) ?*T { @@ -416,11 +222,13 @@ pub const File = struct { } pub fn makeWritable(base: *File) !void { + const comp = base.comp; + const gpa = comp.gpa; switch (base.tag) { .coff, .elf, .macho, .plan9, .wasm => { if (build_options.only_c) unreachable; if (base.file != null) return; - const emit = base.options.emit orelse return; + const emit = base.emit; if (base.child_pid) |pid| { if (builtin.os.tag == .windows) { base.cast(Coff).?.ptraceAttach(pid) catch |err| { @@ -431,9 +239,10 @@ pub const File = struct { // it will return ETXTBSY. So instead, we copy the file, atomically rename it // over top of the exe path, and then proceed normally. This changes the inode, // avoiding the error. - const tmp_sub_path = try std.fmt.allocPrint(base.allocator, "{s}-{x}", .{ + const tmp_sub_path = try std.fmt.allocPrint(gpa, "{s}-{x}", .{ emit.sub_path, std.crypto.random.int(u32), }); + defer gpa.free(tmp_sub_path); try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, tmp_sub_path, .{}); try emit.directory.handle.rename(tmp_sub_path, emit.sub_path); switch (builtin.os.tag) { @@ -448,10 +257,13 @@ pub const File = struct { } } } + const use_lld = build_options.have_llvm and comp.config.use_lld; + const output_mode = comp.config.output_mode; + const link_mode = comp.config.link_mode; base.file = try emit.directory.handle.createFile(emit.sub_path, .{ .truncate = false, .read = true, - .mode = determineMode(base.options), + .mode = determineMode(use_lld, output_mode, link_mode), }); }, .c, .spirv, .nvptx => {}, @@ -459,9 +271,14 @@ pub const File = struct { } pub fn makeExecutable(base: *File) !void { - switch (base.options.output_mode) { + const comp = base.comp; + const output_mode = comp.config.output_mode; + const link_mode = comp.config.link_mode; + const use_lld = build_options.have_llvm and comp.config.use_lld; + + switch (output_mode) { .Obj => return, - .Lib => switch (base.options.link_mode) { + .Lib => switch (link_mode) { .Static => return, .Dynamic => {}, }, @@ -470,8 +287,7 @@ pub const File = struct { switch (base.tag) { .elf => if (base.file) |f| { if (build_options.only_c) unreachable; - const use_lld = build_options.have_llvm and base.options.use_lld; - if (base.intermediary_basename != null and use_lld) { + if (base.zcu_object_sub_path != null and use_lld) { // The file we have open is not the final file that we want to // make executable, so we don't have to close it. return; @@ -490,7 +306,7 @@ pub const File = struct { }, .coff, .macho, .plan9, .wasm => if (base.file) |f| { if (build_options.only_c) unreachable; - if (base.intermediary_basename != null) { + if (base.zcu_object_sub_path != null) { // The file we have open is not the final file that we want to // make executable, so we don't have to close it. return; @@ -555,16 +371,12 @@ pub const File = struct { pub fn lowerUnnamedConst(base: *File, tv: TypedValue, decl_index: InternPool.DeclIndex) UpdateDeclError!u32 { if (build_options.only_c) @compileError("unreachable"); switch (base.tag) { - // zig fmt: off - .coff => return @fieldParentPtr(Coff, "base", base).lowerUnnamedConst(tv, decl_index), - .elf => return @fieldParentPtr(Elf, "base", base).lowerUnnamedConst(tv, decl_index), - .macho => return @fieldParentPtr(MachO, "base", base).lowerUnnamedConst(tv, decl_index), - .plan9 => return @fieldParentPtr(Plan9, "base", base).lowerUnnamedConst(tv, decl_index), .spirv => unreachable, - .c => unreachable, - .wasm => return @fieldParentPtr(Wasm, "base", base).lowerUnnamedConst(tv, decl_index), + .c => unreachable, .nvptx => unreachable, - // zig fmt: on + inline else => |t| { + return @fieldParentPtr(t.Type(), "base", base).lowerUnnamedConst(tv, decl_index); + }, } } @@ -577,16 +389,13 @@ pub const File = struct { if (build_options.only_c) @compileError("unreachable"); log.debug("getGlobalSymbol '{s}' (expected in '{?s}')", .{ name, lib_name }); switch (base.tag) { - // zig fmt: off - .coff => return @fieldParentPtr(Coff, "base", base).getGlobalSymbol(name, lib_name), - .elf => return @fieldParentPtr(Elf, "base", base).getGlobalSymbol(name, lib_name), - .macho => return @fieldParentPtr(MachO, "base", base).getGlobalSymbol(name, lib_name), .plan9 => unreachable, .spirv => unreachable, - .c => unreachable, - .wasm => return @fieldParentPtr(Wasm, "base", base).getGlobalSymbol(name, lib_name), + .c => unreachable, .nvptx => unreachable, - // zig fmt: on + inline else => |t| { + return @fieldParentPtr(t.Type(), "base", base).getGlobalSymbol(name, lib_name); + }, } } @@ -594,59 +403,48 @@ pub const File = struct { pub fn updateDecl(base: *File, module: *Module, decl_index: InternPool.DeclIndex) UpdateDeclError!void { const decl = module.declPtr(decl_index); assert(decl.has_tv); - if (build_options.only_c) { - assert(base.tag == .c); - return @fieldParentPtr(C, "base", base).updateDecl(module, decl_index); - } switch (base.tag) { - // zig fmt: off - .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl_index), - .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl_index), - .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl_index), - .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl_index), - .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl_index), - .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl_index), - .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDecl(module, decl_index), - .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDecl(module, decl_index), - // zig fmt: on + .c => { + return @fieldParentPtr(C, "base", base).updateDecl(module, decl_index); + }, + inline else => |tag| { + if (build_options.only_c) unreachable; + return @fieldParentPtr(tag.Type(), "base", base).updateDecl(module, decl_index); + }, } } /// May be called before or after updateExports for any given Decl. - pub fn updateFunc(base: *File, module: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) UpdateDeclError!void { - if (build_options.only_c) { - assert(base.tag == .c); - return @fieldParentPtr(C, "base", base).updateFunc(module, func_index, air, liveness); - } + pub fn updateFunc( + base: *File, + module: *Module, + func_index: InternPool.Index, + air: Air, + liveness: Liveness, + ) UpdateDeclError!void { switch (base.tag) { - // zig fmt: off - .coff => return @fieldParentPtr(Coff, "base", base).updateFunc(module, func_index, air, liveness), - .elf => return @fieldParentPtr(Elf, "base", base).updateFunc(module, func_index, air, liveness), - .macho => return @fieldParentPtr(MachO, "base", base).updateFunc(module, func_index, air, liveness), - .c => return @fieldParentPtr(C, "base", base).updateFunc(module, func_index, air, liveness), - .wasm => return @fieldParentPtr(Wasm, "base", base).updateFunc(module, func_index, air, liveness), - .spirv => return @fieldParentPtr(SpirV, "base", base).updateFunc(module, func_index, air, liveness), - .plan9 => return @fieldParentPtr(Plan9, "base", base).updateFunc(module, func_index, air, liveness), - .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateFunc(module, func_index, air, liveness), - // zig fmt: on + .c => { + return @fieldParentPtr(C, "base", base).updateFunc(module, func_index, air, liveness); + }, + inline else => |tag| { + if (build_options.only_c) unreachable; + return @fieldParentPtr(tag.Type(), "base", base).updateFunc(module, func_index, air, liveness); + }, } } pub fn updateDeclLineNumber(base: *File, module: *Module, decl_index: InternPool.DeclIndex) UpdateDeclError!void { const decl = module.declPtr(decl_index); assert(decl.has_tv); - if (build_options.only_c) { - assert(base.tag == .c); - return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index); - } switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl_index), - .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl_index), - .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl_index), - .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index), - .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl_index), - .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl_index), .spirv, .nvptx => {}, + .c => { + return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index); + }, + inline else => |tag| { + if (build_options.only_c) unreachable; + return @fieldParentPtr(tag.Type(), "base", base).updateDeclLineNumber(module, decl_index); + }, } } @@ -666,56 +464,12 @@ 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 => { + .c => @fieldParentPtr(C, "base", base).deinit(), + + inline else => |tag| { if (build_options.only_c) unreachable; - const parent = @fieldParentPtr(NvPtx, "base", base); - parent.deinit(); - base.allocator.destroy(parent); + @fieldParentPtr(tag.Type(), "base", base).deinit(); }, } } @@ -793,19 +547,22 @@ pub const File = struct { /// Commit pending changes and write headers. Takes into account final output mode /// and `use_lld`, not only `effectiveOutputMode`. - pub fn flush(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) FlushError!void { + /// `arena` has the lifetime of the call to `Compilation.update`. + pub fn flush(base: *File, arena: Allocator, prog_node: *std.Progress.Node) FlushError!void { if (build_options.only_c) { assert(base.tag == .c); - return @fieldParentPtr(C, "base", base).flush(comp, prog_node); + return @fieldParentPtr(C, "base", base).flush(arena, prog_node); } + const comp = base.comp; if (comp.clang_preprocessor_mode == .yes) { - const emit = base.options.emit orelse return; // -fno-emit-bin + const gpa = comp.gpa; + const emit = base.emit; // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case) // Until then, we do `lld -r -o output.o input.o` even though the output is the same // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file // to the final location. See also the corresponding TODO in Coff linking. - const full_out_path = try emit.directory.join(comp.gpa, &[_][]const u8{emit.sub_path}); - defer comp.gpa.free(full_out_path); + const full_out_path = try emit.directory.join(gpa, &[_][]const u8{emit.sub_path}); + defer gpa.free(full_out_path); assert(comp.c_object_table.count() == 1); const the_key = comp.c_object_table.keys()[0]; const cached_pp_file_path = the_key.status.success.object_path; @@ -813,75 +570,43 @@ pub const File = struct { return; } - const use_lld = build_options.have_llvm and base.options.use_lld; - if (use_lld and base.options.output_mode == .Lib and base.options.link_mode == .Static) { - return base.linkAsArchive(comp, prog_node); + const use_lld = build_options.have_llvm and comp.config.use_lld; + const output_mode = comp.config.output_mode; + const link_mode = comp.config.link_mode; + if (use_lld and output_mode == .Lib and link_mode == .Static) { + return base.linkAsArchive(arena, prog_node); } switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).flush(comp, prog_node), - .elf => return @fieldParentPtr(Elf, "base", base).flush(comp, prog_node), - .macho => return @fieldParentPtr(MachO, "base", base).flush(comp, prog_node), - .c => return @fieldParentPtr(C, "base", base).flush(comp, prog_node), - .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp, prog_node), - .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp, prog_node), - .plan9 => return @fieldParentPtr(Plan9, "base", base).flush(comp, prog_node), - .nvptx => return @fieldParentPtr(NvPtx, "base", base).flush(comp, prog_node), + inline else => |tag| { + return @fieldParentPtr(tag.Type(), "base", base).flush(arena, prog_node); + }, } } /// Commit pending changes and write headers. Works based on `effectiveOutputMode` /// rather than final output mode. - pub fn flushModule(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) FlushError!void { - if (build_options.only_c) { - assert(base.tag == .c); - return @fieldParentPtr(C, "base", base).flushModule(comp, prog_node); - } + pub fn flushModule(base: *File, arena: Allocator, prog_node: *std.Progress.Node) FlushError!void { switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).flushModule(comp, prog_node), - .elf => return @fieldParentPtr(Elf, "base", base).flushModule(comp, prog_node), - .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp, prog_node), - .c => return @fieldParentPtr(C, "base", base).flushModule(comp, prog_node), - .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp, prog_node), - .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp, prog_node), - .plan9 => return @fieldParentPtr(Plan9, "base", base).flushModule(comp, prog_node), - .nvptx => return @fieldParentPtr(NvPtx, "base", base).flushModule(comp, prog_node), + .c => { + return @fieldParentPtr(C, "base", base).flushModule(arena, prog_node); + }, + inline else => |tag| { + if (build_options.only_c) unreachable; + return @fieldParentPtr(tag.Type(), "base", base).flushModule(arena, prog_node); + }, } } /// Called when a Decl is deleted from the Module. pub fn freeDecl(base: *File, decl_index: InternPool.DeclIndex) void { - if (build_options.only_c) { - assert(base.tag == .c); - return @fieldParentPtr(C, "base", base).freeDecl(decl_index); - } - switch (base.tag) { - .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl_index), - .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl_index), - .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl_index), - .c => @fieldParentPtr(C, "base", base).freeDecl(decl_index), - .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl_index), - .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl_index), - .plan9 => @fieldParentPtr(Plan9, "base", base).freeDecl(decl_index), - .nvptx => @fieldParentPtr(NvPtx, "base", base).freeDecl(decl_index), - } - } - - pub fn errorFlags(base: *File) ErrorFlags { - switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).error_flags, - .elf => return @fieldParentPtr(Elf, "base", base).error_flags, - .macho => return @fieldParentPtr(MachO, "base", base).error_flags, - .plan9 => return @fieldParentPtr(Plan9, "base", base).error_flags, - .c => return .{ .no_entry_point_found = false }, - .wasm, .spirv, .nvptx => return ErrorFlags{}, - } - } - - pub fn miscErrors(base: *File) []const ErrorMsg { switch (base.tag) { - .elf => return @fieldParentPtr(Elf, "base", base).misc_errors.items, - .macho => return @fieldParentPtr(MachO, "base", base).misc_errors.items, - else => return &.{}, + .c => { + @fieldParentPtr(C, "base", base).freeDecl(decl_index); + }, + inline else => |tag| { + if (build_options.only_c) unreachable; + @fieldParentPtr(tag.Type(), "base", base).freeDecl(decl_index); + }, } } @@ -900,19 +625,14 @@ pub const File = struct { exported: Module.Exported, exports: []const *Module.Export, ) UpdateExportsError!void { - if (build_options.only_c) { - assert(base.tag == .c); - return @fieldParentPtr(C, "base", base).updateExports(module, exported, exports); - } switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).updateExports(module, exported, exports), - .elf => return @fieldParentPtr(Elf, "base", base).updateExports(module, exported, exports), - .macho => return @fieldParentPtr(MachO, "base", base).updateExports(module, exported, exports), - .c => return @fieldParentPtr(C, "base", base).updateExports(module, exported, exports), - .wasm => return @fieldParentPtr(Wasm, "base", base).updateExports(module, exported, exports), - .spirv => return @fieldParentPtr(SpirV, "base", base).updateExports(module, exported, exports), - .plan9 => return @fieldParentPtr(Plan9, "base", base).updateExports(module, exported, exports), - .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateExports(module, exported, exports), + .c => { + return @fieldParentPtr(C, "base", base).updateExports(module, exported, exports); + }, + inline else => |tag| { + if (build_options.only_c) unreachable; + return @fieldParentPtr(tag.Type(), "base", base).updateExports(module, exported, exports); + }, } } @@ -929,150 +649,91 @@ pub const File = struct { /// May be called before or after updateFunc/updateDecl therefore it is up to the linker to allocate /// the block/atom. pub fn getDeclVAddr(base: *File, decl_index: InternPool.DeclIndex, reloc_info: RelocInfo) !u64 { - if (build_options.only_c) unreachable; + if (build_options.only_c) @compileError("unreachable"); switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl_index, reloc_info), - .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl_index, reloc_info), - .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl_index, reloc_info), - .plan9 => return @fieldParentPtr(Plan9, "base", base).getDeclVAddr(decl_index, reloc_info), .c => unreachable, - .wasm => return @fieldParentPtr(Wasm, "base", base).getDeclVAddr(decl_index, reloc_info), .spirv => unreachable, .nvptx => unreachable, + inline else => |tag| { + return @fieldParentPtr(tag.Type(), "base", base).getDeclVAddr(decl_index, reloc_info); + }, } } pub const LowerResult = @import("codegen.zig").Result; - pub fn lowerAnonDecl(base: *File, decl_val: InternPool.Index, decl_align: InternPool.Alignment, src_loc: Module.SrcLoc) !LowerResult { - if (build_options.only_c) unreachable; + pub fn lowerAnonDecl( + base: *File, + decl_val: InternPool.Index, + decl_align: InternPool.Alignment, + src_loc: Module.SrcLoc, + ) !LowerResult { + if (build_options.only_c) @compileError("unreachable"); switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).lowerAnonDecl(decl_val, decl_align, src_loc), - .elf => return @fieldParentPtr(Elf, "base", base).lowerAnonDecl(decl_val, decl_align, src_loc), - .macho => return @fieldParentPtr(MachO, "base", base).lowerAnonDecl(decl_val, decl_align, src_loc), - .plan9 => return @fieldParentPtr(Plan9, "base", base).lowerAnonDecl(decl_val, src_loc), .c => unreachable, - .wasm => return @fieldParentPtr(Wasm, "base", base).lowerAnonDecl(decl_val, decl_align, src_loc), .spirv => unreachable, .nvptx => unreachable, + inline else => |tag| { + return @fieldParentPtr(tag.Type(), "base", base).lowerAnonDecl(decl_val, decl_align, src_loc); + }, } } pub fn getAnonDeclVAddr(base: *File, decl_val: InternPool.Index, reloc_info: RelocInfo) !u64 { - if (build_options.only_c) unreachable; + if (build_options.only_c) @compileError("unreachable"); switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).getAnonDeclVAddr(decl_val, reloc_info), - .elf => return @fieldParentPtr(Elf, "base", base).getAnonDeclVAddr(decl_val, reloc_info), - .macho => return @fieldParentPtr(MachO, "base", base).getAnonDeclVAddr(decl_val, reloc_info), - .plan9 => return @fieldParentPtr(Plan9, "base", base).getAnonDeclVAddr(decl_val, reloc_info), .c => unreachable, - .wasm => return @fieldParentPtr(Wasm, "base", base).getAnonDeclVAddr(decl_val, reloc_info), .spirv => unreachable, .nvptx => unreachable, + inline else => |tag| { + return @fieldParentPtr(tag.Type(), "base", base).getAnonDeclVAddr(decl_val, reloc_info); + }, } } - pub fn deleteDeclExport(base: *File, decl_index: InternPool.DeclIndex, name: InternPool.NullTerminatedString) !void { - if (build_options.only_c) unreachable; - switch (base.tag) { - .coff => return @fieldParentPtr(Coff, "base", base).deleteDeclExport(decl_index, name), - .elf => return @fieldParentPtr(Elf, "base", base).deleteDeclExport(decl_index, name), - .macho => return @fieldParentPtr(MachO, "base", base).deleteDeclExport(decl_index, name), - .plan9 => {}, - .c => {}, - .wasm => return @fieldParentPtr(Wasm, "base", base).deleteDeclExport(decl_index), - .spirv => {}, - .nvptx => {}, - } - } - - /// This function is called by the frontend before flush(). It communicates that - /// `options.bin_file.emit` directory needs to be renamed from - /// `[zig-cache]/tmp/[random]` to `[zig-cache]/o/[digest]`. - /// The frontend would like to simply perform a file system rename, however, - /// some linker backends care about the file paths of the objects they are linking. - /// So this function call tells linker backends to rename the paths of object files - /// to observe the new directory path. - /// Linker backends which do not have this requirement can fall back to the simple - /// implementation at the bottom of this function. - /// This function is only called when CacheMode is `whole`. - pub fn renameTmpIntoCache( + pub fn deleteDeclExport( base: *File, - cache_directory: Compilation.Directory, - tmp_dir_sub_path: []const u8, - o_sub_path: []const u8, + decl_index: InternPool.DeclIndex, + name: InternPool.NullTerminatedString, ) !void { - // So far, none of the linker backends need to respond to this event, however, - // it makes sense that they might want to. So we leave this mechanism here - // for now. Once the linker backends get more mature, if it turns out this - // is not needed we can refactor this into having the frontend do the rename - // directly, and remove this function from link.zig. - _ = base; - while (true) { - if (builtin.os.tag == .windows) { - // Work around windows `renameW` can't fail with `PathAlreadyExists` - // See https://github.com/ziglang/zig/issues/8362 - if (cache_directory.handle.access(o_sub_path, .{})) |_| { - try cache_directory.handle.deleteTree(o_sub_path); - continue; - } else |err| switch (err) { - error.FileNotFound => {}, - else => |e| return e, - } - std.fs.rename( - cache_directory.handle, - tmp_dir_sub_path, - cache_directory.handle, - o_sub_path, - ) catch |err| { - log.err("unable to rename cache dir {s} to {s}: {s}", .{ tmp_dir_sub_path, o_sub_path, @errorName(err) }); - return err; - }; - break; - } else { - std.fs.rename( - cache_directory.handle, - tmp_dir_sub_path, - cache_directory.handle, - o_sub_path, - ) catch |err| switch (err) { - error.PathAlreadyExists => { - try cache_directory.handle.deleteTree(o_sub_path); - continue; - }, - else => |e| return e, - }; - break; - } + if (build_options.only_c) @compileError("unreachable"); + switch (base.tag) { + .plan9, + .c, + .spirv, + .nvptx, + => {}, + + inline else => |tag| { + return @fieldParentPtr(tag.Type(), "base", base).deleteDeclExport(decl_index, name); + }, } } - pub fn linkAsArchive(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) FlushError!void { - const emit = base.options.emit orelse return; - + pub fn linkAsArchive(base: *File, arena: Allocator, prog_node: *std.Progress.Node) FlushError!void { const tracy = trace(@src()); defer tracy.end(); - var arena_allocator = std.heap.ArenaAllocator.init(base.allocator); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); + const comp = base.comp; + const gpa = comp.gpa; - const directory = emit.directory; // Just an alias to make it shorter to type. - const full_out_path = try directory.join(arena, &[_][]const u8{emit.sub_path}); + const directory = base.emit.directory; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); const full_out_path_z = try arena.dupeZ(u8, full_out_path); + const opt_zcu = comp.module; // If there is no Zig code to compile, then we should skip flushing the output file // because it will not be part of the linker line anyway. - const module_obj_path: ?[]const u8 = if (base.options.module != null) blk: { - try base.flushModule(comp, prog_node); + const zcu_obj_path: ?[]const u8 = if (opt_zcu != null) blk: { + try base.flushModule(arena, prog_node); const dirname = fs.path.dirname(full_out_path_z) orelse "."; - break :blk try fs.path.join(arena, &.{ dirname, base.intermediary_basename.? }); + break :blk try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? }); } else null; - log.debug("module_obj_path={s}", .{if (module_obj_path) |s| s else "(null)"}); + log.debug("zcu_obj_path={s}", .{if (zcu_obj_path) |s| s else "(null)"}); - const compiler_rt_path: ?[]const u8 = if (base.options.include_compiler_rt) + const compiler_rt_path: ?[]const u8 = if (comp.include_compiler_rt) comp.compiler_rt_obj.?.full_object_path else null; @@ -1084,17 +745,19 @@ pub const File = struct { const id_symlink_basename = "llvm-ar.id"; var man: Cache.Manifest = undefined; - defer if (!base.options.disable_lld_caching) man.deinit(); + defer if (!base.disable_lld_caching) man.deinit(); + + const objects = comp.objects; var digest: [Cache.hex_digest_len]u8 = undefined; - if (!base.options.disable_lld_caching) { + if (!base.disable_lld_caching) { man = comp.cache_parent.obtain(); // We are about to obtain this lock, so here we give other processes a chance first. base.releaseLock(); - for (base.options.objects) |obj| { + for (objects) |obj| { _ = try man.addFile(obj.path, null); man.hash.add(obj.must_link); man.hash.add(obj.loption); @@ -1107,7 +770,7 @@ pub const File = struct { _ = try man.addFile(key.status.success.res_path, null); } } - try man.addOptionalFile(module_obj_path); + try man.addOptionalFile(zcu_obj_path); try man.addOptionalFile(compiler_rt_path); // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. @@ -1137,11 +800,11 @@ pub const File = struct { } const win32_resource_table_len = if (build_options.only_core_functionality) 0 else comp.win32_resource_table.count(); - const num_object_files = base.options.objects.len + comp.c_object_table.count() + win32_resource_table_len + 2; - var object_files = try std.ArrayList([*:0]const u8).initCapacity(base.allocator, num_object_files); + const num_object_files = objects.len + comp.c_object_table.count() + win32_resource_table_len + 2; + var object_files = try std.ArrayList([*:0]const u8).initCapacity(gpa, num_object_files); defer object_files.deinit(); - for (base.options.objects) |obj| { + for (objects) |obj| { object_files.appendAssumeCapacity(try arena.dupeZ(u8, obj.path)); } for (comp.c_object_table.keys()) |key| { @@ -1152,14 +815,14 @@ pub const File = struct { object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path)); } } - if (module_obj_path) |p| { + if (zcu_obj_path) |p| { object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); } if (compiler_rt_path) |p| { object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); } - if (base.options.verbose_link) { + if (comp.verbose_link) { std.debug.print("ar rcs {s}", .{full_out_path_z}); for (object_files.items) |arg| { std.debug.print(" {s}", .{arg}); @@ -1170,12 +833,13 @@ pub const File = struct { const llvm_bindings = @import("codegen/llvm/bindings.zig"); const Builder = @import("codegen/llvm/Builder.zig"); const llvm = @import("codegen/llvm.zig"); - Builder.initializeLLVMTarget(base.options.target.cpu.arch); - const os_tag = llvm.targetOs(base.options.target.os.tag); + const target = comp.root_mod.resolved_target.result; + Builder.initializeLLVMTarget(target.cpu.arch); + const os_tag = llvm.targetOs(target.os.tag); const bad = llvm_bindings.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_tag); if (bad) return error.UnableToWriteArchive; - if (!base.options.disable_lld_caching) { + if (!base.disable_lld_caching) { Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)}); }; @@ -1199,6 +863,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 { @@ -1237,6 +930,73 @@ 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 fn isStatic(self: File) bool { + return self.comp.config.link_mode == .Static; + } + + pub fn isObject(self: File) bool { + const output_mode = self.comp.config.output_mode; + return output_mode == .Obj; + } + + pub fn isExe(self: File) bool { + const output_mode = self.comp.config.output_mode; + return output_mode == .Exe; + } + + pub fn isStaticLib(self: File) bool { + const output_mode = self.comp.config.output_mode; + return output_mode == .Lib and self.isStatic(); + } + + pub fn isRelocatable(self: File) bool { + return self.isObject() or self.isStaticLib(); + } + + pub fn isDynLib(self: File) bool { + const output_mode = self.comp.config.output_mode; + return output_mode == .Lib and !self.isStatic(); + } + + pub fn emitLlvmObject( + base: File, + arena: Allocator, + llvm_object: *LlvmObject, + prog_node: *std.Progress.Node, + ) !void { + return base.comp.emitLlvmObject(arena, base.emit, .{ + .directory = null, + .basename = base.zcu_object_sub_path.?, + }, llvm_object, prog_node); + } + pub const C = @import("link/C.zig"); pub const Coff = @import("link/Coff.zig"); pub const Plan9 = @import("link/Plan9.zig"); @@ -1247,19 +1007,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/C.zig b/src/link/C.zig index 85d13bf7ec4c..68facb374b27 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -5,6 +5,7 @@ const Allocator = std.mem.Allocator; const fs = std.fs; const C = @This(); +const build_options = @import("build_options"); const Module = @import("../Module.zig"); const InternPool = @import("../InternPool.zig"); const Alignment = InternPool.Alignment; @@ -83,7 +84,8 @@ pub fn getString(this: C, s: String) []const u8 { } pub fn addString(this: *C, s: []const u8) Allocator.Error!String { - const gpa = this.base.allocator; + const comp = this.base.comp; + const gpa = comp.gpa; try this.string_bytes.appendSlice(gpa, s); return .{ .start = @intCast(this.string_bytes.items.len - s.len), @@ -91,28 +93,55 @@ pub fn addString(this: *C, s: []const u8) Allocator.Error!String { }; } -pub fn openPath(gpa: Allocator, sub_path: []const u8, options: link.Options) !*C { - assert(options.target.ofmt == .c); - - if (options.use_llvm) return error.LLVMHasNoCBackend; - if (options.use_lld) return error.LLDHasNoCBackend; +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*C { + return createEmpty(arena, comp, emit, options); +} - const file = try options.emit.?.directory.handle.createFile(sub_path, .{ +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*C { + const target = comp.root_mod.resolved_target.result; + assert(target.ofmt == .c); + const optimize_mode = comp.root_mod.optimize_mode; + const use_lld = build_options.have_llvm and comp.config.use_lld; + const use_llvm = comp.config.use_llvm; + const output_mode = comp.config.output_mode; + const link_mode = comp.config.link_mode; + + // These are caught by `Compilation.Config.resolve`. + assert(!use_lld); + assert(!use_llvm); + + const file = try emit.directory.handle.createFile(emit.sub_path, .{ // Truncation is done on `flush`. .truncate = false, - .mode = link.determineMode(options), + .mode = link.File.determineMode(use_lld, output_mode, link_mode), }); errdefer file.close(); - const c_file = try gpa.create(C); - errdefer gpa.destroy(c_file); + const c_file = try arena.create(C); c_file.* = .{ .base = .{ .tag = .c, - .options = options, + .comp = comp, + .emit = emit, + .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), + .print_gc_sections = options.print_gc_sections, + .stack_size = options.stack_size orelse 16777216, + .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = file, - .allocator = gpa, + .disable_lld_caching = options.disable_lld_caching, + .build_id = options.build_id, + .rpath_list = options.rpath_list, }, }; @@ -120,7 +149,7 @@ pub fn openPath(gpa: Allocator, sub_path: []const u8, options: link.Options) !*C } pub fn deinit(self: *C) void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; for (self.decl_table.values()) |*db| { db.deinit(gpa); @@ -141,7 +170,7 @@ pub fn deinit(self: *C) void { } pub fn freeDecl(self: *C, decl_index: InternPool.DeclIndex) void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; if (self.decl_table.fetchSwapRemove(decl_index)) |kv| { var decl_block = kv.value; decl_block.deinit(gpa); @@ -155,7 +184,7 @@ pub fn updateFunc( air: Air, liveness: Liveness, ) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const func = module.funcInfo(func_index); const decl_index = func.owner_decl; @@ -223,7 +252,7 @@ pub fn updateFunc( } fn updateAnonDecl(self: *C, module: *Module, i: usize) !void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const anon_decl = self.anon_decls.keys()[i]; const fwd_decl = &self.fwd_decl_buf; @@ -285,7 +314,7 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: InternPool.DeclIndex) ! const tracy = trace(@src()); defer tracy.end(); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const gop = try self.decl_table.getOrPut(gpa, decl_index); if (!gop.found_existing) { @@ -347,12 +376,13 @@ pub fn updateDeclLineNumber(self: *C, module: *Module, decl_index: InternPool.De _ = decl_index; } -pub fn flush(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void { - return self.flushModule(comp, prog_node); +pub fn flush(self: *C, arena: Allocator, prog_node: *std.Progress.Node) !void { + return self.flushModule(arena, prog_node); } fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) { - var defines = std.ArrayList(u8).init(self.base.allocator); + const gpa = self.base.comp.gpa; + var defines = std.ArrayList(u8).init(gpa); errdefer defines.deinit(); const writer = defines.writer(); switch (target.abi) { @@ -363,7 +393,9 @@ fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) { return defines; } -pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !void { +pub fn flushModule(self: *C, arena: Allocator, prog_node: *std.Progress.Node) !void { + _ = arena; // Has the same lifetime as the call to Compilation.update. + const tracy = trace(@src()); defer tracy.end(); @@ -371,8 +403,9 @@ pub fn flushModule(self: *C, _: *Compilation, prog_node: *std.Progress.Node) !vo sub_prog_node.activate(); defer sub_prog_node.end(); - const gpa = self.base.allocator; - const module = self.base.options.module.?; + const comp = self.base.comp; + const gpa = comp.gpa; + const module = self.base.comp.module.?; { var i: usize = 0; @@ -520,8 +553,8 @@ fn flushCTypes( pass: codegen.DeclGen.Pass, decl_ctypes: codegen.CType.Store, ) FlushDeclError!void { - const gpa = self.base.allocator; - const mod = self.base.options.module.?; + const gpa = self.base.comp.gpa; + const mod = self.base.comp.module.?; const decl_ctypes_len = decl_ctypes.count(); f.ctypes_map.clearRetainingCapacity(); @@ -601,7 +634,7 @@ fn flushCTypes( } fn flushErrDecls(self: *C, ctypes: *codegen.CType.Store) FlushDeclError!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const fwd_decl = &self.lazy_fwd_decl_buf; const code = &self.lazy_code_buf; @@ -609,7 +642,7 @@ fn flushErrDecls(self: *C, ctypes: *codegen.CType.Store) FlushDeclError!void { var object = codegen.Object{ .dg = .{ .gpa = gpa, - .module = self.base.options.module.?, + .module = self.base.comp.module.?, .error_msg = null, .pass = .flush, .is_naked_fn = false, @@ -643,7 +676,7 @@ fn flushLazyFn( ctypes: *codegen.CType.Store, lazy_fn: codegen.LazyFnMap.Entry, ) FlushDeclError!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const fwd_decl = &self.lazy_fwd_decl_buf; const code = &self.lazy_code_buf; @@ -651,7 +684,7 @@ fn flushLazyFn( var object = codegen.Object{ .dg = .{ .gpa = gpa, - .module = self.base.options.module.?, + .module = self.base.comp.module.?, .error_msg = null, .pass = .flush, .is_naked_fn = false, @@ -683,7 +716,7 @@ fn flushLazyFn( } fn flushLazyFns(self: *C, f: *Flush, lazy_fns: codegen.LazyFnMap) FlushDeclError!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; try f.lazy_fns.ensureUnusedCapacity(gpa, @intCast(lazy_fns.count())); var it = lazy_fns.iterator(); @@ -702,7 +735,7 @@ fn flushDeclBlock( export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void), extern_symbol_name: InternPool.OptionalNullTerminatedString, ) FlushDeclError!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; try self.flushLazyFns(f, decl_block.lazy_fns); try f.all_buffers.ensureUnusedCapacity(gpa, 1); fwd_decl: { diff --git a/src/link/Coff.zig b/src/link/Coff.zig index 5fbf02871adf..7d825ef4d14d 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -3,11 +3,24 @@ //! LLD for traditional linking (linking relocatable object files). //! LLD is also the default linker for LLVM. -/// If this is not null, an object file is created by LLVM and linked with LLD afterwards. +/// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path. llvm_object: ?*LlvmObject = null, base: link.File, -error_flags: link.File.ErrorFlags = .{}, +image_base: u64, +subsystem: ?std.Target.SubSystem, +tsaware: bool, +nxcompat: bool, +dynamicbase: bool, +/// TODO this and minor_subsystem_version should be combined into one property and left as +/// default or populated together. They should not be separate fields. +major_subsystem_version: u16, +minor_subsystem_version: u16, +lib_dirs: []const []const u8, +entry: link.File.OpenOptions.Entry, +entry_addr: ?u32, +module_definition_file: ?[]const u8, +pdb_out_path: ?[]const u8, ptr_width: PtrWidth, page_size: u32, @@ -48,9 +61,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,132 +236,112 @@ 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); - - if (options.use_llvm) { - return createEmpty(allocator, options); - } - - const self = try createEmpty(allocator, options); - errdefer self.base.destroy(); - - const file = try options.emit.?.directory.handle.createFile(sub_path, .{ - .truncate = false, - .read = true, - .mode = link.determineMode(options), - }); - self.base.file = file; - - try self.populateMissingMetadata(); - - return self; -} - -pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff { - const ptr_width: PtrWidth = switch (options.target.ptrBitWidth()) { +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*Coff { + const target = comp.root_mod.resolved_target.result; + assert(target.ofmt == .coff); + const optimize_mode = comp.root_mod.optimize_mode; + const output_mode = comp.config.output_mode; + const link_mode = comp.config.link_mode; + const use_llvm = comp.config.use_llvm; + const use_lld = build_options.have_llvm and comp.config.use_lld; + + 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); + + // If using LLD to link, this code should produce an object file so that it + // can be passed to LLD. + // If using LLVM to generate the object file for the zig compilation unit, + // we need a place to put the object file so that it can be subsequently + // handled. + const zcu_object_sub_path = if (!use_lld and !use_llvm) + null + else + try std.fmt.allocPrint(arena, "{s}.obj", .{emit.sub_path}); + + const self = try arena.create(Coff); self.* = .{ .base = .{ .tag = .coff, - .options = options, - .allocator = gpa, + .comp = comp, + .emit = emit, + .zcu_object_sub_path = zcu_object_sub_path, + .stack_size = options.stack_size orelse 16777216, + .gc_sections = options.gc_sections orelse (optimize_mode != .Debug), + .print_gc_sections = options.print_gc_sections, + .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, + .disable_lld_caching = options.disable_lld_caching, + .build_id = options.build_id, + .rpath_list = options.rpath_list, }, .ptr_width = ptr_width, .page_size = page_size, - .data_directories = comptime mem.zeroes([coff.IMAGE_NUMBEROF_DIRECTORY_ENTRIES]coff.ImageDataDirectory), - }; - - if (options.use_llvm) { - self.llvm_object = try LlvmObject.create(gpa, options); - } - return self; -} - -pub fn deinit(self: *Coff) void { - const gpa = self.base.allocator; - if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa); + .data_directories = [1]coff.ImageDataDirectory{.{ + .virtual_address = 0, + .size = 0, + }} ** coff.IMAGE_NUMBEROF_DIRECTORY_ENTRIES, - for (self.objects.items) |*object| { - object.deinit(gpa); - } - self.objects.deinit(gpa); - - for (self.sections.items(.free_list)) |*free_list| { - free_list.deinit(gpa); - } - self.sections.deinit(gpa); - - self.atoms.deinit(gpa); - self.locals.deinit(gpa); - self.globals.deinit(gpa); - - { - var it = self.resolver.keyIterator(); - while (it.next()) |key_ptr| { - gpa.free(key_ptr.*); - } - self.resolver.deinit(gpa); - } - - self.unresolved.deinit(gpa); - self.locals_free_list.deinit(gpa); - self.globals_free_list.deinit(gpa); - self.strtab.deinit(gpa); - self.temp_strtab.deinit(gpa); - self.got_table.deinit(gpa); - - for (self.import_tables.values()) |*itab| { - itab.deinit(gpa); - } - self.import_tables.deinit(gpa); - - self.lazy_syms.deinit(gpa); - - for (self.decls.values()) |*metadata| { - metadata.deinit(gpa); - } - self.decls.deinit(gpa); - - self.atom_by_index_table.deinit(gpa); - - for (self.unnamed_const_atoms.values()) |*atoms| { - atoms.deinit(gpa); - } - self.unnamed_const_atoms.deinit(gpa); + .image_base = options.image_base orelse switch (output_mode) { + .Exe => switch (target.cpu.arch) { + .aarch64 => 0x140000000, + .x86_64, .x86 => 0x400000, + else => unreachable, + }, + .Lib => 0x10000000, + .Obj => 0, + }, - { - var it = self.anon_decls.iterator(); - while (it.next()) |entry| { - entry.value_ptr.exports.deinit(gpa); - } - self.anon_decls.deinit(gpa); + // Subsystem depends on the set of public symbol names from linked objects. + // See LinkerDriver::inferSubsystem from the LLD project for the flow chart. + .subsystem = options.subsystem, + + .entry = options.entry, + + .tsaware = options.tsaware, + .nxcompat = options.nxcompat, + .dynamicbase = options.dynamicbase, + .major_subsystem_version = options.major_subsystem_version orelse 6, + .minor_subsystem_version = options.minor_subsystem_version orelse 0, + .lib_dirs = options.lib_dirs, + .entry_addr = math.cast(u32, options.entry_addr orelse 0) orelse + return error.EntryAddressTooBig, + .module_definition_file = options.module_definition_file, + .pdb_out_path = options.pdb_out_path, + }; + if (use_llvm and comp.config.have_zcu) { + self.llvm_object = try LlvmObject.create(arena, comp); } + errdefer self.base.destroy(); - for (self.relocs.values()) |*relocs| { - relocs.deinit(gpa); + if (use_lld and (use_llvm or !comp.config.have_zcu)) { + // LLVM emits the object file (if any); LLD links it into the final product. + return self; } - self.relocs.deinit(gpa); - for (self.base_relocs.values()) |*relocs| { - relocs.deinit(gpa); - } - self.base_relocs.deinit(gpa); -} + // What path should this COFF linker code output to? + // If using LLD to link, this code should produce an object file so that it + // can be passed to LLD. + const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; + self.base.file = try emit.directory.handle.createFile(sub_path, .{ + .truncate = true, + .read = true, + .mode = link.File.determineMode(use_lld, output_mode, link_mode), + }); -fn populateMissingMetadata(self: *Coff) !void { assert(self.llvm_object == null); - const gpa = self.base.allocator; + const gpa = comp.gpa; try self.strtab.buffer.ensureUnusedCapacity(gpa, @sizeOf(u32)); self.strtab.buffer.appendNTimesAssumeCapacity(0, @sizeOf(u32)); @@ -369,7 +359,7 @@ fn populateMissingMetadata(self: *Coff) !void { }); if (self.text_section_index == null) { - const file_size = @as(u32, @intCast(self.base.options.program_code_size_hint)); + 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, @@ -378,7 +368,7 @@ fn populateMissingMetadata(self: *Coff) !void { } if (self.got_section_index == null) { - const file_size = @as(u32, @intCast(self.base.options.symbol_count_hint)) * self.ptr_width.size(); + 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, @@ -403,7 +393,7 @@ fn populateMissingMetadata(self: *Coff) !void { } if (self.idata_section_index == null) { - const file_size = @as(u32, @intCast(self.base.options.symbol_count_hint)) * self.ptr_width.size(); + 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, @@ -411,7 +401,7 @@ fn populateMissingMetadata(self: *Coff) !void { } if (self.reloc_section_index == null) { - const file_size = @as(u32, @intCast(self.base.options.symbol_count_hint)) * @sizeOf(coff.BaseRelocation); + 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, @@ -438,6 +428,91 @@ fn populateMissingMetadata(self: *Coff) !void { } try self.base.file.?.pwriteAll(&[_]u8{0}, max_file_offset); } + + return self; +} + +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*Coff { + // TODO: restore saved linker state, don't truncate the file, and + // participate in incremental compilation. + return createEmpty(arena, comp, emit, options); +} + +pub fn deinit(self: *Coff) void { + const gpa = self.base.comp.gpa; + + if (self.llvm_object) |llvm_object| llvm_object.deinit(); + + for (self.objects.items) |*object| { + object.deinit(gpa); + } + self.objects.deinit(gpa); + + for (self.sections.items(.free_list)) |*free_list| { + free_list.deinit(gpa); + } + self.sections.deinit(gpa); + + self.atoms.deinit(gpa); + self.locals.deinit(gpa); + self.globals.deinit(gpa); + + { + var it = self.resolver.keyIterator(); + while (it.next()) |key_ptr| { + gpa.free(key_ptr.*); + } + self.resolver.deinit(gpa); + } + + self.unresolved.deinit(gpa); + self.locals_free_list.deinit(gpa); + self.globals_free_list.deinit(gpa); + self.strtab.deinit(gpa); + self.temp_strtab.deinit(gpa); + self.got_table.deinit(gpa); + + for (self.import_tables.values()) |*itab| { + itab.deinit(gpa); + } + self.import_tables.deinit(gpa); + + self.lazy_syms.deinit(gpa); + + for (self.decls.values()) |*metadata| { + metadata.deinit(gpa); + } + self.decls.deinit(gpa); + + self.atom_by_index_table.deinit(gpa); + + for (self.unnamed_const_atoms.values()) |*atoms| { + atoms.deinit(gpa); + } + self.unnamed_const_atoms.deinit(gpa); + + { + var it = self.anon_decls.iterator(); + while (it.next()) |entry| { + entry.value_ptr.exports.deinit(gpa); + } + self.anon_decls.deinit(gpa); + } + + for (self.relocs.values()) |*relocs| { + relocs.deinit(gpa); + } + self.relocs.deinit(gpa); + + for (self.base_relocs.values()) |*relocs| { + relocs.deinit(gpa); + } + self.base_relocs.deinit(gpa); } fn allocateSection(self: *Coff, name: []const u8, size: u32, flags: coff.SectionHeaderFlags) !u16 { @@ -471,8 +546,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 +730,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 +758,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 +782,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 +836,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 @@ -807,7 +884,7 @@ fn writeAtom(self: *Coff, atom_index: Atom.Index, code: []u8) !void { } } - self.resolveRelocs(atom_index, relocs.items, code, self.getImageBase()); + self.resolveRelocs(atom_index, relocs.items, code, self.image_base); try self.base.file.?.pwriteAll(code, file_offset); // Now we can mark the relocs as resolved. @@ -853,24 +930,24 @@ fn writeOffsetTableEntry(self: *Coff, index: usize) !void { const file_offset = header.pointer_to_raw_data + entry_offset; const vmaddr = header.virtual_address + entry_offset; - log.debug("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value + self.getImageBase() }); + log.debug("writing GOT entry {d}: @{x} => {x}", .{ index, vmaddr, entry_value + self.image_base }); switch (self.ptr_width) { .p32 => { var buf: [4]u8 = undefined; - mem.writeInt(u32, &buf, @as(u32, @intCast(entry_value + self.getImageBase())), .little); + mem.writeInt(u32, &buf, @as(u32, @intCast(entry_value + self.image_base)), .little); try self.base.file.?.pwriteAll(&buf, file_offset); }, .p64 => { var buf: [8]u8 = undefined; - mem.writeInt(u64, &buf, entry_value + self.getImageBase(), .little); + mem.writeInt(u64, &buf, entry_value + self.image_base, .little); try self.base.file.?.pwriteAll(&buf, file_offset); }, } 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 +1051,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 +1138,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,8 +1168,8 @@ 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 mod = self.base.options.module.?; + const gpa = self.base.comp.gpa; + const mod = self.base.comp.module.?; const decl = mod.declPtr(decl_index); const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index); if (!gop.found_existing) { @@ -1121,7 +1199,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 +1252,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 +1267,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,8 +1299,8 @@ fn updateLazySymbolAtom( atom_index: Atom.Index, section_index: u16, ) !void { - const gpa = self.base.allocator; - const mod = self.base.options.module.?; + const gpa = self.base.comp.gpa; + const mod = self.base.comp.module.?; var required_alignment: InternPool.Alignment = .none; var code_buffer = std.ArrayList(u8).init(gpa); @@ -1281,8 +1360,9 @@ fn updateLazySymbolAtom( } pub fn getOrCreateAtomForLazySymbol(self: *Coff, sym: link.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 mod = self.base.comp.module.?; + 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 +1385,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(), @@ -1317,9 +1398,9 @@ pub fn getOrCreateAtomForDecl(self: *Coff, decl_index: InternPool.DeclIndex) !At } fn getDeclOutputSection(self: *Coff, decl_index: InternPool.DeclIndex) u16 { - const decl = self.base.options.module.?.declPtr(decl_index); + const decl = self.base.comp.module.?.declPtr(decl_index); const ty = decl.ty; - const mod = self.base.options.module.?; + const mod = self.base.comp.module.?; const zig_ty = ty.zigTypeTag(mod); const val = decl.val; const index: u16 = blk: { @@ -1343,7 +1424,7 @@ fn getDeclOutputSection(self: *Coff, decl_index: InternPool.DeclIndex) u16 { } fn updateDeclCode(self: *Coff, decl_index: InternPool.DeclIndex, code: []u8, complex_type: coff.ComplexType) !void { - const mod = self.base.options.module.?; + const mod = self.base.comp.module.?; const decl = mod.declPtr(decl_index); const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); @@ -1401,7 +1482,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,7 +1493,8 @@ 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 mod = self.base.options.module.?; + const gpa = self.base.comp.gpa; + const mod = self.base.comp.module.?; const decl = mod.declPtr(decl_index); log.debug("freeDecl {*}", .{decl}); @@ -1421,7 +1503,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); } } @@ -1436,8 +1518,10 @@ pub fn updateExports( } const ip = &mod.intern_pool; + const comp = self.base.comp; + const target = comp.root_mod.resolved_target.result; - if (self.base.options.use_llvm) { + if (comp.config.use_llvm) { // Even in the case of LLVM, we need to notice certain exported symbols in order to // detect the default subsystem. for (exports) |exp| { @@ -1447,16 +1531,16 @@ pub fn updateExports( }; const exported_decl = mod.declPtr(exported_decl_index); if (exported_decl.getOwnedFunction(mod) == null) continue; - const winapi_cc = switch (self.base.options.target.cpu.arch) { + const winapi_cc = switch (target.cpu.arch) { .x86 => std.builtin.CallingConvention.Stdcall, else => std.builtin.CallingConvention.C, }; const decl_cc = exported_decl.ty.fnCallingConvention(mod); if (decl_cc == .C and ip.stringEqlSlice(exp.opts.name, "main") and - self.base.options.link_libc) + comp.config.link_libc) { mod.stage1_flags.have_c_main = true; - } else if (decl_cc == winapi_cc and self.base.options.target.os.tag == .windows) { + } else if (decl_cc == winapi_cc and target.os.tag == .windows) { if (ip.stringEqlSlice(exp.opts.name, "WinMain")) { mod.stage1_flags.have_winmain = true; } else if (ip.stringEqlSlice(exp.opts.name, "wWinMain")) { @@ -1474,9 +1558,7 @@ pub fn updateExports( if (self.llvm_object) |llvm_object| return llvm_object.updateExports(mod, exported, exports); - if (self.base.options.emit == null) return; - - const gpa = self.base.allocator; + const gpa = comp.gpa; const metadata = switch (exported) { .decl_index => |decl_index| blk: { @@ -1570,11 +1652,11 @@ pub fn deleteDeclExport( ) void { if (self.llvm_object) |_| return; const metadata = self.decls.getPtr(decl_index) orelse return; - const mod = self.base.options.module.?; + const mod = self.base.comp.module.?; 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 +1684,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); @@ -1624,38 +1706,35 @@ fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void { gop.value_ptr.* = current; } -pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { - if (self.base.options.emit == null) { - if (self.llvm_object) |llvm_object| { - return try llvm_object.flushModule(comp, prog_node); - } - return; - } - const use_lld = build_options.have_llvm and self.base.options.use_lld; +pub fn flush(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { + const comp = self.base.comp; + const use_lld = build_options.have_llvm and comp.config.use_lld; if (use_lld) { - return lld.linkWithLLD(self, comp, prog_node); + return lld.linkWithLLD(self, arena, prog_node); } - switch (self.base.options.output_mode) { - .Exe, .Obj => return self.flushModule(comp, prog_node), + switch (comp.config.output_mode) { + .Exe, .Obj => return self.flushModule(arena, prog_node), .Lib => return error.TODOImplementWritingLibFiles, } } -pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { +pub fn flushModule(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { const tracy = trace(@src()); defer tracy.end(); + const comp = self.base.comp; + const gpa = comp.gpa; + if (self.llvm_object) |llvm_object| { - return try llvm_object.flushModule(comp, prog_node); + try self.base.emitLlvmObject(arena, llvm_object, prog_node); + return; } var sub_prog_node = prog_node.start("COFF Flush", 0); sub_prog_node.activate(); defer sub_prog_node.end(); - const gpa = self.base.allocator; - - const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + const module = comp.module orelse return error.LinkingWithoutZigSourceUnimplemented; if (self.lazy_syms.getPtr(.none)) |metadata| { // Most lazy symbols can be updated on first use, but @@ -1756,12 +1835,12 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try self.writeDataDirectoriesHeaders(); try self.writeSectionHeaders(); - if (self.entry_addr == null and self.base.options.output_mode == .Exe) { + if (self.entry_addr == null and comp.config.output_mode == .Exe) { log.debug("flushing. no_entry_point_found = true\n", .{}); - self.error_flags.no_entry_point_found = true; + comp.link_error_flags.no_entry_point_found = true; } else { log.debug("flushing. no_entry_point_found = false\n", .{}); - self.error_flags.no_entry_point_found = false; + comp.link_error_flags.no_entry_point_found = false; try self.writeHeader(); } @@ -1794,8 +1873,8 @@ pub fn lowerAnonDecl( explicit_alignment: InternPool.Alignment, src_loc: Module.SrcLoc, ) !codegen.Result { - const gpa = self.base.allocator; - const mod = self.base.options.module.?; + const gpa = self.base.comp.gpa; + const mod = self.base.comp.module.?; const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val)); const decl_alignment = switch (explicit_alignment) { .none => ty.abiAlignment(mod), @@ -1868,7 +1947,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 +1974,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 +2085,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 +2233,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 +2256,8 @@ fn writeDataDirectoriesHeaders(self: *Coff) !void { } fn writeHeader(self: *Coff) !void { - const gpa = self.base.allocator; + const target = self.base.comp.root_mod.resolved_target.result; + const gpa = self.base.comp.gpa; var buffer = std.ArrayList(u8).init(gpa); defer buffer.deinit(); const writer = buffer.writer(); @@ -2194,14 +2275,14 @@ fn writeHeader(self: *Coff) !void { .p32 => flags.@"32BIT_MACHINE" = 1, .p64 => flags.LARGE_ADDRESS_AWARE = 1, } - if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Dynamic) { + if (self.base.comp.config.output_mode == .Lib and self.base.comp.config.link_mode == .Dynamic) { flags.DLL = 1; } const timestamp = std.time.timestamp(); const size_of_optional_header = @as(u16, @intCast(self.getOptionalHeaderSize() + self.getDataDirectoryHeadersSize())); var coff_header = coff.CoffHeader{ - .machine = coff.MachineType.fromTargetCpuArch(self.base.options.target.cpu.arch), + .machine = coff.MachineType.fromTargetCpuArch(target.cpu.arch), .number_of_sections = @as(u16, @intCast(self.sections.slice().len)), // TODO what if we prune a section .time_date_stamp = @as(u32, @truncate(@as(u64, @bitCast(timestamp)))), .pointer_to_symbol_table = self.strtab_offset orelse 0, @@ -2221,8 +2302,6 @@ fn writeHeader(self: *Coff) !void { const subsystem: coff.Subsystem = .WINDOWS_CUI; const size_of_image: u32 = self.getSizeOfImage(); const size_of_headers: u32 = mem.alignForward(u32, self.getSizeOfHeaders(), default_file_alignment); - const image_base = self.getImageBase(); - const base_of_code = self.sections.get(self.text_section_index.?).header.virtual_address; const base_of_data = self.sections.get(self.data_section_index.?).header.virtual_address; @@ -2253,15 +2332,15 @@ fn writeHeader(self: *Coff) !void { .address_of_entry_point = self.entry_addr orelse 0, .base_of_code = base_of_code, .base_of_data = base_of_data, - .image_base = @as(u32, @intCast(image_base)), + .image_base = @intCast(self.image_base), .section_alignment = self.page_size, .file_alignment = default_file_alignment, .major_operating_system_version = 6, .minor_operating_system_version = 0, .major_image_version = 0, .minor_image_version = 0, - .major_subsystem_version = 6, - .minor_subsystem_version = 0, + .major_subsystem_version = @intCast(self.major_subsystem_version), + .minor_subsystem_version = @intCast(self.minor_subsystem_version), .win32_version_value = 0, .size_of_image = size_of_image, .size_of_headers = size_of_headers, @@ -2273,7 +2352,7 @@ fn writeHeader(self: *Coff) !void { .size_of_heap_reserve = default_size_of_heap_reserve, .size_of_heap_commit = default_size_of_heap_commit, .loader_flags = 0, - .number_of_rva_and_sizes = @as(u32, @intCast(self.data_directories.len)), + .number_of_rva_and_sizes = @intCast(self.data_directories.len), }; writer.writeAll(mem.asBytes(&opt_header)) catch unreachable; }, @@ -2287,15 +2366,15 @@ fn writeHeader(self: *Coff) !void { .size_of_uninitialized_data = size_of_uninitialized_data, .address_of_entry_point = self.entry_addr orelse 0, .base_of_code = base_of_code, - .image_base = image_base, + .image_base = self.image_base, .section_alignment = self.page_size, .file_alignment = default_file_alignment, .major_operating_system_version = 6, .minor_operating_system_version = 0, .major_image_version = 0, .minor_image_version = 0, - .major_subsystem_version = 6, - .minor_subsystem_version = 0, + .major_subsystem_version = self.major_subsystem_version, + .minor_subsystem_version = self.minor_subsystem_version, .win32_version_value = 0, .size_of_image = size_of_image, .size_of_headers = size_of_headers, @@ -2307,7 +2386,7 @@ fn writeHeader(self: *Coff) !void { .size_of_heap_reserve = default_size_of_heap_reserve, .size_of_heap_commit = default_size_of_heap_commit, .loader_flags = 0, - .number_of_rva_and_sizes = @as(u32, @intCast(self.data_directories.len)), + .number_of_rva_and_sizes = @intCast(self.data_directories.len), }; writer.writeAll(mem.asBytes(&opt_header)) catch unreachable; }, @@ -2421,22 +2500,23 @@ inline fn getSizeOfImage(self: Coff) u32 { /// Returns symbol location corresponding to the set entrypoint (if any). pub fn getEntryPoint(self: Coff) ?SymbolWithLoc { - const entry_name = self.base.options.entry orelse "wWinMainCRTStartup"; // TODO this is incomplete - const global_index = self.resolver.get(entry_name) orelse return null; - return self.globals.items[global_index]; -} - -pub fn getImageBase(self: Coff) u64 { - const image_base: u64 = self.base.options.image_base_override orelse switch (self.base.options.output_mode) { - .Exe => switch (self.base.options.target.cpu.arch) { - .aarch64 => @as(u64, 0x140000000), - .x86_64, .x86 => 0x400000, - else => unreachable, // unsupported target architecture + const comp = self.base.comp; + + // TODO This is incomplete. + // The entry symbol name depends on the subsystem as well as the set of + // public symbol names from linked objects. + // See LinkerDriver::findDefaultEntry from the LLD project for the flow chart. + const entry_name = switch (self.entry) { + .disabled => return null, + .default => switch (comp.config.output_mode) { + .Exe => "wWinMainCRTStartup", + .Obj, .Lib => return null, }, - .Lib => 0x10000000, - .Obj => 0, + .enabled => "wWinMainCRTStartup", + .named => |name| name, }; - return image_base; + const global_index = self.resolver.get(entry_name) orelse return null; + return self.globals.items[global_index]; } /// Returns pointer-to-symbol described by `sym_loc` descriptor. @@ -2499,7 +2579,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 +2610,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 +2630,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/Coff/Atom.zig b/src/link/Coff/Atom.zig index 9e5557575541..4aa97fa6aced 100644 --- a/src/link/Coff/Atom.zig +++ b/src/link/Coff/Atom.zig @@ -94,7 +94,8 @@ pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool { } pub fn addRelocation(coff_file: *Coff, atom_index: Index, reloc: Relocation) !void { - const gpa = coff_file.base.allocator; + const comp = coff_file.base.comp; + const gpa = comp.gpa; log.debug(" (adding reloc of type {s} to target %{d})", .{ @tagName(reloc.type), reloc.target.sym_index }); const gop = try coff_file.relocs.getOrPut(gpa, atom_index); if (!gop.found_existing) { @@ -104,7 +105,8 @@ pub fn addRelocation(coff_file: *Coff, atom_index: Index, reloc: Relocation) !vo } pub fn addBaseRelocation(coff_file: *Coff, atom_index: Index, offset: u32) !void { - const gpa = coff_file.base.allocator; + const comp = coff_file.base.comp; + const gpa = comp.gpa; log.debug(" (adding base relocation at offset 0x{x} in %{d})", .{ offset, coff_file.getAtom(atom_index).getSymbolIndex().?, @@ -117,7 +119,8 @@ pub fn addBaseRelocation(coff_file: *Coff, atom_index: Index, offset: u32) !void } pub fn freeRelocations(coff_file: *Coff, atom_index: Index) void { - const gpa = coff_file.base.allocator; + const comp = coff_file.base.comp; + const gpa = comp.gpa; var removed_relocs = coff_file.relocs.fetchOrderedRemove(atom_index); if (removed_relocs) |*relocs| relocs.value.deinit(gpa); var removed_base_relocs = coff_file.base_relocs.fetchOrderedRemove(atom_index); diff --git a/src/link/Coff/Relocation.zig b/src/link/Coff/Relocation.zig index 84cfcdc18a88..b25427fedaa8 100644 --- a/src/link/Coff/Relocation.zig +++ b/src/link/Coff/Relocation.zig @@ -107,7 +107,8 @@ pub fn resolve(self: Relocation, atom_index: Atom.Index, code: []u8, image_base: .ptr_width = coff_file.ptr_width, }; - switch (coff_file.base.options.target.cpu.arch) { + const target = coff_file.base.comp.root_mod.resolved_target.result; + switch (target.cpu.arch) { .aarch64 => self.resolveAarch64(ctx), .x86, .x86_64 => self.resolveX86(ctx), else => unreachable, // unhandled target architecture diff --git a/src/link/Coff/lld.zig b/src/link/Coff/lld.zig index 573a85e167f9..914be5a4435a 100644 --- a/src/link/Coff/lld.zig +++ b/src/link/Coff/lld.zig @@ -17,26 +17,25 @@ const Allocator = mem.Allocator; const Coff = @import("../Coff.zig"); const Compilation = @import("../../Compilation.zig"); -pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void { +pub fn linkWithLLD(self: *Coff, arena: Allocator, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); - var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); + const comp = self.base.comp; + const gpa = comp.gpa; - 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 directory = self.base.emit.directory; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. - const module_obj_path: ?[]const u8 = if (self.base.options.module != null) blk: { - try self.flushModule(comp, prog_node); + const module_obj_path: ?[]const u8 = if (comp.module != null) blk: { + try self.flushModule(arena, prog_node); if (fs.path.dirname(full_out_path)) |dirname| { - break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? }); + break :blk try fs.path.join(arena, &.{ dirname, self.base.zcu_object_sub_path.? }); } else { - break :blk self.base.intermediary_basename.?; + break :blk self.base.zcu_object_sub_path.?; } } else null; @@ -45,27 +44,35 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod sub_prog_node.context.refresh(); defer sub_prog_node.end(); - const is_lib = self.base.options.output_mode == .Lib; - const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; - const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; - const link_in_crt = self.base.options.link_libc and is_exe_or_dyn_lib; - const target = self.base.options.target; + const is_lib = comp.config.output_mode == .Lib; + const is_dyn_lib = comp.config.link_mode == .Dynamic and is_lib; + const is_exe_or_dyn_lib = is_dyn_lib or comp.config.output_mode == .Exe; + const link_in_crt = comp.config.link_libc and is_exe_or_dyn_lib; + const target = comp.root_mod.resolved_target.result; + const optimize_mode = comp.root_mod.optimize_mode; + const entry_name: ?[]const u8 = switch (self.entry) { + // This logic isn't quite right for disabled or enabled. No point in fixing it + // when the goal is to eliminate dependency on LLD anyway. + // https://github.com/ziglang/zig/issues/17751 + .disabled, .default, .enabled => null, + .named => |name| name, + }; // See link/Elf.zig for comments on how this mechanism works. const id_symlink_basename = "lld.id"; var man: Cache.Manifest = undefined; - defer if (!self.base.options.disable_lld_caching) man.deinit(); + defer if (!self.base.disable_lld_caching) man.deinit(); var digest: [Cache.hex_digest_len]u8 = undefined; - if (!self.base.options.disable_lld_caching) { + if (!self.base.disable_lld_caching) { man = comp.cache_parent.obtain(); self.base.releaseLock(); comptime assert(Compilation.link_hash_implementation_version == 10); - for (self.base.options.objects) |obj| { + for (comp.objects) |obj| { _ = try man.addFile(obj.path, null); man.hash.add(obj.must_link); } @@ -78,14 +85,14 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } try man.addOptionalFile(module_obj_path); - man.hash.addOptionalBytes(self.base.options.entry); - man.hash.addOptional(self.base.options.stack_size_override); - man.hash.addOptional(self.base.options.image_base_override); - man.hash.addListOfBytes(self.base.options.lib_dirs); - man.hash.add(self.base.options.skip_linker_dependencies); - if (self.base.options.link_libc) { - man.hash.add(self.base.options.libc_installation != null); - if (self.base.options.libc_installation) |libc_installation| { + man.hash.addOptionalBytes(entry_name); + man.hash.add(self.base.stack_size); + man.hash.add(self.image_base); + man.hash.addListOfBytes(self.lib_dirs); + man.hash.add(comp.skip_linker_dependencies); + if (comp.config.link_libc) { + man.hash.add(comp.libc_installation != null); + if (comp.libc_installation) |libc_installation| { man.hash.addBytes(libc_installation.crt_dir.?); if (target.abi == .msvc) { man.hash.addBytes(libc_installation.msvc_lib_dir.?); @@ -93,19 +100,19 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } } - try link.hashAddSystemLibs(&man, self.base.options.system_libs); - man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys()); - man.hash.addOptional(self.base.options.subsystem); - man.hash.add(self.base.options.is_test); - man.hash.add(self.base.options.tsaware); - man.hash.add(self.base.options.nxcompat); - man.hash.add(self.base.options.dynamicbase); - man.hash.addOptional(self.base.options.allow_shlib_undefined); + try link.hashAddSystemLibs(&man, comp.system_libs); + man.hash.addListOfBytes(comp.force_undefined_symbols.keys()); + man.hash.addOptional(self.subsystem); + man.hash.add(comp.config.is_test); + man.hash.add(self.tsaware); + man.hash.add(self.nxcompat); + man.hash.add(self.dynamicbase); + man.hash.add(self.base.allow_shlib_undefined); // strip does not need to go into the linker hash because it is part of the hash namespace - man.hash.addOptional(self.base.options.major_subsystem_version); - man.hash.addOptional(self.base.options.minor_subsystem_version); - man.hash.addOptional(self.base.options.version); - try man.addOptionalFile(self.base.options.module_definition_file); + man.hash.add(self.major_subsystem_version); + man.hash.add(self.minor_subsystem_version); + man.hash.addOptional(comp.version); + try man.addOptionalFile(self.module_definition_file); // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. _ = try man.hit(); @@ -135,13 +142,13 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod }; } - if (self.base.options.output_mode == .Obj) { + if (comp.config.output_mode == .Obj) { // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy // here. TODO: think carefully about how we can avoid this redundant operation when doing // build-obj. See also the corresponding TODO in linkAsArchive. const the_object_path = blk: { - if (self.base.options.objects.len != 0) - break :blk self.base.options.objects[0].path; + if (comp.objects.len != 0) + break :blk comp.objects[0].path; if (comp.c_object_table.count() != 0) break :blk comp.c_object_table.keys()[0].status.success.object_path; @@ -160,7 +167,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } else { // Create an LLD command line and invoke it. - var argv = std.ArrayList([]const u8).init(self.base.allocator); + var argv = std.ArrayList([]const u8).init(gpa); defer argv.deinit(); // We will invoke ourselves as a child process to gain access to LLD. // This is necessary because LLD does not behave properly as a library - @@ -170,34 +177,31 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try argv.append("-ERRORLIMIT:0"); try argv.append("-NOLOGO"); - if (!self.base.options.strip) { + if (comp.config.debug_format != .strip) { try argv.append("-DEBUG"); const out_ext = std.fs.path.extension(full_out_path); - const out_pdb = self.base.options.pdb_out_path orelse try allocPrint(arena, "{s}.pdb", .{ + const out_pdb = self.pdb_out_path orelse try allocPrint(arena, "{s}.pdb", .{ full_out_path[0 .. full_out_path.len - out_ext.len], }); try argv.append(try allocPrint(arena, "-PDB:{s}", .{out_pdb})); try argv.append(try allocPrint(arena, "-PDBALTPATH:{s}", .{out_pdb})); } - if (self.base.options.version) |version| { + if (comp.version) |version| { try argv.append(try allocPrint(arena, "-VERSION:{}.{}", .{ version.major, version.minor })); } - if (self.base.options.lto) { - switch (self.base.options.optimize_mode) { + if (comp.config.lto) { + switch (optimize_mode) { .Debug => {}, .ReleaseSmall => try argv.append("-OPT:lldlto=2"), .ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"), } } - if (self.base.options.output_mode == .Exe) { - const stack_size = self.base.options.stack_size_override orelse 16777216; - try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size})); - } - if (self.base.options.image_base_override) |image_base| { - try argv.append(try std.fmt.allocPrint(arena, "-BASE:{d}", .{image_base})); + if (comp.config.output_mode == .Exe) { + try argv.append(try allocPrint(arena, "-STACK:{d}", .{self.base.stack_size})); } + try argv.append(try std.fmt.allocPrint(arena, "-BASE:{d}", .{self.image_base})); if (target.cpu.arch == .x86) { try argv.append("-MACHINE:X86"); @@ -211,7 +215,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } - for (self.base.options.force_undefined_symbols.keys()) |symbol| { + for (comp.force_undefined_symbols.keys()) |symbol| { try argv.append(try allocPrint(arena, "-INCLUDE:{s}", .{symbol})); } @@ -219,34 +223,32 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try argv.append("-DLL"); } - if (self.base.options.entry) |entry| { - try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{entry})); + if (entry_name) |name| { + try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{name})); } - if (self.base.options.tsaware) { + if (self.tsaware) { try argv.append("-tsaware"); } - if (self.base.options.nxcompat) { + if (self.nxcompat) { try argv.append("-nxcompat"); } - if (!self.base.options.dynamicbase) { + if (!self.dynamicbase) { try argv.append("-dynamicbase:NO"); } - if (self.base.options.allow_shlib_undefined) |allow_shlib_undefined| { - if (allow_shlib_undefined) { - try argv.append("-FORCE:UNRESOLVED"); - } + if (self.base.allow_shlib_undefined) { + try argv.append("-FORCE:UNRESOLVED"); } try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path})); - if (self.base.options.implib_emit) |emit| { + if (comp.implib_emit) |emit| { const implib_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path}); try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path})); } - if (self.base.options.link_libc) { - if (self.base.options.libc_installation) |libc_installation| { + if (comp.config.link_libc) { + if (comp.libc_installation) |libc_installation| { try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?})); if (target.abi == .msvc) { @@ -256,12 +258,12 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } - for (self.base.options.lib_dirs) |lib_dir| { + for (self.lib_dirs) |lib_dir| { try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir})); } - try argv.ensureUnusedCapacity(self.base.options.objects.len); - for (self.base.options.objects) |obj| { + try argv.ensureUnusedCapacity(comp.objects.len); + for (comp.objects) |obj| { if (obj.must_link) { argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{s}", .{obj.path})); } else { @@ -283,18 +285,18 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try argv.append(p); } - if (self.base.options.module_definition_file) |def| { + if (self.module_definition_file) |def| { try argv.append(try allocPrint(arena, "-DEF:{s}", .{def})); } const resolved_subsystem: ?std.Target.SubSystem = blk: { - if (self.base.options.subsystem) |explicit| break :blk explicit; + if (self.subsystem) |explicit| break :blk explicit; switch (target.os.tag) { .windows => { - if (self.base.options.module) |module| { + if (comp.module) |module| { if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib) break :blk null; - if (module.stage1_flags.have_c_main or self.base.options.is_test or + if (module.stage1_flags.have_c_main or comp.config.is_test or module.stage1_flags.have_winmain_crt_startup or module.stage1_flags.have_wwinmain_crt_startup) { @@ -313,16 +315,9 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod const Mode = enum { uefi, win32 }; const mode: Mode = mode: { if (resolved_subsystem) |subsystem| { - const subsystem_suffix = ss: { - if (self.base.options.major_subsystem_version) |major| { - if (self.base.options.minor_subsystem_version) |minor| { - break :ss try allocPrint(arena, ",{d}.{d}", .{ major, minor }); - } else { - break :ss try allocPrint(arena, ",{d}", .{major}); - } - } - break :ss ""; - }; + const subsystem_suffix = try allocPrint(arena, ",{d}.{d}", .{ + self.major_subsystem_version, self.minor_subsystem_version, + }); switch (subsystem) { .Console => { @@ -419,21 +414,21 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod try argv.append(try comp.get_libc_crt_file(arena, "uuid.lib")); for (mingw.always_link_libs) |name| { - if (!self.base.options.system_libs.contains(name)) { + if (!comp.system_libs.contains(name)) { const lib_basename = try allocPrint(arena, "{s}.lib", .{name}); try argv.append(try comp.get_libc_crt_file(arena, lib_basename)); } } } else { - const lib_str = switch (self.base.options.link_mode) { + const lib_str = switch (comp.config.link_mode) { .Dynamic => "", .Static => "lib", }; - const d_str = switch (self.base.options.optimize_mode) { + const d_str = switch (optimize_mode) { .Debug => "d", else => "", }; - switch (self.base.options.link_mode) { + switch (comp.config.link_mode) { .Static => try argv.append(try allocPrint(arena, "libcmt{s}.lib", .{d_str})), .Dynamic => try argv.append(try allocPrint(arena, "msvcrt{s}.lib", .{d_str})), } @@ -451,8 +446,8 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } else { try argv.append("-NODEFAULTLIB"); - if (!is_lib and self.base.options.entry == null) { - if (self.base.options.module) |module| { + if (!is_lib and entry_name == null) { + if (comp.module) |module| { if (module.stage1_flags.have_winmain_crt_startup) { try argv.append("-ENTRY:WinMainCRTStartup"); } else { @@ -467,18 +462,18 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } // libc++ dep - if (self.base.options.link_libcpp) { + if (comp.config.link_libcpp) { try argv.append(comp.libcxxabi_static_lib.?.full_object_path); try argv.append(comp.libcxx_static_lib.?.full_object_path); } // libunwind dep - if (self.base.options.link_libunwind) { + if (comp.config.link_libunwind) { try argv.append(comp.libunwind_static_lib.?.full_object_path); } - if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) { - if (!self.base.options.link_libc) { + if (is_exe_or_dyn_lib and !comp.skip_linker_dependencies) { + if (!comp.config.link_libc) { if (comp.libc_static_lib) |lib| { try argv.append(lib.full_object_path); } @@ -489,20 +484,20 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod if (comp.compiler_rt_lib) |lib| try argv.append(lib.full_object_path); } - try argv.ensureUnusedCapacity(self.base.options.system_libs.count()); - for (self.base.options.system_libs.keys()) |key| { + try argv.ensureUnusedCapacity(comp.system_libs.count()); + for (comp.system_libs.keys()) |key| { const lib_basename = try allocPrint(arena, "{s}.lib", .{key}); if (comp.crt_files.get(lib_basename)) |crt_file| { argv.appendAssumeCapacity(crt_file.full_object_path); continue; } - if (try findLib(arena, lib_basename, self.base.options.lib_dirs)) |full_path| { + if (try findLib(arena, lib_basename, self.lib_dirs)) |full_path| { argv.appendAssumeCapacity(full_path); continue; } if (target.abi.isGnu()) { const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key}); - if (try findLib(arena, fallback_name, self.base.options.lib_dirs)) |full_path| { + if (try findLib(arena, fallback_name, self.lib_dirs)) |full_path| { argv.appendAssumeCapacity(full_path); continue; } @@ -516,7 +511,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod return error.DllImportLibraryNotFound; } - if (self.base.options.verbose_link) { + if (comp.verbose_link) { // Skip over our own name so that the LLD linker name is the first argv item. Compilation.dump_argv(argv.items[1..]); } @@ -586,7 +581,7 @@ pub fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod } } - if (!self.base.options.disable_lld_caching) { + if (!self.base.disable_lld_caching) { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index ef803a45f18b..cabaed1addc7 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -1022,16 +1022,18 @@ const min_nop_size = 2; /// actual_capacity + (actual_capacity / ideal_factor) const ideal_factor = 3; -pub fn init(allocator: Allocator, bin_file: *File, format: Format) Dwarf { - const target = &bin_file.options.target; +pub fn init(lf: *File, format: Format) Dwarf { + const comp = lf.comp; + const gpa = comp.gpa; + const target = comp.root_mod.resolved_target.result; const ptr_width: PtrWidth = switch (target.ptrBitWidth()) { 0...32 => .p32, 33...64 => .p64, else => unreachable, }; return .{ - .allocator = allocator, - .bin_file = bin_file, + .allocator = gpa, + .bin_file = lf, .format = format, .ptr_width = ptr_width, .dbg_line_header = switch (target.cpu.arch) { @@ -1190,7 +1192,7 @@ pub fn initDeclState(self: *Dwarf, mod: *Module, decl_index: InternPool.DeclInde pub fn commitDeclState( self: *Dwarf, - mod: *Module, + zcu: *Module, decl_index: InternPool.DeclIndex, sym_addr: u64, sym_size: u64, @@ -1200,15 +1202,17 @@ pub fn commitDeclState( defer tracy.end(); const gpa = self.allocator; + const decl = zcu.declPtr(decl_index); + const ip = &zcu.intern_pool; + const namespace = zcu.namespacePtr(decl.src_namespace); + const target = namespace.file_scope.mod.resolved_target.result; + const target_endian = target.cpu.arch.endian(); + var dbg_line_buffer = &decl_state.dbg_line; var dbg_info_buffer = &decl_state.dbg_info; - const decl = mod.declPtr(decl_index); - const ip = &mod.intern_pool; - - const target_endian = self.bin_file.options.target.cpu.arch.endian(); assert(decl.has_tv); - switch (decl.ty.zigTypeTag(mod)) { + switch (decl.ty.zigTypeTag(zcu)) { .Fn => { try decl_state.setInlineFunc(decl.val.toIntern()); @@ -1407,18 +1411,18 @@ pub fn commitDeclState( if (ip.isErrorSetType(ty.toIntern())) continue; symbol.offset = @intCast(dbg_info_buffer.items.len); - try decl_state.addDbgInfoType(mod, di_atom_index, ty); + try decl_state.addDbgInfoType(zcu, di_atom_index, ty); } } try self.updateDeclDebugInfoAllocation(di_atom_index, @intCast(dbg_info_buffer.items.len)); while (decl_state.abbrev_relocs.popOrNull()) |reloc| { - if (reloc.target) |target| { - const symbol = decl_state.abbrev_table.items[target]; + if (reloc.target) |reloc_target| { + const symbol = decl_state.abbrev_table.items[reloc_target]; const ty = symbol.type; if (ip.isErrorSetType(ty.toIntern())) { - log.debug("resolving %{d} deferred until flush", .{target}); + log.debug("resolving %{d} deferred until flush", .{reloc_target}); try self.global_abbrev_relocs.append(gpa, .{ .target = null, .offset = reloc.offset, @@ -1431,8 +1435,8 @@ pub fn commitDeclState( log.debug("{x}: [() => {x}] (%{d}, '{}')", .{ reloc.offset, value, - target, - ty.fmt(mod), + reloc_target, + ty.fmt(zcu), }); mem.writeInt( u32, @@ -1741,6 +1745,7 @@ pub fn freeDecl(self: *Dwarf, decl_index: InternPool.DeclIndex) void { } pub fn writeDbgAbbrev(self: *Dwarf) !void { + const gpa = self.allocator; // These are LEB encoded but since the values are all less than 127 // we can simply append these bytes. // zig fmt: off @@ -1883,7 +1888,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void { .wasm => { const wasm_file = self.bin_file.cast(File.Wasm).?; const debug_abbrev = &wasm_file.getAtomPtr(wasm_file.debug_abbrev_atom.?).code; - try debug_abbrev.resize(wasm_file.base.allocator, needed_size); + try debug_abbrev.resize(gpa, needed_size); debug_abbrev.items[0..abbrev_buf.len].* = abbrev_buf; }, else => unreachable, @@ -1895,7 +1900,7 @@ fn dbgInfoHeaderBytes(self: *Dwarf) usize { return 120; } -pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u64) !void { +pub fn writeDbgInfoHeader(self: *Dwarf, zcu: *Module, low_pc: u64, high_pc: u64) !void { // If this value is null it means there is an error in the module; // leave debug_info_header_dirty=true. const first_dbg_info_off = self.getDebugInfoOff() orelse return; @@ -1906,7 +1911,9 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u var di_buf = try std.ArrayList(u8).initCapacity(self.allocator, needed_bytes); defer di_buf.deinit(); - const target_endian = self.bin_file.options.target.cpu.arch.endian(); + const comp = self.bin_file.comp; + const target = comp.root_mod.resolved_target.result; + const target_endian = target.cpu.arch.endian(); const init_len_size: usize = switch (self.format) { .dwarf32 => 4, .dwarf64 => 12, @@ -1929,9 +1936,9 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u di_buf.appendAssumeCapacity(self.ptrWidthBytes()); // address size // Write the form for the compile unit, which must match the abbrev table above. - const name_strp = try self.strtab.insert(self.allocator, module.root_mod.root_src_path); + const name_strp = try self.strtab.insert(self.allocator, zcu.root_mod.root_src_path); var compile_unit_dir_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; - const compile_unit_dir = resolveCompilationDir(module, &compile_unit_dir_buffer); + const compile_unit_dir = resolveCompilationDir(zcu, &compile_unit_dir_buffer); const comp_dir_strp = try self.strtab.insert(self.allocator, compile_unit_dir); const producer_strp = try self.strtab.insert(self.allocator, link.producer_string); @@ -1995,7 +2002,9 @@ fn resolveCompilationDir(module: *Module, buffer: *[std.fs.MAX_PATH_BYTES]u8) [] } fn writeAddrAssumeCapacity(self: *Dwarf, buf: *std.ArrayList(u8), addr: u64) void { - const target_endian = self.bin_file.options.target.cpu.arch.endian(); + const comp = self.bin_file.comp; + const target = comp.root_mod.resolved_target.result; + const target_endian = target.cpu.arch.endian(); switch (self.ptr_width) { .p32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @intCast(addr), target_endian), .p64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), addr, target_endian), @@ -2003,7 +2012,9 @@ fn writeAddrAssumeCapacity(self: *Dwarf, buf: *std.ArrayList(u8), addr: u64) voi } fn writeOffsetAssumeCapacity(self: *Dwarf, buf: *std.ArrayList(u8), off: u64) void { - const target_endian = self.bin_file.options.target.cpu.arch.endian(); + const comp = self.bin_file.comp; + const target = comp.root_mod.resolved_target.result; + const target_endian = target.cpu.arch.endian(); switch (self.format) { .dwarf32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @intCast(off), target_endian), .dwarf64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), off, target_endian), @@ -2225,7 +2236,10 @@ fn writeDbgInfoNopsToArrayList( } pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void { - const target_endian = self.bin_file.options.target.cpu.arch.endian(); + const comp = self.bin_file.comp; + const gpa = comp.gpa; + const target = comp.root_mod.resolved_target.result; + const target_endian = target.cpu.arch.endian(); const ptr_width_bytes = self.ptrWidthBytes(); // Enough for all the data without resizing. When support for more compilation units @@ -2289,7 +2303,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void { .wasm => { const wasm_file = self.bin_file.cast(File.Wasm).?; const debug_ranges = &wasm_file.getAtomPtr(wasm_file.debug_ranges_atom.?).code; - try debug_ranges.resize(wasm_file.base.allocator, needed_size); + try debug_ranges.resize(gpa, needed_size); @memcpy(debug_ranges.items[0..di_buf.items.len], di_buf.items); }, else => unreachable, @@ -2297,9 +2311,10 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void { } pub fn writeDbgLineHeader(self: *Dwarf) !void { + const comp = self.bin_file.comp; const gpa = self.allocator; - - const target_endian = self.bin_file.options.target.cpu.arch.endian(); + const target = comp.root_mod.resolved_target.result; + const target_endian = target.cpu.arch.endian(); const init_len_size: usize = switch (self.format) { .dwarf32 => 4, .dwarf64 => 12, @@ -2563,7 +2578,8 @@ fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { } pub fn flushModule(self: *Dwarf, module: *Module) !void { - const target = self.bin_file.options.target; + const comp = self.bin_file.comp; + const target = comp.root_mod.resolved_target.result; if (self.global_abbrev_relocs.items.len > 0) { const gpa = self.allocator; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index d52a5fa423a5..2f1d5703f973 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1,8 +1,34 @@ base: link.File, +image_base: u64, +emit_relocs: bool, +z_nodelete: bool, +z_notext: bool, +z_defs: bool, +z_origin: bool, +z_nocopyreloc: bool, +z_now: bool, +z_relro: bool, +/// TODO make this non optional and resolve the default in open() +z_common_page_size: ?u64, +/// TODO make this non optional and resolve the default in open() +z_max_page_size: ?u64, +lib_dirs: []const []const u8, +hash_style: HashStyle, +compress_debug_sections: CompressDebugSections, +symbol_wrap_set: std.StringArrayHashMapUnmanaged(void), +each_lib_rpath: bool, +sort_section: ?SortSection, +soname: ?[]const u8, +bind_global_refs_locally: bool, +linker_script: ?[]const u8, +version_script: ?[]const u8, +print_icf_sections: bool, +print_map: bool, +entry_name: ?[]const u8, ptr_width: PtrWidth, -/// If this is not null, an object file is created by LLVM and linked with LLD afterwards. +/// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path. llvm_object: ?*LlvmObject = null, /// A list of all input files. @@ -171,9 +197,6 @@ resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{}, has_text_reloc: bool = false, num_ifunc_dynrelocs: usize = 0, -error_flags: link.File.ErrorFlags = link.File.ErrorFlags{}, -misc_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .{}, - /// List of atoms that are owned directly by the linker. atoms: std.ArrayListUnmanaged(Atom) = .{}, @@ -199,59 +222,161 @@ const minimum_atom_size = 64; pub const min_text_capacity = padToIdeal(minimum_atom_size); pub const PtrWidth = enum { p32, p64 }; +pub const HashStyle = enum { sysv, gnu, both }; +pub const CompressDebugSections = enum { none, zlib, zstd }; +pub const SortSection = enum { name, alignment }; + +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*Elf { + const target = comp.root_mod.resolved_target.result; + assert(target.ofmt == .elf); + + const use_lld = build_options.have_llvm and comp.config.use_lld; + const use_llvm = comp.config.use_llvm; + const opt_zcu = comp.module; + const output_mode = comp.config.output_mode; + const link_mode = comp.config.link_mode; + const optimize_mode = comp.root_mod.optimize_mode; + const is_native_os = comp.root_mod.resolved_target.is_native_os; + const ptr_width: PtrWidth = switch (target.ptrBitWidth()) { + 0...32 => .p32, + 33...64 => .p64, + else => return error.UnsupportedELFArchitecture, + }; -pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf { - assert(options.target.ofmt == .elf); + const page_size: u32 = switch (target.cpu.arch) { + .powerpc64le => 0x10000, + .sparc64 => 0x2000, + else => 0x1000, + }; + const is_dyn_lib = output_mode == .Lib and link_mode == .Dynamic; + const default_sym_version: elf.Elf64_Versym = if (is_dyn_lib or comp.config.rdynamic) + elf.VER_NDX_GLOBAL + else + elf.VER_NDX_LOCAL; - const self = try createEmpty(allocator, options); - errdefer self.base.destroy(); + // If using LLD to link, this code should produce an object file so that it + // can be passed to LLD. + // If using LLVM to generate the object file for the zig compilation unit, + // we need a place to put the object file so that it can be subsequently + // handled. + const zcu_object_sub_path = if (!use_lld and !use_llvm) + null + else + try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path}); - const is_obj = options.output_mode == .Obj; - const is_obj_or_ar = is_obj or (options.output_mode == .Lib and options.link_mode == .Static); + const self = try arena.create(Elf); + self.* = .{ + .base = .{ + .tag = .elf, + .comp = comp, + .emit = emit, + .zcu_object_sub_path = zcu_object_sub_path, + .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), + .print_gc_sections = options.print_gc_sections, + .stack_size = options.stack_size orelse 16777216, + .allow_shlib_undefined = options.allow_shlib_undefined orelse !is_native_os, + .file = null, + .disable_lld_caching = options.disable_lld_caching, + .build_id = options.build_id, + .rpath_list = options.rpath_list, + }, + .ptr_width = ptr_width, + .page_size = page_size, + .default_sym_version = default_sym_version, - if (options.use_llvm) { - const use_lld = build_options.have_llvm and self.base.options.use_lld; - if (use_lld) return self; + .entry_name = switch (options.entry) { + .disabled => null, + .default => if (output_mode != .Exe) null else defaultEntrySymbolName(target.cpu.arch), + .enabled => defaultEntrySymbolName(target.cpu.arch), + .named => |name| name, + }, - 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), - }); - } + .image_base = b: { + if (is_dyn_lib) break :b 0; + if (output_mode == .Exe and comp.config.pie) break :b 0; + break :b options.image_base orelse switch (ptr_width) { + .p32 => 0x10000, + .p64 => 0x1000000, + }; + }, + + .emit_relocs = options.emit_relocs, + .z_nodelete = options.z_nodelete, + .z_notext = options.z_notext, + .z_defs = options.z_defs, + .z_origin = options.z_origin, + .z_nocopyreloc = options.z_nocopyreloc, + .z_now = options.z_now, + .z_relro = options.z_relro, + .z_common_page_size = options.z_common_page_size, + .z_max_page_size = options.z_max_page_size, + .lib_dirs = options.lib_dirs, + .hash_style = options.hash_style, + .compress_debug_sections = options.compress_debug_sections, + .symbol_wrap_set = options.symbol_wrap_set, + .each_lib_rpath = options.each_lib_rpath, + .sort_section = options.sort_section, + .soname = options.soname, + .bind_global_refs_locally = options.bind_global_refs_locally, + .linker_script = options.linker_script, + .version_script = options.version_script, + .print_icf_sections = options.print_icf_sections, + .print_map = options.print_map, + }; + if (use_llvm and comp.config.have_zcu) { + self.llvm_object = try LlvmObject.create(arena, comp); + } + errdefer self.base.destroy(); + + if (use_lld and (use_llvm or !comp.config.have_zcu)) { + // LLVM emits the object file (if any); LLD links it into the final product. + return self; } - errdefer if (self.base.intermediary_basename) |path| allocator.free(path); - self.base.file = try options.emit.?.directory.handle.createFile(sub_path, .{ - .truncate = false, + const is_obj = output_mode == .Obj; + const is_obj_or_ar = is_obj or (output_mode == .Lib and link_mode == .Static); + + // What path should this ELF linker code output to? + // If using LLD to link, this code should produce an object file so that it + // can be passed to LLD. + const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; + self.base.file = try emit.directory.handle.createFile(sub_path, .{ + .truncate = true, .read = true, - .mode = link.determineMode(options), + .mode = link.File.determineMode(use_lld, output_mode, link_mode), }); + const gpa = 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) { .p32 => @alignOf(elf.Elf32_Phdr), .p64 => @alignOf(elf.Elf64_Phdr), }; - const image_base = self.calcImageBase(); const ehsize: u64 = switch (self.ptr_width) { .p32 => @sizeOf(elf.Elf32_Ehdr), .p64 => @sizeOf(elf.Elf64_Ehdr), @@ -266,7 +391,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option .type = elf.PT_PHDR, .flags = elf.PF_R, .@"align" = p_align, - .addr = image_base + ehsize, + .addr = self.image_base + ehsize, .offset = ehsize, .filesz = reserved, .memsz = reserved, @@ -275,71 +400,49 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option .type = elf.PT_LOAD, .flags = elf.PF_R, .@"align" = self.page_size, - .addr = image_base, + .addr = self.image_base, .offset = 0, .filesz = reserved + ehsize, .memsz = reserved + ehsize, }); } - if (options.module != null and !options.use_llvm) { - const index = @as(File.Index, @intCast(try self.files.addOne(allocator))); - self.files.set(index, .{ .zig_object = .{ - .index = index, - .path = try std.fmt.allocPrint(self.base.allocator, "{s}.o", .{std.fs.path.stem( - options.module.?.main_mod.root_src_path, - )}), - } }); - self.zig_object_index = index; - try self.zigObjectPtr().?.init(self); - try self.initMetadata(); + if (opt_zcu) |zcu| { + if (!use_llvm) { + const index: File.Index = @intCast(try self.files.addOne(gpa)); + self.files.set(index, .{ .zig_object = .{ + .index = index, + .path = try std.fmt.allocPrint(arena, "{s}.o", .{std.fs.path.stem( + zcu.main_mod.root_src_path, + )}), + } }); + self.zig_object_index = index; + try self.zigObjectPtr().?.init(self); + try self.initMetadata(.{ + .symbol_count_hint = options.symbol_count_hint, + .program_code_size_hint = options.program_code_size_hint, + }); + } } return self; } -pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { - const ptr_width: PtrWidth = switch (options.target.ptrBitWidth()) { - 0...32 => .p32, - 33...64 => .p64, - else => return error.UnsupportedELFArchitecture, - }; - const self = try gpa.create(Elf); - errdefer gpa.destroy(self); - - const page_size: u32 = switch (options.target.cpu.arch) { - .powerpc64le => 0x10000, - .sparc64 => 0x2000, - else => 0x1000, - }; - const is_dyn_lib = options.output_mode == .Lib and options.link_mode == .Dynamic; - const default_sym_version: elf.Elf64_Versym = if (is_dyn_lib or options.rdynamic) - elf.VER_NDX_GLOBAL - else - elf.VER_NDX_LOCAL; - - self.* = .{ - .base = .{ - .tag = .elf, - .options = options, - .allocator = gpa, - .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); - } - - return self; +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*Elf { + // TODO: restore saved linker state, don't truncate the file, and + // participate in incremental compilation. + return createEmpty(arena, comp, emit, options); } 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 => {}, @@ -378,7 +481,6 @@ pub fn deinit(self: *Elf) void { } self.last_atom_and_free_list_table.deinit(gpa); - self.misc_errors.deinit(gpa); self.comdat_groups.deinit(gpa); self.comdat_groups_owners.deinit(gpa); self.comdat_groups_table.deinit(gpa); @@ -494,17 +596,23 @@ fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 { return start; } +pub const InitMetadataOptions = struct { + symbol_count_hint: u64, + program_code_size_hint: u64, +}; + /// TODO move to ZigObject -pub fn initMetadata(self: *Elf) !void { - const gpa = self.base.allocator; +pub fn initMetadata(self: *Elf, options: InitMetadataOptions) !void { + 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 { fn fillSection(elf_file: *Elf, shdr: *elf.Elf64_Shdr, size: u64, phndx: ?u16) void { - if (elf_file.isRelocatable()) { + if (elf_file.base.isRelocatable()) { const off = elf_file.findFreeSpace(size, shdr.sh_addralign); shdr.sh_offset = off; shdr.sh_size = size; @@ -519,9 +627,9 @@ pub fn initMetadata(self: *Elf) !void { comptime assert(number_of_zig_segments == 5); - if (!self.isRelocatable()) { + if (!self.base.isRelocatable()) { if (self.phdr_zig_load_re_index == null) { - const filesz = self.base.options.program_code_size_hint; + const filesz = options.program_code_size_hint; const off = self.findFreeSpace(filesz, self.page_size); self.phdr_zig_load_re_index = try self.addPhdr(.{ .type = elf.PT_LOAD, @@ -538,7 +646,7 @@ pub fn initMetadata(self: *Elf) !void { // We really only need ptr alignment but since we are using PROGBITS, linux requires // page align. const alignment = if (is_linux) self.page_size else @as(u16, ptr_size); - const filesz = @as(u64, ptr_size) * self.base.options.symbol_count_hint; + const filesz = @as(u64, ptr_size) * options.symbol_count_hint; const off = self.findFreeSpace(filesz, alignment); self.phdr_zig_got_index = try self.addPhdr(.{ .type = elf.PT_LOAD, @@ -602,8 +710,8 @@ pub fn initMetadata(self: *Elf) !void { .offset = std.math.maxInt(u64), }); const shdr = &self.shdrs.items[self.zig_text_section_index.?]; - fillSection(self, shdr, self.base.options.program_code_size_hint, self.phdr_zig_load_re_index); - if (self.isRelocatable()) { + fillSection(self, shdr, options.program_code_size_hint, self.phdr_zig_load_re_index); + if (self.base.isRelocatable()) { const rela_shndx = try self.addRelaShdr(".rela.text.zig", self.zig_text_section_index.?); try self.output_rela_sections.putNoClobber(gpa, self.zig_text_section_index.?, .{ .shndx = rela_shndx, @@ -619,7 +727,7 @@ pub fn initMetadata(self: *Elf) !void { try self.last_atom_and_free_list_table.putNoClobber(gpa, self.zig_text_section_index.?, .{}); } - if (self.zig_got_section_index == null and !self.isRelocatable()) { + if (self.zig_got_section_index == null and !self.base.isRelocatable()) { self.zig_got_section_index = try self.addSection(.{ .name = ".got.zig", .type = elf.SHT_PROGBITS, @@ -650,7 +758,7 @@ pub fn initMetadata(self: *Elf) !void { }); const shdr = &self.shdrs.items[self.zig_data_rel_ro_section_index.?]; fillSection(self, shdr, 1024, self.phdr_zig_load_ro_index); - if (self.isRelocatable()) { + if (self.base.isRelocatable()) { const rela_shndx = try self.addRelaShdr( ".rela.data.rel.ro.zig", self.zig_data_rel_ro_section_index.?, @@ -679,7 +787,7 @@ pub fn initMetadata(self: *Elf) !void { }); const shdr = &self.shdrs.items[self.zig_data_section_index.?]; fillSection(self, shdr, 1024, self.phdr_zig_load_rw_index); - if (self.isRelocatable()) { + if (self.base.isRelocatable()) { const rela_shndx = try self.addRelaShdr( ".rela.data.zig", self.zig_data_section_index.?, @@ -918,56 +1026,47 @@ pub fn markDirty(self: *Elf, shdr_index: u16) void { } } -pub fn flush(self: *Elf, 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 use_lld = build_options.have_llvm and self.base.options.use_lld; +pub fn flush(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { + const use_lld = build_options.have_llvm and self.base.comp.config.use_lld; if (use_lld) { - return self.linkWithLLD(comp, prog_node); + return self.linkWithLLD(arena, prog_node); } - try self.flushModule(comp, prog_node); + try self.flushModule(arena, prog_node); } -pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { +pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { const tracy = trace(@src()); defer tracy.end(); - if (self.llvm_object) |llvm_object| { - try llvm_object.flushModule(comp, prog_node); + const comp = self.base.comp; + const gpa = comp.gpa; - const use_lld = build_options.have_llvm and self.base.options.use_lld; + if (self.llvm_object) |llvm_object| { + try self.base.emitLlvmObject(arena, llvm_object, prog_node); + const use_lld = build_options.have_llvm and comp.config.use_lld; if (use_lld) return; } - const gpa = self.base.allocator; var sub_prog_node = prog_node.start("ELF Flush", 0); sub_prog_node.activate(); defer sub_prog_node.end(); - var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - - const target = self.base.options.target; - 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: { + const target = comp.root_mod.resolved_target.result; + const link_mode = comp.config.link_mode; + const directory = self.base.emit.directory; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); + const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { if (fs.path.dirname(full_out_path)) |dirname| { break :blk try fs.path.join(arena, &.{ dirname, path }); } else { break :blk path; } } else null; - const gc_sections = self.base.options.gc_sections orelse false; // --verbose-link - if (self.base.options.verbose_link) try self.dumpArgv(comp); + if (comp.verbose_link) try self.dumpArgv(comp); - const csu = try CsuObjects.init(arena, self.base.options, comp); + const csu = try CsuObjects.init(arena, comp); const compiler_rt_path: ?[]const u8 = blk: { if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; @@ -975,8 +1074,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node }; if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self); - if (self.isStaticLib()) return self.flushStaticLib(comp, module_obj_path); - if (self.isObject()) return self.flushObject(comp, module_obj_path); + if (self.base.isStaticLib()) return self.flushStaticLib(comp, module_obj_path); + if (self.base.isObject()) return self.flushObject(comp, module_obj_path); // Here we will parse input positional and library files (if referenced). // This will roughly match in any linker backend we support. @@ -987,8 +1086,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node if (csu.crti) |v| try positionals.append(.{ .path = v }); if (csu.crtbegin) |v| try positionals.append(.{ .path = v }); - try positionals.ensureUnusedCapacity(self.base.options.objects.len); - positionals.appendSliceAssumeCapacity(self.base.options.objects); + try positionals.ensureUnusedCapacity(comp.objects.len); + positionals.appendSliceAssumeCapacity(comp.objects); // This is a set of object files emitted by clang in a single `build-exe` invocation. // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up @@ -1000,24 +1099,24 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node if (module_obj_path) |path| try positionals.append(.{ .path = path }); // rpaths - var rpath_table = std.StringArrayHashMap(void).init(self.base.allocator); + var rpath_table = std.StringArrayHashMap(void).init(gpa); defer rpath_table.deinit(); - for (self.base.options.rpath_list) |rpath| { + for (self.base.rpath_list) |rpath| { _ = try rpath_table.put(rpath, {}); } - if (self.base.options.each_lib_rpath) { - var test_path = std.ArrayList(u8).init(self.base.allocator); + if (self.each_lib_rpath) { + var test_path = std.ArrayList(u8).init(gpa); defer test_path.deinit(); - for (self.base.options.lib_dirs) |lib_dir_path| { - for (self.base.options.system_libs.keys()) |link_lib| { + for (self.lib_dirs) |lib_dir_path| { + for (comp.system_libs.keys()) |link_lib| { if (!(try self.accessLibPath(&test_path, null, lib_dir_path, link_lib, .Dynamic))) continue; _ = try rpath_table.put(lib_dir_path, {}); } } - for (self.base.options.objects) |obj| { + for (comp.objects) |obj| { if (Compilation.classifyFileExt(obj.path) == .shared_library) { const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue; if (obj.loption) continue; @@ -1027,14 +1126,12 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } // TSAN - if (self.base.options.tsan) { + if (comp.config.any_sanitize_thread) { try positionals.append(.{ .path = comp.tsan_static_lib.?.full_object_path }); } // libc - if (!self.base.options.skip_linker_dependencies and - !self.base.options.link_libc) - { + if (!comp.skip_linker_dependencies and !comp.config.link_libc) { if (comp.libc_static_lib) |lib| { try positionals.append(.{ .path = lib.full_object_path }); } @@ -1053,27 +1150,27 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node var system_libs = std.ArrayList(SystemLib).init(arena); - try system_libs.ensureUnusedCapacity(self.base.options.system_libs.values().len); - for (self.base.options.system_libs.values()) |lib_info| { + try system_libs.ensureUnusedCapacity(comp.system_libs.values().len); + for (comp.system_libs.values()) |lib_info| { system_libs.appendAssumeCapacity(.{ .needed = lib_info.needed, .path = lib_info.path.? }); } // libc++ dep - if (self.base.options.link_libcpp) { + if (comp.config.link_libcpp) { try system_libs.ensureUnusedCapacity(2); system_libs.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path }); system_libs.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path }); } // libunwind dep - if (self.base.options.link_libunwind) { + if (comp.config.link_libunwind) { try system_libs.append(.{ .path = comp.libunwind_static_lib.?.full_object_path }); } // libc dep - self.error_flags.missing_libc = false; - if (self.base.options.link_libc) { - if (self.base.options.libc_installation) |lc| { + comp.link_error_flags.missing_libc = false; + if (comp.config.link_libc) { + if (comp.libc_installation) |lc| { const flags = target_util.libcFullLinkFlags(target); try system_libs.ensureUnusedCapacity(flags.len); @@ -1085,7 +1182,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node const lib_name = flag["-l".len..]; success: { - if (!self.isStatic()) { + if (!self.base.isStatic()) { if (try self.accessLibPath(&test_path, &checked_paths, lc.crt_dir.?, lib_name, .Dynamic)) break :success; } @@ -1116,13 +1213,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node .path = try comp.get_libc_crt_file(arena, "libc_nonshared.a"), }); } else if (target.isMusl()) { - const path = try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) { + const path = try comp.get_libc_crt_file(arena, switch (link_mode) { .Static => "libc.a", .Dynamic => "libc.so", }); try system_libs.append(.{ .path = path }); } else { - self.error_flags.missing_libc = true; + comp.link_error_flags.missing_libc = true; } } @@ -1160,7 +1257,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node }; } - if (self.misc_errors.items.len > 0) return error.FlushFailure; + if (comp.link_errors.items.len > 0) return error.FlushFailure; // Init all objects for (self.objects.items) |index| { @@ -1170,7 +1267,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try self.file(index).?.shared_object.init(self); } - if (self.misc_errors.items.len > 0) return error.FlushFailure; + if (comp.link_errors.items.len > 0) return error.FlushFailure; // Dedup shared objects { @@ -1211,18 +1308,15 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // Look for entry address in objects if not set by the incremental compiler. if (self.entry_index == null) { - const entry: ?[]const u8 = entry: { - if (self.base.options.entry) |entry| break :entry entry; - if (!self.isDynLib()) break :entry "_start"; - break :entry null; - }; - self.entry_index = if (entry) |name| self.globalByName(name) else null; + if (self.entry_name) |name| { + self.entry_index = self.globalByName(name); + } } - if (gc_sections) { + if (self.base.gc_sections) { try gc.gcAtoms(self); - if (self.base.options.print_gc_sections) { + if (self.base.print_gc_sections) { try gc.dumpPrunedAtoms(self); } } @@ -1290,26 +1384,26 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node try self.writeAtoms(); try self.writeSyntheticSections(); - if (self.entry_index == null and self.isExe()) { + if (self.entry_index == null and self.base.isExe()) { log.debug("flushing. no_entry_point_found = true", .{}); - self.error_flags.no_entry_point_found = true; + comp.link_error_flags.no_entry_point_found = true; } else { log.debug("flushing. no_entry_point_found = false", .{}); - self.error_flags.no_entry_point_found = false; + comp.link_error_flags.no_entry_point_found = false; try self.writeElfHeader(); } - if (self.misc_errors.items.len > 0) return error.FlushFailure; + if (comp.link_errors.items.len > 0) return error.FlushFailure; } pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void { - const gpa = self.base.allocator; + const gpa = comp.gpa; var positionals = std.ArrayList(Compilation.LinkObject).init(gpa); defer positionals.deinit(); - try positionals.ensureUnusedCapacity(self.base.options.objects.len); - positionals.appendSliceAssumeCapacity(self.base.options.objects); + try positionals.ensureUnusedCapacity(comp.objects.len); + positionals.appendSliceAssumeCapacity(comp.objects); // This is a set of object files emitted by clang in a single `build-exe` invocation. // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up @@ -1331,7 +1425,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const }; } - if (self.misc_errors.items.len > 0) return error.FlushFailure; + if (comp.link_errors.items.len > 0) return error.FlushFailure; // First, we flush relocatable object file generated with our backends. if (self.zigObjectPtr()) |zig_object| { @@ -1443,16 +1537,16 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const try self.base.file.?.setEndPos(total_size); try self.base.file.?.pwriteAll(buffer.items, 0); - if (self.misc_errors.items.len > 0) return error.FlushFailure; + if (comp.link_errors.items.len > 0) return error.FlushFailure; } 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(); - try positionals.ensureUnusedCapacity(self.base.options.objects.len); - positionals.appendSliceAssumeCapacity(self.base.options.objects); + try positionals.ensureUnusedCapacity(comp.objects.len); + positionals.appendSliceAssumeCapacity(comp.objects); // This is a set of object files emitted by clang in a single `build-exe` invocation. // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up @@ -1474,14 +1568,14 @@ pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) }; } - if (self.misc_errors.items.len > 0) return error.FlushFailure; + if (comp.link_errors.items.len > 0) return error.FlushFailure; // Init all objects for (self.objects.items) |index| { try self.file(index).?.object.init(self); } - if (self.misc_errors.items.len > 0) return error.FlushFailure; + if (comp.link_errors.items.len > 0) return error.FlushFailure; // Now, we are ready to resolve the symbols across all input files. // We will first resolve the files in the ZigObject, next in the parsed @@ -1515,28 +1609,29 @@ pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) try self.writeShdrTable(); try self.writeElfHeader(); - if (self.misc_errors.items.len > 0) return error.FlushFailure; + if (comp.link_errors.items.len > 0) return error.FlushFailure; } /// --verbose-link output fn dumpArgv(self: *Elf, comp: *Compilation) !void { - 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(); - const target = self.base.options.target; - 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: { + const target = self.base.comp.root_mod.resolved_target.result; + const link_mode = self.base.comp.config.link_mode; + const directory = self.base.emit.directory; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); + const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: { if (fs.path.dirname(full_out_path)) |dirname| { break :blk try fs.path.join(arena, &.{ dirname, path }); } else { break :blk path; } } else null; - const gc_sections = self.base.options.gc_sections orelse false; - const csu = try CsuObjects.init(arena, self.base.options, comp); + const csu = try CsuObjects.init(arena, comp); const compiler_rt_path: ?[]const u8 = blk: { if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; @@ -1547,21 +1642,21 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { try argv.append("zig"); - if (self.isStaticLib()) { + if (self.base.isStaticLib()) { try argv.append("ar"); } else { try argv.append("ld"); } - if (self.isObject()) { + if (self.base.isObject()) { try argv.append("-r"); } try argv.append("-o"); try argv.append(full_out_path); - if (self.isRelocatable()) { - for (self.base.options.objects) |obj| { + if (self.base.isRelocatable()) { + for (comp.objects) |obj| { try argv.append(obj.path); } @@ -1573,36 +1668,35 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { try argv.append(p); } } else { - if (!self.isStatic()) { - if (self.base.options.dynamic_linker) |path| { + if (!self.base.isStatic()) { + if (target.dynamic_linker.get()) |path| { try argv.append("-dynamic-linker"); try argv.append(path); } } - if (self.isDynLib()) { - if (self.base.options.soname) |name| { + if (self.base.isDynLib()) { + if (self.soname) |name| { try argv.append("-soname"); try argv.append(name); } } - if (self.base.options.entry) |entry| { - try argv.append("--entry"); - try argv.append(entry); + if (self.entry_name) |name| { + try argv.appendSlice(&.{ "--entry", name }); } - for (self.base.options.rpath_list) |rpath| { + for (self.base.rpath_list) |rpath| { try argv.append("-rpath"); try argv.append(rpath); } - if (self.base.options.each_lib_rpath) { - for (self.base.options.lib_dirs) |lib_dir_path| { + if (self.each_lib_rpath) { + for (self.lib_dirs) |lib_dir_path| { try argv.append("-rpath"); try argv.append(lib_dir_path); } - for (self.base.options.objects) |obj| { + for (comp.objects) |obj| { if (Compilation.classifyFileExt(obj.path) == .shared_library) { const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue; if (obj.loption) continue; @@ -1613,57 +1707,55 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } } - if (self.base.options.stack_size_override) |ss| { - try argv.append("-z"); - try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{ss})); - } + try argv.appendSlice(&.{ + "-z", + try std.fmt.allocPrint(arena, "stack-size={d}", .{self.base.stack_size}), + }); - if (self.base.options.image_base_override) |image_base| { - try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base})); - } + try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base})); - if (gc_sections) { + if (self.base.gc_sections) { try argv.append("--gc-sections"); } - if (self.base.options.print_gc_sections) { + if (self.base.print_gc_sections) { try argv.append("--print-gc-sections"); } - if (self.base.options.eh_frame_hdr) { + if (comp.link_eh_frame_hdr) { try argv.append("--eh-frame-hdr"); } - if (self.base.options.rdynamic) { + if (comp.config.rdynamic) { try argv.append("--export-dynamic"); } - if (self.base.options.z_notext) { + if (self.z_notext) { try argv.append("-z"); try argv.append("notext"); } - if (self.base.options.z_nocopyreloc) { + if (self.z_nocopyreloc) { try argv.append("-z"); try argv.append("nocopyreloc"); } - if (self.base.options.z_now) { + if (self.z_now) { try argv.append("-z"); try argv.append("now"); } - if (self.isStatic()) { + if (self.base.isStatic()) { try argv.append("-static"); - } else if (self.isDynLib()) { + } else if (self.base.isDynLib()) { try argv.append("-shared"); } - if (self.base.options.pie and self.isExe()) { + if (comp.config.pie and self.base.isExe()) { try argv.append("-pie"); } - if (self.base.options.strip) { + if (comp.config.debug_format == .strip) { try argv.append("-s"); } @@ -1672,20 +1764,20 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { if (csu.crti) |v| try argv.append(v); if (csu.crtbegin) |v| try argv.append(v); - for (self.base.options.lib_dirs) |lib_dir| { + for (self.lib_dirs) |lib_dir| { try argv.append("-L"); try argv.append(lib_dir); } - if (self.base.options.link_libc) { - if (self.base.options.libc_installation) |libc_installation| { + if (comp.config.link_libc) { + if (self.base.comp.libc_installation) |libc_installation| { try argv.append("-L"); try argv.append(libc_installation.crt_dir.?); } } var whole_archive = false; - for (self.base.options.objects) |obj| { + for (comp.objects) |obj| { if (obj.must_link and !whole_archive) { try argv.append("-whole-archive"); whole_archive = true; @@ -1713,15 +1805,12 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { try argv.append(p); } - // TSAN - if (self.base.options.tsan) { + if (comp.config.any_sanitize_thread) { try argv.append(comp.tsan_static_lib.?.full_object_path); } // libc - if (!self.base.options.skip_linker_dependencies and - !self.base.options.link_libc) - { + if (!comp.skip_linker_dependencies and !comp.config.link_libc) { if (comp.libc_static_lib) |lib| { try argv.append(lib.full_object_path); } @@ -1730,11 +1819,11 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { // Shared libraries. // Worst-case, we need an --as-needed argument for every lib, as well // as one before and one after. - try argv.ensureUnusedCapacity(self.base.options.system_libs.keys().len * 2 + 2); + try argv.ensureUnusedCapacity(self.base.comp.system_libs.keys().len * 2 + 2); argv.appendAssumeCapacity("--as-needed"); var as_needed = true; - for (self.base.options.system_libs.values()) |lib_info| { + for (self.base.comp.system_libs.values()) |lib_info| { const lib_as_needed = !lib_info.needed; switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) { 0b00, 0b11 => {}, @@ -1756,20 +1845,20 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } // libc++ dep - if (self.base.options.link_libcpp) { + if (comp.config.link_libcpp) { try argv.append(comp.libcxxabi_static_lib.?.full_object_path); try argv.append(comp.libcxx_static_lib.?.full_object_path); } // libunwind dep - if (self.base.options.link_libunwind) { + if (comp.config.link_libunwind) { try argv.append(comp.libunwind_static_lib.?.full_object_path); } // libc dep - if (self.base.options.link_libc) { - if (self.base.options.libc_installation != null) { - const needs_grouping = self.base.options.link_mode == .Static; + if (comp.config.link_libc) { + if (self.base.comp.libc_installation != null) { + const needs_grouping = link_mode == .Static; if (needs_grouping) try argv.append("--start-group"); try argv.appendSlice(target_util.libcFullLinkFlags(target)); if (needs_grouping) try argv.append("--end-group"); @@ -1782,7 +1871,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { } try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a")); } else if (target.isMusl()) { - try argv.append(try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) { + try argv.append(try comp.get_libc_crt_file(arena, switch (link_mode) { .Static => "libc.a", .Dynamic => "libc.so", })); @@ -1842,7 +1931,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 +1951,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 +1977,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 +1999,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)); @@ -1920,8 +2009,6 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { defer script.deinit(gpa); try script.parse(data, self); - const lib_dirs = self.base.options.lib_dirs; - var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); @@ -1938,8 +2025,8 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { // TODO I think technically we should re-use the mechanism used by the frontend here. // Maybe we should hoist search-strategy all the way here? - for (lib_dirs) |lib_dir| { - if (!self.isStatic()) { + for (self.lib_dirs) |lib_dir| { + if (!self.base.isStatic()) { if (try self.accessLibPath(&test_path, &checked_paths, lib_dir, lib_name, .Dynamic)) break :success; } @@ -1955,7 +2042,7 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { } else |_| {} try checked_paths.append(try gpa.dupe(u8, scr_obj.path)); - for (lib_dirs) |lib_dir| { + for (self.lib_dirs) |lib_dir| { if (try self.accessLibPath(&test_path, &checked_paths, lib_dir, scr_obj.path, null)) break :success; } @@ -1995,8 +2082,9 @@ fn accessLibPath( lib_name: []const u8, link_mode: ?std.builtin.LinkMode, ) !bool { + const gpa = self.base.comp.gpa; 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) { @@ -2010,7 +2098,7 @@ fn accessLibPath( suffix, }); if (checked_paths) |cpaths| { - try cpaths.append(try self.base.allocator.dupe(u8, test_path.items)); + try cpaths.append(try gpa.dupe(u8, test_path.items)); } fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { error.FileNotFound => return false, @@ -2139,7 +2227,7 @@ fn markImportsExports(self: *Elf) void { } if (file_ptr.index() == file_index) { global.flags.@"export" = true; - if (elf_file.isDynLib() and vis != .PROTECTED) { + if (elf_file.base.isDynLib() and vis != .PROTECTED) { global.flags.import = true; } } @@ -2147,7 +2235,7 @@ fn markImportsExports(self: *Elf) void { } }.mark; - if (!self.isDynLib()) { + if (!self.base.isDynLib()) { for (self.shared_objects.items) |index| { for (self.file(index).?.globals()) |global_index| { const global = self.symbol(global_index); @@ -2190,7 +2278,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 { @@ -2259,26 +2347,25 @@ fn scanRelocs(self: *Elf) !void { } } -fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { +fn linkWithLLD(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); - var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); + const comp = self.base.comp; + const gpa = comp.gpa; - 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 directory = self.base.emit.directory; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path}); // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. - const module_obj_path: ?[]const u8 = if (self.base.options.module != null) blk: { - try self.flushModule(comp, prog_node); + const module_obj_path: ?[]const u8 = if (comp.module != null) blk: { + try self.flushModule(arena, prog_node); if (fs.path.dirname(full_out_path)) |dirname| { - break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? }); + break :blk try fs.path.join(arena, &.{ dirname, self.base.zcu_object_sub_path.? }); } else { - break :blk self.base.intermediary_basename.?; + break :blk self.base.zcu_object_sub_path.?; } } else null; @@ -2287,16 +2374,15 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v sub_prog_node.context.refresh(); defer sub_prog_node.end(); - const is_obj = self.base.options.output_mode == .Obj; - const is_lib = self.base.options.output_mode == .Lib; - const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; - 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 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; + const output_mode = comp.config.output_mode; + const is_obj = output_mode == .Obj; + const is_lib = output_mode == .Lib; + const link_mode = comp.config.link_mode; + const is_dyn_lib = link_mode == .Dynamic and is_lib; + const is_exe_or_dyn_lib = is_dyn_lib or output_mode == .Exe; + const have_dynamic_linker = comp.config.link_libc and + link_mode == .Dynamic and is_exe_or_dyn_lib; + const target = comp.root_mod.resolved_target.result; const compiler_rt_path: ?[]const u8 = blk: { if (comp.compiler_rt_lib) |x| break :blk x.full_object_path; if (comp.compiler_rt_obj) |x| break :blk x.full_object_path; @@ -2314,11 +2400,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v const id_symlink_basename = "lld.id"; var man: Cache.Manifest = undefined; - defer if (!self.base.options.disable_lld_caching) man.deinit(); + defer if (!self.base.disable_lld_caching) man.deinit(); var digest: [Cache.hex_digest_len]u8 = undefined; - if (!self.base.options.disable_lld_caching) { + if (!self.base.disable_lld_caching) { man = comp.cache_parent.obtain(); // We are about to obtain this lock, so here we give other processes a chance first. @@ -2326,9 +2412,9 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v comptime assert(Compilation.link_hash_implementation_version == 10); - try man.addOptionalFile(self.base.options.linker_script); - try man.addOptionalFile(self.base.options.version_script); - for (self.base.options.objects) |obj| { + try man.addOptionalFile(self.linker_script); + try man.addOptionalFile(self.version_script); + for (comp.objects) |obj| { _ = try man.addFile(obj.path, null); man.hash.add(obj.must_link); man.hash.add(obj.loption); @@ -2341,52 +2427,51 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // We can skip hashing libc and libc++ components that we are in charge of building from Zig // installation sources because they are always a product of the compiler version + target information. - man.hash.addOptionalBytes(self.base.options.entry); - man.hash.addOptional(self.base.options.image_base_override); - man.hash.add(gc_sections); - man.hash.addOptional(self.base.options.sort_section); - man.hash.add(self.base.options.eh_frame_hdr); - man.hash.add(self.base.options.emit_relocs); - man.hash.add(self.base.options.rdynamic); - man.hash.addListOfBytes(self.base.options.lib_dirs); - man.hash.addListOfBytes(self.base.options.rpath_list); - man.hash.add(self.base.options.each_lib_rpath); - if (self.base.options.output_mode == .Exe) { - man.hash.add(stack_size); - man.hash.add(self.base.options.build_id); - } - man.hash.addListOfBytes(self.base.options.symbol_wrap_set.keys()); - man.hash.add(self.base.options.skip_linker_dependencies); - man.hash.add(self.base.options.z_nodelete); - man.hash.add(self.base.options.z_notext); - man.hash.add(self.base.options.z_defs); - man.hash.add(self.base.options.z_origin); - man.hash.add(self.base.options.z_nocopyreloc); - man.hash.add(self.base.options.z_now); - man.hash.add(self.base.options.z_relro); - man.hash.add(self.base.options.z_common_page_size orelse 0); - man.hash.add(self.base.options.z_max_page_size orelse 0); - man.hash.add(self.base.options.hash_style); + man.hash.addOptionalBytes(self.entry_name); + man.hash.add(self.image_base); + man.hash.add(self.base.gc_sections); + man.hash.addOptional(self.sort_section); + man.hash.add(comp.link_eh_frame_hdr); + man.hash.add(self.emit_relocs); + man.hash.add(comp.config.rdynamic); + man.hash.addListOfBytes(self.lib_dirs); + man.hash.addListOfBytes(self.base.rpath_list); + man.hash.add(self.each_lib_rpath); + if (output_mode == .Exe) { + man.hash.add(self.base.stack_size); + man.hash.add(self.base.build_id); + } + man.hash.addListOfBytes(self.symbol_wrap_set.keys()); + man.hash.add(comp.skip_linker_dependencies); + man.hash.add(self.z_nodelete); + man.hash.add(self.z_notext); + man.hash.add(self.z_defs); + man.hash.add(self.z_origin); + man.hash.add(self.z_nocopyreloc); + man.hash.add(self.z_now); + man.hash.add(self.z_relro); + man.hash.add(self.z_common_page_size orelse 0); + man.hash.add(self.z_max_page_size orelse 0); + man.hash.add(self.hash_style); // strip does not need to go into the linker hash because it is part of the hash namespace - if (self.base.options.link_libc) { - man.hash.add(self.base.options.libc_installation != null); - if (self.base.options.libc_installation) |libc_installation| { + if (comp.config.link_libc) { + man.hash.add(comp.libc_installation != null); + if (comp.libc_installation) |libc_installation| { man.hash.addBytes(libc_installation.crt_dir.?); } if (have_dynamic_linker) { - man.hash.addOptionalBytes(self.base.options.dynamic_linker); + man.hash.addOptionalBytes(target.dynamic_linker.get()); } } - man.hash.addOptionalBytes(self.base.options.soname); - man.hash.addOptional(self.base.options.version); - try link.hashAddSystemLibs(&man, self.base.options.system_libs); - man.hash.addListOfBytes(self.base.options.force_undefined_symbols.keys()); - man.hash.add(allow_shlib_undefined); - man.hash.add(self.base.options.bind_global_refs_locally); - man.hash.add(self.base.options.compress_debug_sections); - man.hash.add(self.base.options.tsan); - man.hash.addOptionalBytes(self.base.options.sysroot); - man.hash.add(self.base.options.linker_optimization); + man.hash.addOptionalBytes(self.soname); + man.hash.addOptional(comp.version); + try link.hashAddSystemLibs(&man, comp.system_libs); + man.hash.addListOfBytes(comp.force_undefined_symbols.keys()); + man.hash.add(self.base.allow_shlib_undefined); + man.hash.add(self.bind_global_refs_locally); + man.hash.add(self.compress_debug_sections); + man.hash.add(comp.config.any_sanitize_thread); + man.hash.addOptionalBytes(comp.sysroot); // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. _ = try man.hit(); @@ -2421,15 +2506,15 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // copy when generating relocatables. Normally, we would expect `lld -r` to work. // However, because LLD wants to resolve BPF relocations which it shouldn't, it fails // before even generating the relocatable. - if (self.base.options.output_mode == .Obj and - (self.base.options.lto or target.isBpfFreestanding())) + if (output_mode == .Obj and + (comp.config.lto or target.isBpfFreestanding())) { // In this case we must do a simple file copy // here. TODO: think carefully about how we can avoid this redundant operation when doing // build-obj. See also the corresponding TODO in linkAsArchive. const the_object_path = blk: { - if (self.base.options.objects.len != 0) - break :blk self.base.options.objects[0].path; + if (comp.objects.len != 0) + break :blk comp.objects[0].path; if (comp.c_object_table.count() != 0) break :blk comp.c_object_table.keys()[0].status.success.object_path; @@ -2448,7 +2533,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } } else { // Create an LLD command line and invoke it. - var argv = std.ArrayList([]const u8).init(self.base.allocator); + var argv = std.ArrayList([]const u8).init(gpa); defer argv.deinit(); // We will invoke ourselves as a child process to gain access to LLD. // This is necessary because LLD does not behave properly as a library - @@ -2461,46 +2546,49 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append("--error-limit=0"); - if (self.base.options.sysroot) |sysroot| { + if (comp.sysroot) |sysroot| { try argv.append(try std.fmt.allocPrint(arena, "--sysroot={s}", .{sysroot})); } - if (self.base.options.lto) { - switch (self.base.options.optimize_mode) { + if (comp.config.lto) { + switch (comp.root_mod.optimize_mode) { .Debug => {}, .ReleaseSmall => try argv.append("--lto-O2"), .ReleaseFast, .ReleaseSafe => try argv.append("--lto-O3"), } } - try argv.append(try std.fmt.allocPrint(arena, "-O{d}", .{ - self.base.options.linker_optimization, - })); + switch (comp.root_mod.optimize_mode) { + .Debug => {}, + .ReleaseSmall => try argv.append("-O2"), + .ReleaseFast, .ReleaseSafe => try argv.append("-O3"), + } - if (self.base.options.entry) |entry| { - try argv.append("--entry"); - try argv.append(entry); + if (self.entry_name) |name| { + try argv.appendSlice(&.{ "--entry", name }); } - for (self.base.options.force_undefined_symbols.keys()) |sym| { + for (comp.force_undefined_symbols.keys()) |sym| { try argv.append("-u"); try argv.append(sym); } - switch (self.base.options.hash_style) { + switch (self.hash_style) { .gnu => try argv.append("--hash-style=gnu"), .sysv => try argv.append("--hash-style=sysv"), .both => {}, // this is the default } - if (self.base.options.output_mode == .Exe) { - try argv.append("-z"); - try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size})); + if (output_mode == .Exe) { + try argv.appendSlice(&.{ + "-z", + try std.fmt.allocPrint(arena, "stack-size={d}", .{self.base.stack_size}), + }); - switch (self.base.options.build_id) { + switch (self.base.build_id) { .none => {}, .fast, .uuid, .sha1, .md5 => { try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{ - @tagName(self.base.options.build_id), + @tagName(self.base.build_id), })); }, .hexstring => |hs| { @@ -2511,85 +2599,83 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } } - if (self.base.options.image_base_override) |image_base| { - try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base})); - } + try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base})); - if (self.base.options.linker_script) |linker_script| { + if (self.linker_script) |linker_script| { try argv.append("-T"); try argv.append(linker_script); } - if (self.base.options.sort_section) |how| { + if (self.sort_section) |how| { const arg = try std.fmt.allocPrint(arena, "--sort-section={s}", .{@tagName(how)}); try argv.append(arg); } - if (gc_sections) { + if (self.base.gc_sections) { try argv.append("--gc-sections"); } - if (self.base.options.print_gc_sections) { + if (self.base.print_gc_sections) { try argv.append("--print-gc-sections"); } - if (self.base.options.print_icf_sections) { + if (self.print_icf_sections) { try argv.append("--print-icf-sections"); } - if (self.base.options.print_map) { + if (self.print_map) { try argv.append("--print-map"); } - if (self.base.options.eh_frame_hdr) { + if (comp.link_eh_frame_hdr) { try argv.append("--eh-frame-hdr"); } - if (self.base.options.emit_relocs) { + if (self.emit_relocs) { try argv.append("--emit-relocs"); } - if (self.base.options.rdynamic) { + if (comp.config.rdynamic) { try argv.append("--export-dynamic"); } - if (self.base.options.strip) { + if (comp.config.debug_format == .strip) { try argv.append("-s"); } - if (self.base.options.z_nodelete) { + if (self.z_nodelete) { try argv.append("-z"); try argv.append("nodelete"); } - if (self.base.options.z_notext) { + if (self.z_notext) { try argv.append("-z"); try argv.append("notext"); } - if (self.base.options.z_defs) { + if (self.z_defs) { try argv.append("-z"); try argv.append("defs"); } - if (self.base.options.z_origin) { + if (self.z_origin) { try argv.append("-z"); try argv.append("origin"); } - if (self.base.options.z_nocopyreloc) { + if (self.z_nocopyreloc) { try argv.append("-z"); try argv.append("nocopyreloc"); } - if (self.base.options.z_now) { + if (self.z_now) { // LLD defaults to -zlazy try argv.append("-znow"); } - if (!self.base.options.z_relro) { + if (!self.z_relro) { // LLD defaults to -zrelro try argv.append("-znorelro"); } - if (self.base.options.z_common_page_size) |size| { + if (self.z_common_page_size) |size| { try argv.append("-z"); try argv.append(try std.fmt.allocPrint(arena, "common-page-size={d}", .{size})); } - if (self.base.options.z_max_page_size) |size| { + if (self.z_max_page_size) |size| { try argv.append("-z"); try argv.append(try std.fmt.allocPrint(arena, "max-page-size={d}", .{size})); } @@ -2604,7 +2690,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append(arg); } - if (self.base.options.link_mode == .Static) { + if (link_mode == .Static) { if (target.cpu.arch.isArmOrThumb()) { try argv.append("-Bstatic"); } else { @@ -2614,7 +2700,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append("-shared"); } - if (self.base.options.pie and self.base.options.output_mode == .Exe) { + if (comp.config.pie and output_mode == .Exe) { try argv.append("-pie"); } @@ -2631,29 +2717,29 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append(full_out_path); // csu prelude - const csu = try CsuObjects.init(arena, self.base.options, comp); + const csu = try CsuObjects.init(arena, comp); if (csu.crt0) |v| try argv.append(v); if (csu.crti) |v| try argv.append(v); if (csu.crtbegin) |v| try argv.append(v); // rpaths - var rpath_table = std.StringHashMap(void).init(self.base.allocator); + var rpath_table = std.StringHashMap(void).init(gpa); defer rpath_table.deinit(); - for (self.base.options.rpath_list) |rpath| { + for (self.base.rpath_list) |rpath| { if ((try rpath_table.fetchPut(rpath, {})) == null) { try argv.append("-rpath"); try argv.append(rpath); } } - for (self.base.options.symbol_wrap_set.keys()) |symbol_name| { + for (self.symbol_wrap_set.keys()) |symbol_name| { try argv.appendSlice(&.{ "-wrap", symbol_name }); } - if (self.base.options.each_lib_rpath) { + if (self.each_lib_rpath) { var test_path = std.ArrayList(u8).init(arena); - for (self.base.options.lib_dirs) |lib_dir_path| { - for (self.base.options.system_libs.keys()) |link_lib| { + for (self.lib_dirs) |lib_dir_path| { + for (comp.system_libs.keys()) |link_lib| { if (!(try self.accessLibPath(&test_path, null, lib_dir_path, link_lib, .Dynamic))) continue; if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) { @@ -2662,7 +2748,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } } } - for (self.base.options.objects) |obj| { + for (comp.objects) |obj| { if (Compilation.classifyFileExt(obj.path) == .shared_library) { const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue; if (obj.loption) continue; @@ -2675,19 +2761,19 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } } - for (self.base.options.lib_dirs) |lib_dir| { + for (self.lib_dirs) |lib_dir| { try argv.append("-L"); try argv.append(lib_dir); } - if (self.base.options.link_libc) { - if (self.base.options.libc_installation) |libc_installation| { + if (comp.config.link_libc) { + if (comp.libc_installation) |libc_installation| { try argv.append("-L"); try argv.append(libc_installation.crt_dir.?); } if (have_dynamic_linker) { - if (self.base.options.dynamic_linker) |dynamic_linker| { + if (target.dynamic_linker.get()) |dynamic_linker| { try argv.append("-dynamic-linker"); try argv.append(dynamic_linker); } @@ -2695,11 +2781,11 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } if (is_dyn_lib) { - if (self.base.options.soname) |soname| { + if (self.soname) |soname| { try argv.append("-soname"); try argv.append(soname); } - if (self.base.options.version_script) |version_script| { + if (self.version_script) |version_script| { try argv.append("-version-script"); try argv.append(version_script); } @@ -2707,7 +2793,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // Positional arguments to the linker such as object files. var whole_archive = false; - for (self.base.options.objects) |obj| { + for (comp.objects) |obj| { if (obj.must_link and !whole_archive) { try argv.append("-whole-archive"); whole_archive = true; @@ -2735,15 +2821,14 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v try argv.append(p); } - // TSAN - if (self.base.options.tsan) { + if (comp.config.any_sanitize_thread) { try argv.append(comp.tsan_static_lib.?.full_object_path); } // libc if (is_exe_or_dyn_lib and - !self.base.options.skip_linker_dependencies and - !self.base.options.link_libc) + !comp.skip_linker_dependencies and + !comp.config.link_libc) { if (comp.libc_static_lib) |lib| { try argv.append(lib.full_object_path); @@ -2752,8 +2837,8 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v // Shared libraries. if (is_exe_or_dyn_lib) { - const system_libs = self.base.options.system_libs.keys(); - const system_libs_values = self.base.options.system_libs.values(); + const system_libs = comp.system_libs.keys(); + const system_libs_values = comp.system_libs.values(); // Worst-case, we need an --as-needed argument for every lib, as well // as one before and one after. @@ -2788,21 +2873,21 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } // libc++ dep - if (self.base.options.link_libcpp) { + if (comp.config.link_libcpp) { try argv.append(comp.libcxxabi_static_lib.?.full_object_path); try argv.append(comp.libcxx_static_lib.?.full_object_path); } // libunwind dep - if (self.base.options.link_libunwind) { + if (comp.config.link_libunwind) { try argv.append(comp.libunwind_static_lib.?.full_object_path); } // libc dep - self.error_flags.missing_libc = false; - if (self.base.options.link_libc) { - if (self.base.options.libc_installation != null) { - const needs_grouping = self.base.options.link_mode == .Static; + comp.link_error_flags.missing_libc = false; + if (comp.config.link_libc) { + if (comp.libc_installation != null) { + const needs_grouping = link_mode == .Static; if (needs_grouping) try argv.append("--start-group"); try argv.appendSlice(target_util.libcFullLinkFlags(target)); if (needs_grouping) try argv.append("--end-group"); @@ -2815,12 +2900,12 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a")); } else if (target.isMusl()) { - try argv.append(try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) { + try argv.append(try comp.get_libc_crt_file(arena, switch (link_mode) { .Static => "libc.a", .Dynamic => "libc.so", })); } else { - self.error_flags.missing_libc = true; + comp.link_error_flags.missing_libc = true; } } } @@ -2836,21 +2921,21 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v if (csu.crtend) |v| try argv.append(v); if (csu.crtn) |v| try argv.append(v); - if (allow_shlib_undefined) { + if (self.base.allow_shlib_undefined) { try argv.append("--allow-shlib-undefined"); } - switch (self.base.options.compress_debug_sections) { + switch (self.compress_debug_sections) { .none => {}, .zlib => try argv.append("--compress-debug-sections=zlib"), .zstd => try argv.append("--compress-debug-sections=zstd"), } - if (self.base.options.bind_global_refs_locally) { + if (self.bind_global_refs_locally) { try argv.append("-Bsymbolic"); } - if (self.base.options.verbose_link) { + if (comp.verbose_link) { // Skip over our own name so that the LLD linker name is the first argv item. Compilation.dump_argv(argv.items[1..]); } @@ -2920,7 +3005,7 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v } } - if (!self.base.options.disable_lld_caching) { + if (!self.base.disable_lld_caching) { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { @@ -2937,7 +3022,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 +3031,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 +3088,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.?]; @@ -3040,7 +3128,8 @@ fn writePhdrTable(self: *Elf) !void { } fn writeElfHeader(self: *Elf) !void { - if (self.misc_errors.items.len > 0) return; // We had errors, so skip flushing to render the output unusable + const comp = self.base.comp; + if (comp.link_errors.items.len > 0) return; // We had errors, so skip flushing to render the output unusable var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined; @@ -3054,7 +3143,8 @@ fn writeElfHeader(self: *Elf) !void { }; index += 1; - const endian = self.base.options.target.cpu.arch.endian(); + const target = comp.root_mod.resolved_target.result; + const endian = target.cpu.arch.endian(); hdr_buf[index] = switch (endian) { .little => elf.ELFDATA2LSB, .big => elf.ELFDATA2MSB, @@ -3072,10 +3162,12 @@ fn writeElfHeader(self: *Elf) !void { assert(index == 16); - const elf_type: elf.ET = switch (self.base.options.output_mode) { - .Exe => if (self.base.options.pie) .DYN else .EXEC, + const output_mode = comp.config.output_mode; + const link_mode = comp.config.link_mode; + const elf_type: elf.ET = switch (output_mode) { + .Exe => if (comp.config.pie) .DYN else .EXEC, .Obj => .REL, - .Lib => switch (self.base.options.link_mode) { + .Lib => switch (link_mode) { .Static => @as(elf.ET, .REL), .Dynamic => .DYN, }, @@ -3083,7 +3175,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; @@ -3201,7 +3293,6 @@ pub fn updateExports( @panic("Attempted to compile for object format that was disabled by build configuration"); } if (self.llvm_object) |llvm_object| return llvm_object.updateExports(mod, exported, exports); - if (self.base.options.emit == null) return; return self.zigObjectPtr().?.updateExports(self, mod, exported, exports); } @@ -3220,6 +3311,9 @@ pub fn deleteDeclExport( } fn addLinkerDefinedSymbols(self: *Elf) !void { + const comp = self.base.comp; + const gpa = comp.gpa; + const linker_defined_index = self.linker_defined_index orelse return; const linker_defined = self.file(linker_defined_index).?.linker_defined; self.dynamic_index = try linker_defined.addGlobal("_DYNAMIC", self); @@ -3234,7 +3328,7 @@ fn addLinkerDefinedSymbols(self: *Elf) !void { self.plt_index = try linker_defined.addGlobal("_PROCEDURE_LINKAGE_TABLE_", self); self.end_index = try linker_defined.addGlobal("_end", self); - if (self.base.options.eh_frame_hdr) { + if (comp.link_eh_frame_hdr) { self.gnu_eh_frame_hdr_index = try linker_defined.addGlobal("__GNU_EH_FRAME_HDR", self); } @@ -3248,7 +3342,6 @@ fn addLinkerDefinedSymbols(self: *Elf) !void { for (self.shdrs.items) |shdr| { if (self.getStartStopBasename(shdr)) |name| { - const gpa = self.base.allocator; try self.start_stop_indexes.ensureUnusedCapacity(gpa, 2); const start = try std.fmt.allocPrintZ(gpa, "__start_{s}", .{name}); @@ -3265,6 +3358,9 @@ fn addLinkerDefinedSymbols(self: *Elf) !void { } fn allocateLinkerDefinedSymbols(self: *Elf) void { + const comp = self.base.comp; + const link_mode = comp.config.link_mode; + // _DYNAMIC if (self.dynamic_section_index) |shndx| { const shdr = &self.shdrs.items[shndx]; @@ -3276,7 +3372,7 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void { // __ehdr_start { const symbol_ptr = self.symbol(self.ehdr_start_index.?); - symbol_ptr.value = self.calcImageBase(); + symbol_ptr.value = self.image_base; symbol_ptr.output_section_index = 1; } @@ -3347,7 +3443,7 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void { // __rela_iplt_start, __rela_iplt_end if (self.rela_dyn_section_index) |shndx| blk: { - if (self.base.options.link_mode != .Static or self.base.options.pie) break :blk; + if (link_mode != .Static or comp.config.pie) break :blk; const shdr = &self.shdrs.items[shndx]; const end_addr = shdr.sh_addr + shdr.sh_size; const start_addr = end_addr - self.calcNumIRelativeRelocs() * @sizeOf(elf.Elf64_Rela); @@ -3394,6 +3490,8 @@ fn initOutputSections(self: *Elf) !void { } fn initSyntheticSections(self: *Elf) !void { + const comp = self.base.comp; + const target = comp.root_mod.resolved_target.result; const ptr_size = self.ptrWidthBytes(); const needs_eh_frame = for (self.objects.items) |index| { @@ -3408,7 +3506,7 @@ fn initSyntheticSections(self: *Elf) !void { .offset = std.math.maxInt(u64), }); - if (self.base.options.eh_frame_hdr) { + if (comp.link_eh_frame_hdr) { self.eh_frame_hdr_section_index = try self.addSection(.{ .name = ".eh_frame_hdr", .type = elf.SHT_PROGBITS, @@ -3502,8 +3600,8 @@ fn initSyntheticSections(self: *Elf) !void { // In this case, if we do generate .interp section and segment, we will get // a segfault in the dynamic linker trying to load a binary that is static // and doesn't contain .dynamic section. - if (self.isStatic() and !self.base.options.pie) break :blk false; - break :blk self.base.options.dynamic_linker != null; + if (self.base.isStatic() and !comp.config.pie) break :blk false; + break :blk target.dynamic_linker.get() != null; }; if (needs_interp) { self.interp_section_index = try self.addSection(.{ @@ -3515,7 +3613,7 @@ fn initSyntheticSections(self: *Elf) !void { }); } - if (self.isDynLib() or self.shared_objects.items.len > 0 or self.base.options.pie) { + if (self.base.isDynLib() or self.shared_objects.items.len > 0 or comp.config.pie) { self.dynstrtab_section_index = try self.addSection(.{ .name = ".dynstr", .flags = elf.SHF_ALLOC, @@ -3613,7 +3711,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; @@ -3700,7 +3798,7 @@ fn initSpecialPhdrs(self: *Elf) !void { self.phdr_gnu_stack_index = try self.addPhdr(.{ .type = elf.PT_GNU_STACK, .flags = elf.PF_W | elf.PF_R, - .memsz = self.base.options.stack_size_override orelse 0, + .memsz = self.base.stack_size, .@"align" = 1, }); @@ -3732,7 +3830,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, @@ -3806,8 +3904,8 @@ fn setDynamicSection(self: *Elf, rpaths: []const []const u8) !void { try self.dynamic.addNeeded(shared_object, self); } - if (self.isDynLib()) { - if (self.base.options.soname) |soname| { + if (self.base.isDynLib()) { + if (self.soname) |soname| { try self.dynamic.setSoname(soname, self); } } @@ -3821,8 +3919,9 @@ fn sortDynamicSymtab(self: *Elf) void { } fn setVersionSymtab(self: *Elf) !void { + const gpa = self.base.comp.gpa; if (self.versym_section_index == null) return; - try self.versym.resize(self.base.allocator, self.dynsym.count()); + try self.versym.resize(gpa, self.dynsym.count()); self.versym.items[0] = elf.VER_NDX_LOCAL; for (self.dynsym.entries.items, 1..) |entry, i| { const sym = self.symbol(entry.symbol_index); @@ -3872,7 +3971,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 +4076,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 +4103,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 +4286,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 +4344,7 @@ fn updateSectionSizes(self: *Elf) !void { } if (self.interp_section_index) |index| { - self.shdrs.items[index].sh_size = self.base.options.dynamic_linker.?.len + 1; + self.shdrs.items[index].sh_size = target.dynamic_linker.get().?.len + 1; } if (self.hash_section_index) |index| { @@ -4453,7 +4553,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 +4791,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 +4879,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 +4952,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,17 +5035,18 @@ 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 = target.dynamic_linker.get().?; + @memcpy(buffer[0..interp.len], interp); + buffer[interp.len] = 0; + const contents = buffer[0 .. interp.len + 1]; const shdr = self.shdrs.items[shndx]; - const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow; - var buffer = try gpa.alloc(u8, sh_size); - defer gpa.free(buffer); - const dylinker = self.base.options.dynamic_linker.?; - @memcpy(buffer[0..dylinker.len], dylinker); - buffer[dylinker.len] = 0; - try self.base.file.?.pwriteAll(buffer, shdr.sh_offset); + assert(shdr.sh_size == contents.len); + try self.base.file.?.pwriteAll(contents, shdr.sh_offset); } if (self.hash_section_index) |shndx| { @@ -5065,7 +5166,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 +5236,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 +5261,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 +5322,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 +5401,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 { @@ -5393,9 +5496,11 @@ const CsuObjects = struct { crtend: ?[]const u8 = null, crtn: ?[]const u8 = null, - fn init(arena: mem.Allocator, link_options: link.Options, comp: *const Compilation) !CsuObjects { + const InitArgs = struct {}; + + fn init(arena: Allocator, comp: *const Compilation) !CsuObjects { // crt objects are only required for libc. - if (!link_options.link_libc) return CsuObjects{}; + if (!comp.config.link_libc) return .{}; var result: CsuObjects = .{}; @@ -5406,19 +5511,21 @@ const CsuObjects = struct { dynamic_pie, static_exe, static_pie, - } = switch (link_options.output_mode) { + } = switch (comp.config.output_mode) { .Obj => return CsuObjects{}, - .Lib => switch (link_options.link_mode) { + .Lib => switch (comp.config.link_mode) { .Dynamic => .dynamic_lib, .Static => return CsuObjects{}, }, - .Exe => switch (link_options.link_mode) { - .Dynamic => if (link_options.pie) .dynamic_pie else .dynamic_exe, - .Static => if (link_options.pie) .static_pie else .static_exe, + .Exe => switch (comp.config.link_mode) { + .Dynamic => if (comp.config.pie) .dynamic_pie else .dynamic_exe, + .Static => if (comp.config.pie) .static_pie else .static_exe, }, }; - if (link_options.target.isAndroid()) { + const target = comp.root_mod.resolved_target.result; + + if (target.isAndroid()) { switch (mode) { // zig fmt: off .dynamic_lib => result.set( null, null, "crtbegin_so.o", "crtend_so.o", null ), @@ -5429,7 +5536,7 @@ const CsuObjects = struct { // zig fmt: on } } else { - switch (link_options.target.os.tag) { + switch (target.os.tag) { .linux => { switch (mode) { // zig fmt: off @@ -5440,7 +5547,7 @@ const CsuObjects = struct { .static_pie => result.set( "rcrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ), // zig fmt: on } - if (link_options.libc_installation) |_| { + if (comp.libc_installation) |_| { // hosted-glibc provides crtbegin/end objects in platform/compiler-specific dirs // and they are not known at comptime. For now null-out crtbegin/end objects; // there is no feature loss, zig has never linked those objects in before. @@ -5448,7 +5555,7 @@ const CsuObjects = struct { result.crtend = null; } else { // Bundled glibc only has Scrt1.o . - if (result.crt0 != null and link_options.target.isGnuLibC()) result.crt0 = "Scrt1.o"; + if (result.crt0 != null and target.isGnuLibC()) result.crt0 = "Scrt1.o"; } }, .dragonfly => switch (mode) { @@ -5510,16 +5617,16 @@ const CsuObjects = struct { } // Convert each object to a full pathname. - if (link_options.libc_installation) |lci| { + if (comp.libc_installation) |lci| { const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir; - switch (link_options.target.os.tag) { + switch (target.os.tag) { .dragonfly => { if (result.crt0) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); if (result.crti) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); if (result.crtn) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* }); var gccv: []const u8 = undefined; - if (link_options.target.os.version_range.semver.isAtLeast(.{ .major = 5, .minor = 4, .patch = 0 }) orelse true) { + if (target.os.version_range.semver.isAtLeast(.{ .major = 5, .minor = 4, .patch = 0 }) orelse true) { gccv = "gcc80"; } else { gccv = "gcc54"; @@ -5576,39 +5683,6 @@ const CsuObjects = struct { } }; -pub fn calcImageBase(self: Elf) u64 { - if (self.isDynLib()) return 0; - if (self.isExe() and self.base.options.pie) return 0; - return self.base.options.image_base_override orelse switch (self.ptr_width) { - .p32 => 0x1000, - .p64 => 0x1000000, - }; -} - -pub fn isStatic(self: Elf) bool { - return self.base.options.link_mode == .Static; -} - -pub fn isObject(self: Elf) bool { - return self.base.options.output_mode == .Obj; -} - -pub fn isExe(self: Elf) bool { - return self.base.options.output_mode == .Exe; -} - -pub fn isStaticLib(self: Elf) bool { - return self.base.options.output_mode == .Lib and self.isStatic(); -} - -pub fn isRelocatable(self: Elf) bool { - return self.isObject() or self.isStaticLib(); -} - -pub fn isDynLib(self: Elf) bool { - return self.base.options.output_mode == .Lib and !self.isStatic(); -} - pub fn isZigSection(self: Elf, shndx: u16) bool { inline for (&[_]?u16{ self.zig_text_section_index, @@ -5648,8 +5722,9 @@ fn addPhdr(self: *Elf, opts: struct { filesz: u64 = 0, memsz: u64 = 0, }) error{OutOfMemory}!u16 { + const gpa = self.base.comp.gpa; const index = @as(u16, @intCast(self.phdrs.items.len)); - try self.phdrs.append(self.base.allocator, .{ + try self.phdrs.append(gpa, .{ .p_type = opts.type, .p_flags = opts.flags, .p_offset = opts.offset, @@ -5694,7 +5769,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.* = .{ @@ -5798,8 +5873,9 @@ pub fn atom(self: *Elf, atom_index: Atom.Index) ?*Atom { } pub fn addAtom(self: *Elf) !Atom.Index { + const gpa = self.base.comp.gpa; const index = @as(Atom.Index, @intCast(self.atoms.items.len)); - const atom_ptr = try self.atoms.addOne(self.base.allocator); + const atom_ptr = try self.atoms.addOne(gpa); atom_ptr.* = .{ .atom_index = index }; return index; } @@ -5821,14 +5897,15 @@ pub fn symbol(self: *Elf, sym_index: Symbol.Index) *Symbol { } pub fn addSymbol(self: *Elf) !Symbol.Index { - try self.symbols.ensureUnusedCapacity(self.base.allocator, 1); + const gpa = self.base.comp.gpa; + try self.symbols.ensureUnusedCapacity(gpa, 1); const index = blk: { if (self.symbols_free_list.popOrNull()) |index| { log.debug(" (reusing symbol index {d})", .{index}); break :blk index; } else { log.debug(" (allocating symbol index {d})", .{self.symbols.items.len}); - const index = @as(Symbol.Index, @intCast(self.symbols.items.len)); + const index: Symbol.Index = @intCast(self.symbols.items.len); _ = self.symbols.addOneAssumeCapacity(); break :blk index; } @@ -5838,8 +5915,9 @@ pub fn addSymbol(self: *Elf) !Symbol.Index { } pub fn addSymbolExtra(self: *Elf, extra: Symbol.Extra) !u32 { + const gpa = self.base.comp.gpa; const fields = @typeInfo(Symbol.Extra).Struct.fields; - try self.symbols_extra.ensureUnusedCapacity(self.base.allocator, fields.len); + try self.symbols_extra.ensureUnusedCapacity(gpa, fields.len); return self.addSymbolExtraAssumeCapacity(extra); } @@ -5887,11 +5965,12 @@ 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) { const index = try self.addSymbol(); + log.debug("added symbol '{s}' at index {d}", .{ name, index }); const global = self.symbol(index); global.name_offset = name_off; global.flags.global = true; @@ -5923,11 +6002,11 @@ 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) { - const index = @as(ComdatGroupOwner.Index, @intCast(self.comdat_groups_owners.items.len)); + const index: ComdatGroupOwner.Index = @intCast(self.comdat_groups_owners.items.len); const owner = try self.comdat_groups_owners.addOne(gpa); owner.* = .{}; gop.value_ptr.* = index; @@ -5939,8 +6018,9 @@ pub fn getOrCreateComdatGroupOwner(self: *Elf, name: [:0]const u8) !GetOrCreateC } pub fn addComdatGroup(self: *Elf) !ComdatGroup.Index { + const gpa = self.base.comp.gpa; const index = @as(ComdatGroup.Index, @intCast(self.comdat_groups.items.len)); - _ = try self.comdat_groups.addOne(self.base.allocator); + _ = try self.comdat_groups.addOne(gpa); return index; } @@ -5971,7 +6051,7 @@ pub fn tlsAddress(self: *Elf) u64 { } const ErrorWithNotes = struct { - /// Allocated index in misc_errors array. + /// Allocated index in comp.link_errors array. index: usize, /// Next available note slot. @@ -5983,8 +6063,9 @@ const ErrorWithNotes = struct { comptime format: []const u8, args: anytype, ) error{OutOfMemory}!void { - const gpa = elf_file.base.allocator; - const err_msg = &elf_file.misc_errors.items[err.index]; + const comp = elf_file.base.comp; + const gpa = comp.gpa; + const err_msg = &comp.link_errors.items[err.index]; err_msg.msg = try std.fmt.allocPrint(gpa, format, args); } @@ -5994,8 +6075,9 @@ const ErrorWithNotes = struct { comptime format: []const u8, args: anytype, ) error{OutOfMemory}!void { - const gpa = elf_file.base.allocator; - const err_msg = &elf_file.misc_errors.items[err.index]; + const comp = elf_file.base.comp; + const gpa = comp.gpa; + const err_msg = &comp.link_errors.items[err.index]; assert(err.note_slot < err_msg.notes.len); err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) }; err.note_slot += 1; @@ -6003,14 +6085,18 @@ const ErrorWithNotes = struct { }; pub fn addErrorWithNotes(self: *Elf, note_count: usize) error{OutOfMemory}!ErrorWithNotes { - try self.misc_errors.ensureUnusedCapacity(self.base.allocator, 1); + const comp = self.base.comp; + const gpa = comp.gpa; + try comp.link_errors.ensureUnusedCapacity(gpa, 1); return self.addErrorWithNotesAssumeCapacity(note_count); } fn addErrorWithNotesAssumeCapacity(self: *Elf, note_count: usize) error{OutOfMemory}!ErrorWithNotes { - const index = self.misc_errors.items.len; - const err = self.misc_errors.addOneAssumeCapacity(); - err.* = .{ .msg = undefined, .notes = try self.base.allocator.alloc(link.File.ErrorMsg, note_count) }; + const comp = self.base.comp; + const gpa = comp.gpa; + const index = comp.link_errors.items.len; + const err = comp.link_errors.addOneAssumeCapacity(); + err.* = .{ .msg = undefined, .notes = try gpa.alloc(link.File.ErrorMsg, note_count) }; return .{ .index = index }; } @@ -6020,9 +6106,10 @@ pub fn getShString(self: Elf, off: u32) [:0]const u8 { } pub fn insertShString(self: *Elf, name: [:0]const u8) error{OutOfMemory}!u32 { + const gpa = self.base.comp.gpa; const off = @as(u32, @intCast(self.shstrtab.items.len)); - try self.shstrtab.ensureUnusedCapacity(self.base.allocator, name.len + 1); - self.shstrtab.writer(self.base.allocator).print("{s}\x00", .{name}) catch unreachable; + try self.shstrtab.ensureUnusedCapacity(gpa, name.len + 1); + self.shstrtab.writer(gpa).print("{s}\x00", .{name}) catch unreachable; return off; } @@ -6032,17 +6119,19 @@ pub fn getDynString(self: Elf, off: u32) [:0]const u8 { } pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 { + const gpa = self.base.comp.gpa; const off = @as(u32, @intCast(self.dynstrtab.items.len)); - try self.dynstrtab.ensureUnusedCapacity(self.base.allocator, name.len + 1); - self.dynstrtab.writer(self.base.allocator).print("{s}\x00", .{name}) catch unreachable; + try self.dynstrtab.ensureUnusedCapacity(gpa, name.len + 1); + self.dynstrtab.writer(gpa).print("{s}\x00", .{name}) catch unreachable; return off; } fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void { - const gpa = self.base.allocator; + const comp = self.base.comp; + const gpa = comp.gpa; const max_notes = 4; - try self.misc_errors.ensureUnusedCapacity(gpa, undefs.count()); + try comp.link_errors.ensureUnusedCapacity(gpa, undefs.count()); var it = undefs.iterator(); while (it.next()) |entry| { @@ -6427,6 +6516,13 @@ const RelaSectionTable = std.AutoArrayHashMapUnmanaged(u32, RelaSection); pub const R_X86_64_ZIG_GOT32 = elf.R_X86_64_NUM + 1; pub const R_X86_64_ZIG_GOTPCREL = elf.R_X86_64_NUM + 2; +fn defaultEntrySymbolName(cpu_arch: std.Target.Cpu.Arch) []const u8 { + return switch (cpu_arch) { + .mips, .mipsel, .mips64, .mips64el => "__start", + else => "_start", + }; +} + const std = @import("std"); const build_options = @import("build_options"); const builtin = @import("builtin"); diff --git a/src/link/Elf/Archive.zig b/src/link/Elf/Archive.zig index 686647a290fe..65135abbc77a 100644 --- a/src/link/Elf/Archive.zig +++ b/src/link/Elf/Archive.zig @@ -20,7 +20,8 @@ pub fn deinit(self: *Archive, allocator: Allocator) void { } pub fn parse(self: *Archive, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; var stream = std.io.fixedBufferStream(self.data); const reader = stream.reader(); @@ -150,7 +151,8 @@ pub const ArSymtab = struct { const hdr = setArHdr(.{ .name = .symtab, .size = @intCast(ar.size(.p64)) }); try writer.writeAll(mem.asBytes(&hdr)); - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; var offsets = std.AutoHashMap(File.Index, u64).init(gpa); defer offsets.deinit(); try offsets.ensureUnusedCapacity(@intCast(elf_file.objects.items.len + 1)); diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index ec12f177212d..a7b876f619a2 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -227,7 +227,8 @@ pub fn grow(self: *Atom, elf_file: *Elf) !void { pub fn free(self: *Atom, elf_file: *Elf) void { log.debug("freeAtom {d} ({s})", .{ self.atom_index, self.name(elf_file) }); - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const shndx = self.outputShndx().?; const meta = elf_file.last_atom_and_free_list_table.getPtr(shndx).?; const free_list = &meta.free_list; @@ -352,7 +353,8 @@ pub fn markFdesDead(self: Atom, elf_file: *Elf) void { } pub fn addReloc(self: Atom, elf_file: *Elf, reloc: elf.Elf64_Rela) !void { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const file_ptr = self.file(elf_file).?; assert(file_ptr == .zig_object); const zig_object = file_ptr.zig_object; @@ -375,8 +377,8 @@ pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) bool { } pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void { - const is_static = elf_file.isStatic(); - const is_dyn_lib = elf_file.isDynLib(); + const is_static = elf_file.base.isStatic(); + const is_dyn_lib = elf_file.base.isDynLib(); const file_ptr = self.file(elf_file).?; const rels = self.relocs(elf_file); var i: usize = 0; @@ -543,7 +545,7 @@ fn scanReloc( try self.reportPicError(symbol, rel, elf_file), .copyrel => { - if (elf_file.base.options.z_nocopyreloc) { + if (elf_file.z_nocopyreloc) { if (symbol.isAbs(elf_file)) try self.reportNoPicError(symbol, rel, elf_file) else @@ -553,9 +555,9 @@ fn scanReloc( }, .dyn_copyrel => { - if (is_writeable or elf_file.base.options.z_nocopyreloc) { + if (is_writeable or elf_file.z_nocopyreloc) { if (!is_writeable) { - if (elf_file.base.options.z_notext) { + if (elf_file.z_notext) { elf_file.has_text_reloc = true; } else { try self.reportTextRelocError(symbol, rel, elf_file); @@ -587,7 +589,7 @@ fn scanReloc( .dynrel, .baserel, .ifunc => { if (!is_writeable) { - if (elf_file.base.options.z_notext) { + if (elf_file.z_notext) { elf_file.has_text_reloc = true; } else { try self.reportTextRelocError(symbol, rel, elf_file); @@ -657,11 +659,12 @@ fn dynAbsRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction { } fn outputType(elf_file: *Elf) u2 { - assert(!elf_file.isRelocatable()); - return switch (elf_file.base.options.output_mode) { + const comp = elf_file.base.comp; + assert(!elf_file.base.isRelocatable()); + return switch (elf_file.base.comp.config.output_mode) { .Obj => unreachable, .Lib => 0, - .Exe => if (elf_file.base.options.pie) 1 else 2, + .Exe => if (comp.config.pie) 1 else 2, }; } @@ -746,6 +749,8 @@ fn reportUndefined( rel: elf.Elf64_Rela, undefs: anytype, ) !void { + const comp = elf_file.base.comp; + const gpa = comp.gpa; const rel_esym = switch (self.file(elf_file).?) { .zig_object => |x| x.elfSym(rel.r_sym()).*, .object => |x| x.symtab.items[rel.r_sym()], @@ -760,7 +765,7 @@ fn reportUndefined( { const gop = try undefs.getOrPut(sym_index); if (!gop.found_existing) { - gop.value_ptr.* = std.ArrayList(Atom.Index).init(elf_file.base.allocator); + gop.value_ptr.* = std.ArrayList(Atom.Index).init(gpa); } try gop.value_ptr.append(self.atom_index); } @@ -956,6 +961,8 @@ fn resolveDynAbsReloc( elf_file: *Elf, writer: anytype, ) !void { + const comp = elf_file.base.comp; + const gpa = comp.gpa; const P = self.value + rel.r_offset; const A = rel.r_addend; const S = @as(i64, @intCast(target.address(.{}, elf_file))); @@ -966,7 +973,7 @@ fn resolveDynAbsReloc( .shared_object => unreachable, inline else => |x| x.num_dynrelocs, }; - try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, num_dynrelocs); + try elf_file.rela_dyn.ensureUnusedCapacity(gpa, num_dynrelocs); switch (action) { .@"error", @@ -979,7 +986,7 @@ fn resolveDynAbsReloc( => try writer.writeInt(i32, @as(i32, @truncate(S + A)), .little), .dyn_copyrel => { - if (is_writeable or elf_file.base.options.z_nocopyreloc) { + if (is_writeable or elf_file.z_nocopyreloc) { elf_file.addRelaDynAssumeCapacity(.{ .offset = P, .sym = target.extra(elf_file).?.dynamic, diff --git a/src/link/Elf/LdScript.zig b/src/link/Elf/LdScript.zig index 803e2e146946..34cecf587993 100644 --- a/src/link/Elf/LdScript.zig +++ b/src/link/Elf/LdScript.zig @@ -14,7 +14,8 @@ pub const Error = error{ }; pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; var tokenizer = Tokenizer{ .source = data }; var tokens = std.ArrayList(Token).init(gpa); defer tokens.deinit(); diff --git a/src/link/Elf/LinkerDefined.zig b/src/link/Elf/LinkerDefined.zig index bc51cfc1f17d..b9fffa1a4a65 100644 --- a/src/link/Elf/LinkerDefined.zig +++ b/src/link/Elf/LinkerDefined.zig @@ -12,7 +12,8 @@ pub fn deinit(self: *LinkerDefined, allocator: Allocator) void { } pub fn addGlobal(self: *LinkerDefined, name: [:0]const u8, elf_file: *Elf) !u32 { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; try self.symtab.ensureUnusedCapacity(gpa, 1); try self.symbols.ensureUnusedCapacity(gpa, 1); const name_off = @as(u32, @intCast(self.strtab.items.len)); diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 6b0cc66c33e3..b6dced258ce0 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -54,7 +54,8 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { self.header = try reader.readStruct(elf.Elf64_Ehdr); - if (elf_file.base.options.target.cpu.arch != self.header.?.e_machine.toTargetCpuArch().?) { + const target = elf_file.base.comp.root_mod.resolved_target.result; + if (target.cpu.arch != self.header.?.e_machine.toTargetCpuArch().?) { try elf_file.reportParseError2( self.index, "invalid cpu architecture: {s}", @@ -65,7 +66,8 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { if (self.header.?.e_shnum == 0) return; - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; if (self.data.len < self.header.?.e_shoff or self.data.len < self.header.?.e_shoff + @as(u64, @intCast(self.header.?.e_shnum)) * @sizeOf(elf.Elf64_Shdr)) @@ -148,8 +150,10 @@ pub fn init(self: *Object, elf_file: *Elf) !void { } fn initAtoms(self: *Object, elf_file: *Elf) !void { + const comp = elf_file.base.comp; + const gpa = comp.gpa; const shdrs = self.shdrs.items; - try self.atoms.resize(elf_file.base.allocator, shdrs.len); + try self.atoms.resize(gpa, shdrs.len); @memset(self.atoms.items, 0); for (shdrs, 0..) |shdr, i| { @@ -184,7 +188,6 @@ fn initAtoms(self: *Object, elf_file: *Elf) !void { continue; } - const gpa = elf_file.base.allocator; const gop = try elf_file.getOrCreateComdatGroupOwner(group_signature); const comdat_group_index = try elf_file.addComdatGroup(); const comdat_group = elf_file.comdatGroup(comdat_group_index); @@ -247,7 +250,7 @@ fn addAtom(self: *Object, shdr: ElfShdr, shndx: u16, elf_file: *Elf) error{OutOf fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMemory}!u16 { const name = blk: { const name = self.getString(shdr.sh_name); - if (elf_file.isRelocatable()) break :blk name; + if (elf_file.base.isRelocatable()) break :blk name; if (shdr.sh_flags & elf.SHF_MERGE != 0) break :blk name; const sh_name_prefixes: []const [:0]const u8 = &.{ ".text", ".data.rel.ro", ".data", ".rodata", ".bss.rel.ro", ".bss", @@ -275,7 +278,7 @@ fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMem }; const flags = blk: { var flags = shdr.sh_flags; - if (!elf_file.isRelocatable()) { + if (!elf_file.base.isRelocatable()) { flags &= ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP | elf.SHF_GNU_RETAIN); } break :blk switch (@"type") { @@ -292,13 +295,14 @@ fn initOutputSection(self: Object, elf_file: *Elf, shdr: ElfShdr) error{OutOfMem } fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool { + const comp = elf_file.base.comp; const shdr = self.shdrs.items[index]; const name = self.getString(shdr.sh_name); const ignore = blk: { if (mem.startsWith(u8, name, ".note")) break :blk true; if (mem.startsWith(u8, name, ".comment")) break :blk true; if (mem.startsWith(u8, name, ".llvm_addrsig")) break :blk true; - if (elf_file.base.options.strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and + if (comp.config.debug_format == .strip and shdr.sh_flags & elf.SHF_ALLOC == 0 and mem.startsWith(u8, name, ".debug")) break :blk true; break :blk false; }; @@ -306,7 +310,8 @@ fn skipShdr(self: *Object, index: u16, elf_file: *Elf) bool { } fn initSymtab(self: *Object, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const first_global = self.first_global orelse self.symtab.items.len; try self.symbols.ensureTotalCapacityPrecise(gpa, self.symtab.items.len); @@ -338,7 +343,8 @@ fn parseEhFrame(self: *Object, shndx: u16, elf_file: *Elf) !void { return; }; - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const raw = self.shdrContents(shndx); const relocs = self.getRelocs(relocs_shndx); const fdes_start = self.fdes.items.len; @@ -438,6 +444,8 @@ fn filterRelocs( } pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void { + const comp = elf_file.base.comp; + const gpa = comp.gpa; for (self.atoms.items) |atom_index| { const atom = elf_file.atom(atom_index) orelse continue; if (!atom.flags.alive) continue; @@ -448,7 +456,7 @@ pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void { // TODO ideally, we don't have to decompress at this stage (should already be done) // and we just fetch the code slice. const code = try self.codeDecompressAlloc(elf_file, atom_index); - defer elf_file.base.allocator.free(code); + defer gpa.free(code); try atom.scanRelocs(elf_file, code, undefs); } else try atom.scanRelocs(elf_file, null, undefs); } @@ -512,7 +520,7 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void { } const is_import = blk: { - if (!elf_file.isDynLib()) break :blk false; + if (!elf_file.base.isDynLib()) break :blk false; const vis = @as(elf.STV, @enumFromInt(esym.st_other)); if (vis == .HIDDEN) break :blk false; break :blk true; @@ -621,7 +629,8 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { continue; } - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const atom_index = try elf_file.addAtom(); try self.atoms.append(gpa, atom_index); @@ -680,7 +689,8 @@ pub fn addAtomsToOutputSections(self: *Object, elf_file: *Elf) !void { const shdr = atom.inputShdr(elf_file); atom.output_section_index = self.initOutputSection(elf_file, shdr) catch unreachable; - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const gop = try elf_file.output_sections.getOrPut(gpa, atom.output_section_index); if (!gop.found_existing) gop.value_ptr.* = .{}; try gop.value_ptr.append(gpa, atom_index); @@ -740,7 +750,8 @@ pub fn addAtomsToRelaSections(self: Object, elf_file: *Elf) !void { shdr.sh_info = atom.outputShndx().?; shdr.sh_link = elf_file.symtab_section_index.?; - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const gop = try elf_file.output_rela_sections.getOrPut(gpa, atom.outputShndx().?); if (!gop.found_existing) gop.value_ptr.* = .{ .shndx = shndx }; try gop.value_ptr.atom_list.append(gpa, atom_index); @@ -748,7 +759,8 @@ pub fn addAtomsToRelaSections(self: Object, elf_file: *Elf) !void { } pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const start = self.first_global orelse self.symtab.items.len; try ar_symtab.symtab.ensureUnusedCapacity(gpa, self.symtab.items.len - start); @@ -855,7 +867,8 @@ pub fn shdrContents(self: Object, index: u32) []const u8 { /// Returns atom's code and optionally uncompresses data if required (for compressed sections). /// Caller owns the memory. pub fn codeDecompressAlloc(self: Object, elf_file: *Elf, atom_index: Atom.Index) ![]u8 { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const atom_ptr = elf_file.atom(atom_index).?; assert(atom_ptr.file_index == self.index); const data = self.shdrContents(atom_ptr.input_section_index); diff --git a/src/link/Elf/SharedObject.zig b/src/link/Elf/SharedObject.zig index 2a39477805ba..e5e87cc51e6d 100644 --- a/src/link/Elf/SharedObject.zig +++ b/src/link/Elf/SharedObject.zig @@ -47,13 +47,15 @@ pub fn deinit(self: *SharedObject, allocator: Allocator) void { } pub fn parse(self: *SharedObject, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; var stream = std.io.fixedBufferStream(self.data); const reader = stream.reader(); self.header = try reader.readStruct(elf.Elf64_Ehdr); - if (elf_file.base.options.target.cpu.arch != self.header.?.e_machine.toTargetCpuArch().?) { + const target = elf_file.base.comp.root_mod.resolved_target.result; + if (target.cpu.arch != self.header.?.e_machine.toTargetCpuArch().?) { try elf_file.reportParseError2( self.index, "invalid cpu architecture: {s}", @@ -100,7 +102,8 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void { } fn parseVersions(self: *SharedObject, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const symtab = self.getSymtabRaw(); try self.verstrings.resize(gpa, 2); @@ -145,7 +148,8 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void { } pub fn init(self: *SharedObject, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const symtab = self.getSymtabRaw(); const strtab = self.getStrtabRaw(); @@ -294,7 +298,8 @@ pub fn initSymbolAliases(self: *SharedObject, elf_file: *Elf) !void { } }; - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; var aliases = std.ArrayList(Symbol.Index).init(gpa); defer aliases.deinit(); try aliases.ensureTotalCapacityPrecise(self.globals().len); diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 3dd35dfe63a0..7c7d3fd17c95 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -43,7 +43,7 @@ pub fn outputShndx(symbol: Symbol) ?u16 { } pub fn isLocal(symbol: Symbol, elf_file: *Elf) bool { - if (elf_file.isRelocatable()) return symbol.elfSym(elf_file).st_bind() == elf.STB_LOCAL; + if (elf_file.base.isRelocatable()) return symbol.elfSym(elf_file).st_bind() == elf.STB_LOCAL; return !(symbol.flags.import or symbol.flags.@"export"); } @@ -186,7 +186,7 @@ const GetOrCreateZigGotEntryResult = struct { }; pub fn getOrCreateZigGotEntry(symbol: *Symbol, symbol_index: Index, elf_file: *Elf) !GetOrCreateZigGotEntryResult { - assert(!elf_file.isRelocatable()); + assert(!elf_file.base.isRelocatable()); assert(symbol.flags.needs_zig_got); if (symbol.flags.has_zig_got) return .{ .found_existing = true, .index = symbol.extra(elf_file).?.zig_got }; const index = try elf_file.zig_got.addSymbol(symbol_index, elf_file); @@ -237,7 +237,7 @@ pub fn setOutputSym(symbol: Symbol, elf_file: *Elf, out: *elf.Elf64_Sym) void { const st_shndx = blk: { if (symbol.flags.has_copy_rel) break :blk elf_file.copy_rel_section_index.?; if (file_ptr == .shared_object or esym.st_shndx == elf.SHN_UNDEF) break :blk elf.SHN_UNDEF; - if (elf_file.isRelocatable() and esym.st_shndx == elf.SHN_COMMON) break :blk elf.SHN_COMMON; + if (elf_file.base.isRelocatable() and esym.st_shndx == elf.SHN_COMMON) break :blk elf.SHN_COMMON; if (symbol.atom(elf_file) == null and file_ptr != .linker_defined) break :blk elf.SHN_ABS; break :blk symbol.outputShndx() orelse elf.SHN_UNDEF; }; diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 7a1fa2143577..a29c3ab69c48 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -4,6 +4,7 @@ //! Think about this as fake in-memory Object file for the Zig module. data: std.ArrayListUnmanaged(u8) = .{}, +/// Externally owned memory. path: []const u8, index: File.Index, @@ -76,7 +77,8 @@ pub const symbol_mask: u32 = 0x7fffffff; pub const SHN_ATOM: u16 = 0x100; pub fn init(self: *ZigObject, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; try self.atoms.append(gpa, 0); // null input section try self.relocs.append(gpa, .{}); // null relocs section @@ -96,14 +98,18 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { esym.st_shndx = elf.SHN_ABS; symbol_ptr.esym_index = esym_index; - if (!elf_file.base.options.strip) { - self.dwarf = Dwarf.init(gpa, &elf_file.base, .dwarf32); + switch (comp.config.debug_format) { + .strip => {}, + .dwarf => |v| { + assert(v == .@"32"); + self.dwarf = Dwarf.init(&elf_file.base, .dwarf32); + }, + .code_view => unreachable, } } pub fn deinit(self: *ZigObject, allocator: Allocator) void { self.data.deinit(allocator); - allocator.free(self.path); self.local_esyms.deinit(allocator); self.global_esyms.deinit(allocator); self.strtab.deinit(allocator); @@ -155,13 +161,13 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void { pub fn flushModule(self: *ZigObject, elf_file: *Elf) !void { // Handle any lazy symbols that were emitted by incremental compilation. if (self.lazy_syms.getPtr(.none)) |metadata| { - const module = elf_file.base.options.module.?; + const zcu = elf_file.base.comp.module.?; // Most lazy symbols can be updated on first use, but // anyerror needs to wait for everything to be flushed. if (metadata.text_state != .unused) self.updateLazySymbol( elf_file, - link.File.LazySymbol.initDecl(.code, null, module), + link.File.LazySymbol.initDecl(.code, null, zcu), metadata.text_symbol_index, ) catch |err| return switch (err) { error.CodegenFail => error.FlushFailure, @@ -169,7 +175,7 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf) !void { }; if (metadata.rodata_state != .unused) self.updateLazySymbol( elf_file, - link.File.LazySymbol.initDecl(.const_data, null, module), + link.File.LazySymbol.initDecl(.const_data, null, zcu), metadata.rodata_symbol_index, ) catch |err| return switch (err) { error.CodegenFail => error.FlushFailure, @@ -182,7 +188,8 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf) !void { } if (self.dwarf) |*dw| { - try dw.flushModule(elf_file.base.options.module.?); + const zcu = elf_file.base.comp.module.?; + try dw.flushModule(zcu); // TODO I need to re-think how to handle ZigObject's debug sections AND debug sections // extracted from input object files correctly. @@ -195,7 +202,7 @@ pub fn flushModule(self: *ZigObject, elf_file: *Elf) !void { const text_shdr = elf_file.shdrs.items[elf_file.zig_text_section_index.?]; const low_pc = text_shdr.sh_addr; const high_pc = text_shdr.sh_addr + text_shdr.sh_size; - try dw.writeDbgInfoHeader(elf_file.base.options.module.?, low_pc, high_pc); + try dw.writeDbgInfoHeader(zcu, low_pc, high_pc); self.debug_info_header_dirty = false; } @@ -268,7 +275,7 @@ pub fn addGlobalEsym(self: *ZigObject, allocator: Allocator) !Symbol.Index { } pub fn addAtom(self: *ZigObject, elf_file: *Elf) !Symbol.Index { - const gpa = elf_file.base.allocator; + const gpa = elf_file.base.comp.gpa; const atom_index = try elf_file.addAtom(); const symbol_index = try elf_file.addSymbol(); const esym_index = try self.addLocalEsym(gpa); @@ -363,7 +370,7 @@ pub fn claimUnresolved(self: ZigObject, elf_file: *Elf) void { } const is_import = blk: { - if (!elf_file.isDynLib()) break :blk false; + if (!elf_file.base.isDynLib()) break :blk false; const vis = @as(elf.STV, @enumFromInt(esym.st_other)); if (vis == .HIDDEN) break :blk false; break :blk true; @@ -411,6 +418,7 @@ pub fn allocateTlvAtoms(self: ZigObject, elf_file: *Elf) void { } pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void { + const gpa = elf_file.base.comp.gpa; for (self.atoms.items) |atom_index| { const atom = elf_file.atom(atom_index) orelse continue; if (!atom.flags.alive) continue; @@ -421,7 +429,7 @@ pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void { // Perhaps it would make sense to save the code until flushModule where we // would free all of generated code? const code = try self.codeAlloc(elf_file, atom_index); - defer elf_file.base.allocator.free(code); + defer gpa.free(code); try atom.scanRelocs(elf_file, code, undefs); } else try atom.scanRelocs(elf_file, null, undefs); } @@ -447,7 +455,7 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void { /// We need this so that we can write to an archive. /// TODO implement writing ZigObject data directly to a buffer instead. pub fn readFileContents(self: *ZigObject, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; + const gpa = elf_file.base.comp.gpa; const shsize: u64 = switch (elf_file.ptr_width) { .p32 => @sizeOf(elf.Elf32_Shdr), .p64 => @sizeOf(elf.Elf64_Shdr), @@ -465,7 +473,7 @@ pub fn readFileContents(self: *ZigObject, elf_file: *Elf) !void { } pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void { - const gpa = elf_file.base.allocator; + const gpa = elf_file.base.comp.gpa; try ar_symtab.symtab.ensureUnusedCapacity(gpa, self.globals().len); @@ -508,7 +516,7 @@ pub fn addAtomsToRelaSections(self: ZigObject, elf_file: *Elf) !void { const out_shdr = elf_file.shdrs.items[out_shndx]; if (out_shdr.sh_type == elf.SHT_NOBITS) continue; - const gpa = elf_file.base.allocator; + const gpa = elf_file.base.comp.gpa; const sec = elf_file.output_rela_sections.getPtr(out_shndx).?; try sec.atom_list.append(gpa, atom_index); } @@ -602,7 +610,7 @@ pub fn asFile(self: *ZigObject) File { /// Returns atom's code. /// Caller owns the memory. pub fn codeAlloc(self: ZigObject, elf_file: *Elf, atom_index: Atom.Index) ![]u8 { - const gpa = elf_file.base.allocator; + const gpa = elf_file.base.comp.gpa; const atom = elf_file.atom(atom_index).?; assert(atom.file_index == self.index); const shdr = &elf_file.shdrs.items[atom.outputShndx().?]; @@ -668,8 +676,8 @@ pub fn lowerAnonDecl( explicit_alignment: InternPool.Alignment, src_loc: Module.SrcLoc, ) !codegen.Result { - const gpa = elf_file.base.allocator; - const mod = elf_file.base.options.module.?; + const gpa = elf_file.base.comp.gpa; + const mod = elf_file.base.comp.module.?; const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val)); const decl_alignment = switch (explicit_alignment) { .none => ty.abiAlignment(mod), @@ -716,8 +724,8 @@ pub fn getOrCreateMetadataForLazySymbol( elf_file: *Elf, lazy_sym: link.File.LazySymbol, ) !Symbol.Index { - const gpa = elf_file.base.allocator; - const mod = elf_file.base.options.module.?; + const gpa = elf_file.base.comp.gpa; + const mod = elf_file.base.comp.module.?; const gop = try self.lazy_syms.getOrPut(gpa, lazy_sym.getDecl(mod)); errdefer _ = if (!gop.found_existing) self.lazy_syms.pop(); if (!gop.found_existing) gop.value_ptr.* = .{}; @@ -752,25 +760,28 @@ pub fn getOrCreateMetadataForLazySymbol( } fn freeUnnamedConsts(self: *ZigObject, elf_file: *Elf, decl_index: InternPool.DeclIndex) void { + const gpa = elf_file.base.comp.gpa; const unnamed_consts = self.unnamed_consts.getPtr(decl_index) orelse return; for (unnamed_consts.items) |sym_index| { self.freeDeclMetadata(elf_file, sym_index); } - unnamed_consts.clearAndFree(elf_file.base.allocator); + unnamed_consts.clearAndFree(gpa); } fn freeDeclMetadata(self: *ZigObject, elf_file: *Elf, sym_index: Symbol.Index) void { _ = self; + const gpa = elf_file.base.comp.gpa; const sym = elf_file.symbol(sym_index); sym.atom(elf_file).?.free(elf_file); log.debug("adding %{d} to local symbols free list", .{sym_index}); - elf_file.symbols_free_list.append(elf_file.base.allocator, sym_index) catch {}; + elf_file.symbols_free_list.append(gpa, sym_index) catch {}; elf_file.symbols.items[sym_index] = .{}; // TODO free GOT entry here } pub fn freeDecl(self: *ZigObject, elf_file: *Elf, decl_index: InternPool.DeclIndex) void { - const mod = elf_file.base.options.module.?; + const gpa = elf_file.base.comp.gpa; + const mod = elf_file.base.comp.module.?; const decl = mod.declPtr(decl_index); log.debug("freeDecl {*}", .{decl}); @@ -780,7 +791,7 @@ pub fn freeDecl(self: *ZigObject, elf_file: *Elf, decl_index: InternPool.DeclInd const sym_index = kv.value.symbol_index; self.freeDeclMetadata(elf_file, sym_index); self.freeUnnamedConsts(elf_file, decl_index); - kv.value.exports.deinit(elf_file.base.allocator); + kv.value.exports.deinit(gpa); } if (self.dwarf) |*dw| { @@ -793,15 +804,16 @@ pub fn getOrCreateMetadataForDecl( elf_file: *Elf, decl_index: InternPool.DeclIndex, ) !Symbol.Index { - const gop = try self.decls.getOrPut(elf_file.base.allocator, decl_index); + const gpa = elf_file.base.comp.gpa; + const gop = try self.decls.getOrPut(gpa, decl_index); if (!gop.found_existing) { - const single_threaded = elf_file.base.options.single_threaded; + const any_non_single_threaded = elf_file.base.comp.config.any_non_single_threaded; const symbol_index = try self.addAtom(elf_file); - const mod = elf_file.base.options.module.?; + const mod = elf_file.base.comp.module.?; const decl = mod.declPtr(decl_index); const sym = elf_file.symbol(symbol_index); if (decl.getOwnedVariable(mod)) |variable| { - if (variable.is_threadlocal and !single_threaded) { + if (variable.is_threadlocal and any_non_single_threaded) { sym.flags.is_tls = true; } } @@ -820,13 +832,13 @@ fn getDeclShdrIndex( code: []const u8, ) error{OutOfMemory}!u16 { _ = self; - const mod = elf_file.base.options.module.?; - const single_threaded = elf_file.base.options.single_threaded; + const mod = elf_file.base.comp.module.?; + const any_non_single_threaded = elf_file.base.comp.config.any_non_single_threaded; const shdr_index = switch (decl.ty.zigTypeTag(mod)) { .Fn => elf_file.zig_text_section_index.?, else => blk: { if (decl.getOwnedVariable(mod)) |variable| { - if (variable.is_threadlocal and !single_threaded) { + if (variable.is_threadlocal and any_non_single_threaded) { const is_all_zeroes = for (code) |byte| { if (byte != 0) break false; } else true; @@ -846,9 +858,12 @@ fn getDeclShdrIndex( } if (variable.is_const) break :blk elf_file.zig_data_rel_ro_section_index.?; if (Value.fromInterned(variable.init).isUndefDeep(mod)) { - const mode = elf_file.base.options.optimize_mode; - if (mode == .Debug or mode == .ReleaseSafe) break :blk elf_file.zig_data_section_index.?; - break :blk elf_file.zig_bss_section_index.?; + // TODO: get the optimize_mode from the Module that owns the decl instead + // of using the root module here. + break :blk switch (elf_file.base.comp.root_mod.optimize_mode) { + .Debug, .ReleaseSafe => elf_file.zig_data_section_index.?, + .ReleaseFast, .ReleaseSmall => elf_file.zig_bss_section_index.?, + }; } // TODO I blatantly copied the logic from the Wasm linker, but is there a less // intrusive check for all zeroes than this? @@ -873,8 +888,8 @@ fn updateDeclCode( code: []const u8, stt_bits: u8, ) !void { - const gpa = elf_file.base.allocator; - const mod = elf_file.base.options.module.?; + const gpa = elf_file.base.comp.gpa; + const mod = elf_file.base.comp.module.?; const decl = mod.declPtr(decl_index); const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); @@ -911,7 +926,7 @@ fn updateDeclCode( sym.value = atom_ptr.value; esym.st_value = atom_ptr.value; - if (!elf_file.isRelocatable()) { + if (!elf_file.base.isRelocatable()) { log.debug(" (writing new offset table entry)", .{}); assert(sym.flags.has_zig_got); const extra = sym.extra(elf_file).?; @@ -929,7 +944,7 @@ fn updateDeclCode( sym.flags.needs_zig_got = true; esym.st_value = atom_ptr.value; - if (!elf_file.isRelocatable()) { + if (!elf_file.base.isRelocatable()) { const gop = try sym.getOrCreateZigGotEntry(sym_index, elf_file); try elf_file.zig_got.writeOne(elf_file, gop.index); } @@ -971,8 +986,8 @@ fn updateTlv( shndx: u16, code: []const u8, ) !void { - const gpa = elf_file.base.allocator; - const mod = elf_file.base.options.module.?; + const gpa = elf_file.base.comp.gpa; + const mod = elf_file.base.comp.module.?; const decl = mod.declPtr(decl_index); const decl_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); @@ -1026,6 +1041,7 @@ pub fn updateFunc( const tracy = trace(@src()); defer tracy.end(); + const gpa = elf_file.base.comp.gpa; const func = mod.funcInfo(func_index); const decl_index = func.owner_decl; const decl = mod.declPtr(decl_index); @@ -1034,7 +1050,7 @@ pub fn updateFunc( self.freeUnnamedConsts(elf_file, decl_index); elf_file.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file); - var code_buffer = std.ArrayList(u8).init(elf_file.base.allocator); + var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(mod, decl_index) else null; @@ -1117,7 +1133,8 @@ pub fn updateDecl( const sym_index = try self.getOrCreateMetadataForDecl(elf_file, decl_index); elf_file.symbol(sym_index).atom(elf_file).?.freeRelocs(elf_file); - var code_buffer = std.ArrayList(u8).init(elf_file.base.allocator); + const gpa = elf_file.base.comp.gpa; + var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(mod, decl_index) else null; @@ -1179,8 +1196,8 @@ fn updateLazySymbol( sym: link.File.LazySymbol, symbol_index: Symbol.Index, ) !void { - const gpa = elf_file.base.allocator; - const mod = elf_file.base.options.module.?; + const gpa = elf_file.base.comp.gpa; + const mod = elf_file.base.comp.module.?; var required_alignment: InternPool.Alignment = .none; var code_buffer = std.ArrayList(u8).init(gpa); @@ -1245,7 +1262,7 @@ fn updateLazySymbol( local_sym.flags.needs_zig_got = true; local_esym.st_value = atom_ptr.value; - if (!elf_file.isRelocatable()) { + if (!elf_file.base.isRelocatable()) { const gop = try local_sym.getOrCreateZigGotEntry(symbol_index, elf_file); try elf_file.zig_got.writeOne(elf_file, gop.index); } @@ -1261,8 +1278,8 @@ pub fn lowerUnnamedConst( typed_value: TypedValue, decl_index: InternPool.DeclIndex, ) !u32 { - const gpa = elf_file.base.allocator; - const mod = elf_file.base.options.module.?; + const gpa = elf_file.base.comp.gpa; + const mod = elf_file.base.comp.module.?; const gop = try self.unnamed_consts.getOrPut(gpa, decl_index); if (!gop.found_existing) { gop.value_ptr.* = .{}; @@ -1308,7 +1325,7 @@ fn lowerConst( output_section_index: u16, src_loc: Module.SrcLoc, ) !LowerConstResult { - const gpa = elf_file.base.allocator; + const gpa = elf_file.base.comp.gpa; var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); @@ -1364,7 +1381,7 @@ pub fn updateExports( const tracy = trace(@src()); defer tracy.end(); - const gpa = elf_file.base.allocator; + const gpa = elf_file.base.comp.gpa; const metadata = switch (exported) { .decl_index => |decl_index| blk: { _ = try self.getOrCreateMetadataForDecl(elf_file, decl_index); @@ -1467,7 +1484,7 @@ pub fn deleteDeclExport( name: InternPool.NullTerminatedString, ) void { const metadata = self.decls.getPtr(decl_index) orelse return; - const mod = elf_file.base.options.module.?; + const mod = elf_file.base.comp.module.?; const exp_name = mod.intern_pool.stringToSlice(name); const esym_index = metadata.@"export"(self, exp_name) orelse return; log.debug("deleting export '{s}'", .{exp_name}); @@ -1485,7 +1502,7 @@ pub fn deleteDeclExport( pub fn getGlobalSymbol(self: *ZigObject, elf_file: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 { _ = lib_name; - const gpa = elf_file.base.allocator; + const gpa = elf_file.base.comp.gpa; const off = try self.strtab.insert(gpa, name); const lookup_gop = try self.globals_lookup.getOrPut(gpa, off); if (!lookup_gop.found_existing) { diff --git a/src/link/Elf/eh_frame.zig b/src/link/Elf/eh_frame.zig index 1d24285b30c0..75401824abe5 100644 --- a/src/link/Elf/eh_frame.zig +++ b/src/link/Elf/eh_frame.zig @@ -233,9 +233,12 @@ pub const Iterator = struct { }; pub fn calcEhFrameSize(elf_file: *Elf) !usize { + const comp = elf_file.base.comp; + const gpa = comp.gpa; + var offset: usize = 0; - var cies = std.ArrayList(Cie).init(elf_file.base.allocator); + var cies = std.ArrayList(Cie).init(gpa); defer cies.deinit(); for (elf_file.objects.items) |index| { @@ -268,7 +271,7 @@ pub fn calcEhFrameSize(elf_file: *Elf) !usize { } } - if (!elf_file.isRelocatable()) { + if (!elf_file.base.isRelocatable()) { offset += 4; // NULL terminator } @@ -327,7 +330,8 @@ fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file: } pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; relocs_log.debug("{x}: .eh_frame", .{elf_file.shdrs.items[elf_file.eh_frame_section_index.?].sh_addr}); @@ -378,7 +382,8 @@ pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { } pub fn writeEhFrameObject(elf_file: *Elf, writer: anytype) !void { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; for (elf_file.objects.items) |index| { const object = elf_file.file(index).?.object; @@ -467,6 +472,9 @@ pub fn writeEhFrameRelocs(elf_file: *Elf, writer: anytype) !void { } pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void { + const comp = elf_file.base.comp; + const gpa = comp.gpa; + try writer.writeByte(1); // version try writer.writeByte(EH_PE.pcrel | EH_PE.sdata4); try writer.writeByte(EH_PE.udata4); @@ -495,7 +503,7 @@ pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void { } }; - var entries = std.ArrayList(Entry).init(elf_file.base.allocator); + var entries = std.ArrayList(Entry).init(gpa); defer entries.deinit(); try entries.ensureTotalCapacityPrecise(num_fdes); diff --git a/src/link/Elf/gc.zig b/src/link/Elf/gc.zig index ade2b75782a8..d95dd78b331f 100644 --- a/src/link/Elf/gc.zig +++ b/src/link/Elf/gc.zig @@ -1,5 +1,6 @@ pub fn gcAtoms(elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const num_files = elf_file.objects.items.len + @intFromBool(elf_file.zig_object_index != null); var files = try std.ArrayList(File.Index).initCapacity(gpa, num_files); defer files.deinit(); diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index 25a9975f01e4..68a36e14ce4e 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -8,14 +8,16 @@ pub const DynamicSection = struct { } pub fn addNeeded(dt: *DynamicSection, shared: *SharedObject, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const off = try elf_file.insertDynString(shared.soname()); try dt.needed.append(gpa, off); } pub fn setRpath(dt: *DynamicSection, rpath_list: []const []const u8, elf_file: *Elf) !void { if (rpath_list.len == 0) return; - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; var rpath = std.ArrayList(u8).init(gpa); defer rpath.deinit(); for (rpath_list, 0..) |path, i| { @@ -32,7 +34,7 @@ pub const DynamicSection = struct { fn getFlags(dt: DynamicSection, elf_file: *Elf) ?u64 { _ = dt; var flags: u64 = 0; - if (elf_file.base.options.z_now) { + if (elf_file.z_now) { flags |= elf.DF_BIND_NOW; } for (elf_file.got.entries.items) |entry| switch (entry.tag) { @@ -49,15 +51,16 @@ pub const DynamicSection = struct { } fn getFlags1(dt: DynamicSection, elf_file: *Elf) ?u64 { + const comp = elf_file.base.comp; _ = dt; var flags_1: u64 = 0; - if (elf_file.base.options.z_now) { + if (elf_file.z_now) { flags_1 |= elf.DF_1_NOW; } - if (elf_file.isExe() and elf_file.base.options.pie) { + if (elf_file.base.isExe() and comp.config.pie) { flags_1 |= elf.DF_1_PIE; } - // if (elf_file.base.options.z_nodlopen) { + // if (elf_file.z_nodlopen) { // flags_1 |= elf.DF_1_NOOPEN; // } return if (flags_1 > 0) flags_1 else null; @@ -86,7 +89,7 @@ pub const DynamicSection = struct { if (elf_file.verneed_section_index != null) nentries += 2; // VERNEED if (dt.getFlags(elf_file) != null) nentries += 1; // FLAGS if (dt.getFlags1(elf_file) != null) nentries += 1; // FLAGS_1 - if (!elf_file.isDynLib()) nentries += 1; // DEBUG + if (!elf_file.base.isDynLib()) nentries += 1; // DEBUG nentries += 1; // NULL return nentries * @sizeOf(elf.Elf64_Dyn); } @@ -213,7 +216,7 @@ pub const DynamicSection = struct { } // DEBUG - if (!elf_file.isDynLib()) try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_DEBUG, .d_val = 0 }); + if (!elf_file.base.isDynLib()) try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_DEBUG, .d_val = 0 }); // NULL try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_NULL, .d_val = 0 }); @@ -246,12 +249,14 @@ pub const ZigGotSection = struct { } pub fn addSymbol(zig_got: *ZigGotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index { - const index = try zig_got.allocateEntry(elf_file.base.allocator); + const comp = elf_file.base.comp; + const gpa = comp.gpa; + const index = try zig_got.allocateEntry(gpa); const entry = &zig_got.entries.items[index]; entry.* = sym_index; const symbol = elf_file.symbol(sym_index); symbol.flags.has_zig_got = true; - if (elf_file.isDynLib() or (elf_file.isExe() and elf_file.base.options.pie)) { + if (elf_file.base.isDynLib() or (elf_file.base.isExe() and comp.config.pie)) { zig_got.flags.needs_rela = true; } if (symbol.extra(elf_file)) |extra| { @@ -287,7 +292,8 @@ pub const ZigGotSection = struct { zig_got.flags.dirty = false; } const entry_size: u16 = elf_file.archPtrWidthBytes(); - const endian = elf_file.base.options.target.cpu.arch.endian(); + const target = elf_file.base.comp.root_mod.resolved_target.result; + const endian = target.cpu.arch.endian(); const off = zig_got.entryOffset(index, elf_file); const vaddr = zig_got.entryAddress(index, elf_file); const entry = zig_got.entries.items[index]; @@ -346,7 +352,9 @@ pub const ZigGotSection = struct { } pub fn addRela(zig_got: ZigGotSection, elf_file: *Elf) !void { - try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, zig_got.numRela()); + const comp = elf_file.base.comp; + const gpa = comp.gpa; + try elf_file.rela_dyn.ensureUnusedCapacity(gpa, zig_got.numRela()); for (zig_got.entries.items) |entry| { const symbol = elf_file.symbol(entry); const offset = symbol.zigGotAddress(elf_file); @@ -477,14 +485,16 @@ pub const GotSection = struct { } pub fn addGotSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !Index { - const index = try got.allocateEntry(elf_file.base.allocator); + const comp = elf_file.base.comp; + const gpa = comp.gpa; + const index = try got.allocateEntry(gpa); const entry = &got.entries.items[index]; entry.tag = .got; entry.symbol_index = sym_index; const symbol = elf_file.symbol(sym_index); symbol.flags.has_got = true; if (symbol.flags.import or symbol.isIFunc(elf_file) or - ((elf_file.isDynLib() or (elf_file.isExe() and elf_file.base.options.pie)) and !symbol.isAbs(elf_file))) + ((elf_file.base.isDynLib() or (elf_file.base.isExe() and comp.config.pie)) and !symbol.isAbs(elf_file))) { got.flags.needs_rela = true; } @@ -497,8 +507,10 @@ pub const GotSection = struct { } pub fn addTlsLdSymbol(got: *GotSection, elf_file: *Elf) !void { + const comp = elf_file.base.comp; + const gpa = comp.gpa; assert(got.flags.needs_tlsld); - const index = try got.allocateEntry(elf_file.base.allocator); + const index = try got.allocateEntry(gpa); const entry = &got.entries.items[index]; entry.tag = .tlsld; entry.symbol_index = undefined; // unused @@ -507,13 +519,15 @@ pub const GotSection = struct { } pub fn addTlsGdSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { - const index = try got.allocateEntry(elf_file.base.allocator); + const comp = elf_file.base.comp; + const gpa = comp.gpa; + const index = try got.allocateEntry(gpa); const entry = &got.entries.items[index]; entry.tag = .tlsgd; entry.symbol_index = sym_index; const symbol = elf_file.symbol(sym_index); symbol.flags.has_tlsgd = true; - if (symbol.flags.import or elf_file.isDynLib()) got.flags.needs_rela = true; + if (symbol.flags.import or elf_file.base.isDynLib()) got.flags.needs_rela = true; if (symbol.extra(elf_file)) |extra| { var new_extra = extra; new_extra.tlsgd = index; @@ -522,13 +536,15 @@ pub const GotSection = struct { } pub fn addGotTpSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { - const index = try got.allocateEntry(elf_file.base.allocator); + const comp = elf_file.base.comp; + const gpa = comp.gpa; + const index = try got.allocateEntry(gpa); const entry = &got.entries.items[index]; entry.tag = .gottp; entry.symbol_index = sym_index; const symbol = elf_file.symbol(sym_index); symbol.flags.has_gottp = true; - if (symbol.flags.import or elf_file.isDynLib()) got.flags.needs_rela = true; + if (symbol.flags.import or elf_file.base.isDynLib()) got.flags.needs_rela = true; if (symbol.extra(elf_file)) |extra| { var new_extra = extra; new_extra.gottp = index; @@ -537,7 +553,9 @@ pub const GotSection = struct { } pub fn addTlsDescSymbol(got: *GotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { - const index = try got.allocateEntry(elf_file.base.allocator); + const comp = elf_file.base.comp; + const gpa = comp.gpa; + const index = try got.allocateEntry(gpa); const entry = &got.entries.items[index]; entry.tag = .tlsdesc; entry.symbol_index = sym_index; @@ -560,7 +578,8 @@ pub const GotSection = struct { } pub fn write(got: GotSection, elf_file: *Elf, writer: anytype) !void { - const is_dyn_lib = elf_file.isDynLib(); + const comp = elf_file.base.comp; + const is_dyn_lib = elf_file.base.isDynLib(); const apply_relocs = true; // TODO add user option for this for (got.entries.items) |entry| { @@ -575,7 +594,7 @@ pub const GotSection = struct { if (symbol.?.flags.import) break :blk 0; if (symbol.?.isIFunc(elf_file)) break :blk if (apply_relocs) value else 0; - if ((elf_file.isDynLib() or (elf_file.isExe() and elf_file.base.options.pie)) and + if ((elf_file.base.isDynLib() or (elf_file.base.isExe() and comp.config.pie)) and !symbol.?.isAbs(elf_file)) { break :blk if (apply_relocs) value else 0; @@ -622,8 +641,10 @@ pub const GotSection = struct { } pub fn addRela(got: GotSection, elf_file: *Elf) !void { - const is_dyn_lib = elf_file.isDynLib(); - try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, got.numRela(elf_file)); + const comp = elf_file.base.comp; + const gpa = comp.gpa; + const is_dyn_lib = elf_file.base.isDynLib(); + try elf_file.rela_dyn.ensureUnusedCapacity(gpa, got.numRela(elf_file)); for (got.entries.items) |entry| { const symbol = switch (entry.tag) { @@ -651,7 +672,7 @@ pub const GotSection = struct { }); continue; } - if ((elf_file.isDynLib() or (elf_file.isExe() and elf_file.base.options.pie)) and + if ((elf_file.base.isDynLib() or (elf_file.base.isExe() and comp.config.pie)) and !symbol.?.isAbs(elf_file)) { elf_file.addRelaDynAssumeCapacity(.{ @@ -724,7 +745,8 @@ pub const GotSection = struct { } pub fn numRela(got: GotSection, elf_file: *Elf) usize { - const is_dyn_lib = elf_file.isDynLib(); + const comp = elf_file.base.comp; + const is_dyn_lib = elf_file.base.isDynLib(); var num: usize = 0; for (got.entries.items) |entry| { const symbol = switch (entry.tag) { @@ -733,7 +755,7 @@ pub const GotSection = struct { }; switch (entry.tag) { .got => if (symbol.?.flags.import or symbol.?.isIFunc(elf_file) or - ((elf_file.isDynLib() or (elf_file.isExe() and elf_file.base.options.pie)) and + ((elf_file.base.isDynLib() or (elf_file.base.isExe() and comp.config.pie)) and !symbol.?.isAbs(elf_file))) { num += 1; @@ -840,6 +862,8 @@ pub const PltSection = struct { } pub fn addSymbol(plt: *PltSection, sym_index: Symbol.Index, elf_file: *Elf) !void { + const comp = elf_file.base.comp; + const gpa = comp.gpa; const index = @as(u32, @intCast(plt.symbols.items.len)); const symbol = elf_file.symbol(sym_index); symbol.flags.has_plt = true; @@ -848,7 +872,7 @@ pub const PltSection = struct { new_extra.plt = index; symbol.setExtra(new_extra, elf_file); } else try symbol.addExtra(.{ .plt = index }, elf_file); - try plt.symbols.append(elf_file.base.allocator, sym_index); + try plt.symbols.append(gpa, sym_index); } pub fn size(plt: PltSection) usize { @@ -888,7 +912,9 @@ pub const PltSection = struct { } pub fn addRela(plt: PltSection, elf_file: *Elf) !void { - try elf_file.rela_plt.ensureUnusedCapacity(elf_file.base.allocator, plt.numRela()); + const comp = elf_file.base.comp; + const gpa = comp.gpa; + try elf_file.rela_plt.ensureUnusedCapacity(gpa, plt.numRela()); for (plt.symbols.items) |sym_index| { const sym = elf_file.symbol(sym_index); assert(sym.flags.import); @@ -1003,6 +1029,8 @@ pub const PltGotSection = struct { } pub fn addSymbol(plt_got: *PltGotSection, sym_index: Symbol.Index, elf_file: *Elf) !void { + const comp = elf_file.base.comp; + const gpa = comp.gpa; const index = @as(u32, @intCast(plt_got.symbols.items.len)); const symbol = elf_file.symbol(sym_index); symbol.flags.has_plt = true; @@ -1012,7 +1040,7 @@ pub const PltGotSection = struct { new_extra.plt_got = index; symbol.setExtra(new_extra, elf_file); } else try symbol.addExtra(.{ .plt_got = index }, elf_file); - try plt_got.symbols.append(elf_file.base.allocator, sym_index); + try plt_got.symbols.append(gpa, sym_index); } pub fn size(plt_got: PltGotSection) usize { @@ -1070,6 +1098,8 @@ pub const CopyRelSection = struct { } pub fn addSymbol(copy_rel: *CopyRelSection, sym_index: Symbol.Index, elf_file: *Elf) !void { + const comp = elf_file.base.comp; + const gpa = comp.gpa; const index = @as(u32, @intCast(copy_rel.symbols.items.len)); const symbol = elf_file.symbol(sym_index); symbol.flags.import = true; @@ -1082,7 +1112,7 @@ pub const CopyRelSection = struct { new_extra.copy_rel = index; symbol.setExtra(new_extra, elf_file); } else try symbol.addExtra(.{ .copy_rel = index }, elf_file); - try copy_rel.symbols.append(elf_file.base.allocator, sym_index); + try copy_rel.symbols.append(gpa, sym_index); const shared_object = symbol.file(elf_file).?.shared_object; if (shared_object.aliases == null) { @@ -1122,7 +1152,9 @@ pub const CopyRelSection = struct { } pub fn addRela(copy_rel: CopyRelSection, elf_file: *Elf) !void { - try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, copy_rel.numRela()); + const comp = elf_file.base.comp; + const gpa = comp.gpa; + try elf_file.rela_dyn.ensureUnusedCapacity(gpa, copy_rel.numRela()); for (copy_rel.symbols.items) |sym_index| { const sym = elf_file.symbol(sym_index); assert(sym.flags.import and sym.flags.has_copy_rel); @@ -1155,7 +1187,8 @@ pub const DynsymSection = struct { } pub fn addSymbol(dynsym: *DynsymSection, sym_index: Symbol.Index, elf_file: *Elf) !void { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const index = @as(u32, @intCast(dynsym.entries.items.len + 1)); const sym = elf_file.symbol(sym_index); sym.flags.has_dynamic = true; @@ -1237,7 +1270,8 @@ pub const HashSection = struct { pub fn generate(hs: *HashSection, elf_file: *Elf) !void { if (elf_file.dynsym.count() == 1) return; - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const nsyms = elf_file.dynsym.count(); var buckets = try gpa.alloc(u32, nsyms); @@ -1325,7 +1359,8 @@ pub const GnuHashSection = struct { try cwriter.writeInt(u32, hash.num_bloom, .little); try cwriter.writeInt(u32, bloom_shift, .little); - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const hashes = try gpa.alloc(u32, exports.len); defer gpa.free(hashes); const indices = try gpa.alloc(u32, exports.len); @@ -1427,7 +1462,8 @@ pub const VerneedSection = struct { } }; - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; var verneed = std.ArrayList(VersionedSymbol).init(gpa); defer verneed.deinit(); try verneed.ensureTotalCapacity(dynsyms.len); @@ -1483,7 +1519,8 @@ pub const VerneedSection = struct { } fn addVerneed(vern: *VerneedSection, soname: []const u8, elf_file: *Elf) !*elf.Elf64_Verneed { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const sym = try vern.verneed.addOne(gpa); sym.* = .{ .vn_version = 1, @@ -1501,7 +1538,8 @@ pub const VerneedSection = struct { version: [:0]const u8, elf_file: *Elf, ) !elf.Elf64_Vernaux { - const gpa = elf_file.base.allocator; + const comp = elf_file.base.comp; + const gpa = comp.gpa; const sym = try vern.vernaux.addOne(gpa); sym.* = .{ .vna_hash = HashSection.hasher(version), @@ -1575,7 +1613,8 @@ pub const ComdatGroupSection = struct { fn writeInt(value: anytype, elf_file: *Elf, writer: anytype) !void { const entry_size = elf_file.archPtrWidthBytes(); - const endian = elf_file.base.options.target.cpu.arch.endian(); + const target = elf_file.base.comp.root_mod.resolved_target.result; + const endian = target.cpu.arch.endian(); switch (entry_size) { 2 => try writer.writeInt(u16, @intCast(value), endian), 4 => try writer.writeInt(u32, @intCast(value), endian), diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 61446b3300d6..cb26aa0ca34e 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -1,6 +1,7 @@ base: File, +entry_name: ?[]const u8, -/// If this is not null, an object file is created by LLVM and linked with LLD afterwards. +/// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path. llvm_object: ?*LlvmObject = null, /// Debug symbols bundle (or dSym). @@ -67,9 +68,6 @@ tlv_ptr_table: TableSection(SymbolWithLoc) = .{}, thunk_table: std.AutoHashMapUnmanaged(Atom.Index, thunks.Thunk.Index) = .{}, thunks: std.ArrayListUnmanaged(thunks.Thunk) = .{}, -error_flags: File.ErrorFlags = File.ErrorFlags{}, -misc_errors: std.ArrayListUnmanaged(File.ErrorMsg) = .{}, - segment_table_dirty: bool = false, got_table_count_dirty: bool = false, got_table_contents_dirty: bool = false, @@ -77,12 +75,6 @@ stub_table_count_dirty: bool = false, stub_table_contents_dirty: bool = false, stub_helper_preamble_allocated: bool = false, -/// A helper var to indicate if we are at the start of the incremental updates, or -/// already somewhere further along the update-and-run chain. -/// TODO once we add opening a prelinked output binary from file, this will become -/// obsolete as we will carry on where we left off. -cold_start: bool = true, - /// List of atoms that are either synthetic or map directly to the Zig source program. atoms: std.ArrayListUnmanaged(Atom) = .{}, @@ -143,89 +135,167 @@ 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); +sdk_layout: ?SdkLayout, +/// Size of the __PAGEZERO segment. +pagezero_vmsize: u64, +/// Minimum space for future expansion of the load commands. +headerpad_size: u32, +/// Set enough space as if all paths were MATPATHLEN. +headerpad_max_install_names: bool, +/// Remove dylibs that are unreachable by the entry point or exported symbols. +dead_strip_dylibs: bool, +frameworks: []const Framework, +/// Install name for the dylib. +/// TODO: unify with soname +install_name: ?[]const u8, +/// Path to entitlements file. +entitlements: ?[]const u8, +compatibility_version: ?std.SemanticVersion, + +/// When adding a new field, remember to update `hashAddFrameworks`. +pub const Framework = struct { + needed: bool = false, + weak: bool = false, + path: []const u8, +}; - if (options.emit == null) { - return createEmpty(allocator, options); +pub fn hashAddFrameworks(man: *Cache.Manifest, hm: []const Framework) !void { + for (hm) |value| { + man.hash.add(value.needed); + man.hash.add(value.weak); + _ = try man.addFile(value.path, null); } +} - const emit = options.emit.?; - const mode: Mode = mode: { - if (options.use_llvm or options.module == null or options.cache_mode == .whole) - break :mode .zld; - break :mode .incremental; - }; - const sub_path = if (mode == .zld) blk: { - if (options.module == null) { - // No point in opening a file, we would not write anything to it. - // Initialize with empty. - return createEmpty(allocator, 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), - }); - } else emit.sub_path; - errdefer if (mode == .zld) allocator.free(sub_path); +/// 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, +}; + +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*MachO { + const target = comp.root_mod.resolved_target.result; + assert(target.ofmt == .macho); + const use_llvm = comp.config.use_llvm; + const gpa = comp.gpa; + const optimize_mode = comp.root_mod.optimize_mode; + const output_mode = comp.config.output_mode; + const link_mode = comp.config.link_mode; + + // TODO: get rid of zld mode + const mode: Mode = if (use_llvm or !comp.config.have_zcu or comp.cache_use == .whole) + .zld + else + .incremental; + + // If using "zld mode" to link, this code should produce an object file so that it + // can be passed to "zld mode". TODO: get rid of "zld mode". + // If using LLVM to generate the object file for the zig compilation unit, + // we need a place to put the object file so that it can be subsequently + // handled. + const zcu_object_sub_path = if (mode != .zld and !use_llvm) + null + else + try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path}); - const self = try createEmpty(allocator, options); + const self = try arena.create(MachO); + self.* = .{ + .base = .{ + .tag = .macho, + .comp = comp, + .emit = emit, + .zcu_object_sub_path = zcu_object_sub_path, + .gc_sections = options.gc_sections orelse (optimize_mode != .Debug), + .print_gc_sections = options.print_gc_sections, + .stack_size = options.stack_size orelse 16777216, + .allow_shlib_undefined = options.allow_shlib_undefined orelse false, + .file = null, + .disable_lld_caching = options.disable_lld_caching, + .build_id = options.build_id, + .rpath_list = options.rpath_list, + }, + .mode = mode, + .pagezero_vmsize = options.pagezero_size orelse default_pagezero_vmsize, + .headerpad_size = options.headerpad_size orelse default_headerpad_size, + .headerpad_max_install_names = options.headerpad_max_install_names, + .dead_strip_dylibs = options.dead_strip_dylibs, + .sdk_layout = options.darwin_sdk_layout, + .frameworks = options.frameworks, + .install_name = options.install_name, + .entitlements = options.entitlements, + .compatibility_version = options.compatibility_version, + .entry_name = switch (options.entry) { + .disabled => null, + .default => if (output_mode != .Exe) null else default_entry_symbol_name, + .enabled => default_entry_symbol_name, + .named => |name| name, + }, + }; + if (use_llvm and comp.config.have_zcu) { + self.llvm_object = try LlvmObject.create(arena, comp); + } errdefer self.base.destroy(); + log.debug("selected linker mode '{s}'", .{@tagName(self.mode)}); + if (mode == .zld) { - // 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. - self.base.intermediary_basename = sub_path; + // TODO: get rid of zld mode return self; } - const file = try emit.directory.handle.createFile(sub_path, .{ - .truncate = false, + const file = try emit.directory.handle.createFile(emit.sub_path, .{ + .truncate = true, .read = true, - .mode = link.determineMode(options), + .mode = link.File.determineMode(false, output_mode, link_mode), }); - errdefer file.close(); self.base.file = file; - if (!options.strip and options.module != null) { + if (comp.config.debug_format != .strip and comp.module != null) { // Create dSYM bundle. - log.debug("creating {s}.dSYM bundle", .{sub_path}); + log.debug("creating {s}.dSYM bundle", .{emit.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}, + .{emit.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(); - const d_sym_file = try d_sym_bundle.createFile(sub_path, .{ + const d_sym_file = try d_sym_bundle.createFile(emit.sub_path, .{ .truncate = false, .read = true, }); self.d_sym = .{ - .allocator = allocator, - .dwarf = link.File.Dwarf.init(allocator, &self.base, .dwarf32), + .allocator = gpa, + .dwarf = link.File.Dwarf.init(&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(); + try self.populateMissingMetadata(.{ + .symbol_count_hint = options.symbol_count_hint, + .program_code_size_hint = options.program_code_size_hint, + }); if (self.d_sym) |*d_sym| { try d_sym.populateMissingMetadata(self); @@ -234,75 +304,59 @@ 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); - - self.* = .{ - .base = .{ - .tag = .macho, - .options = options, - .allocator = gpa, - .file = null, - }, - .mode = if (options.use_llvm or options.module == null or options.cache_mode == .whole) - .zld - else - .incremental, - }; - - if (options.use_llvm and options.module != null) { - self.llvm_object = try LlvmObject.create(gpa, options); - } - - log.debug("selected linker mode '{s}'", .{@tagName(self.mode)}); - - return self; +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*MachO { + // TODO: restore saved linker state, don't truncate the file, and + // participate in incremental compilation. + return createEmpty(arena, comp, emit, options); } -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; - } +pub fn flush(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { + const comp = self.base.comp; + const gpa = comp.gpa; + const output_mode = comp.config.output_mode; - if (self.base.options.output_mode == .Lib and self.base.options.link_mode == .Static) { + if (output_mode == .Lib and comp.config.link_mode == .Static) { if (build_options.have_llvm) { - return self.base.linkAsArchive(comp, prog_node); + return self.base.linkAsArchive(arena, prog_node); } else { - try self.misc_errors.ensureUnusedCapacity(self.base.allocator, 1); - self.misc_errors.appendAssumeCapacity(.{ - .msg = try self.base.allocator.dupe(u8, "TODO: non-LLVM archiver for MachO object files"), + try comp.link_errors.ensureUnusedCapacity(gpa, 1); + comp.link_errors.appendAssumeCapacity(.{ + .msg = try gpa.dupe(u8, "TODO: non-LLVM archiver for MachO object files"), }); return error.FlushFailure; } } switch (self.mode) { - .zld => return zld.linkWithZld(self, comp, prog_node), - .incremental => return self.flushModule(comp, prog_node), + .zld => return zld.linkWithZld(self, arena, prog_node), + .incremental => return self.flushModule(arena, prog_node), } } -pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { +pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { const tracy = trace(@src()); defer tracy.end(); + const comp = self.base.comp; + const gpa = comp.gpa; + if (self.llvm_object) |llvm_object| { - return try llvm_object.flushModule(comp, prog_node); + try self.base.emitLlvmObject(arena, llvm_object, prog_node); + return; } - var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - var sub_prog_node = prog_node.start("MachO Flush", 0); sub_prog_node.activate(); defer sub_prog_node.end(); - const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + const output_mode = comp.config.output_mode; + const module = comp.module orelse return error.LinkingWithoutZigSourceUnimplemented; + const target = comp.root_mod.resolved_target.result; if (self.lazy_syms.getPtr(.none)) |metadata| { // Most lazy symbols can be updated on first use, but @@ -334,97 +388,44 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No } var libs = std.StringArrayHashMap(link.SystemLib).init(arena); - try self.resolveLibSystem(arena, comp, &.{}, &libs); - - const id_symlink_basename = "link.id"; + try self.resolveLibSystem(arena, comp, &libs); - const cache_dir_handle = module.zig_cache_artifact_directory.handle; - var man: Cache.Manifest = undefined; - defer man.deinit(); - - var digest: [Cache.hex_digest_len]u8 = undefined; - man = comp.cache_parent.obtain(); - man.want_shared_lock = false; self.base.releaseLock(); - man.hash.addListOfBytes(libs.keys()); - - _ = try man.hit(); - digest = man.final(); - - var prev_digest_buf: [digest.len]u8 = undefined; - const prev_digest: []u8 = Cache.readSmallFile( - cache_dir_handle, - id_symlink_basename, - &prev_digest_buf, - ) catch |err| blk: { - log.debug("MachO Zld new_digest={s} error: {s}", .{ - std.fmt.fmtSliceHexLower(&digest), - @errorName(err), - }); - // Handle this as a cache miss. - break :blk prev_digest_buf[0..0]; - }; - const cache_miss: bool = cache_miss: { - if (mem.eql(u8, prev_digest, &digest)) { - log.debug("MachO Zld digest={s} match", .{ - std.fmt.fmtSliceHexLower(&digest), - }); - if (!self.cold_start) { - log.debug(" skipping parsing linker line objects", .{}); - break :cache_miss false; - } else { - log.debug(" TODO parse prelinked binary and continue linking where we left off", .{}); - } - } - log.debug("MachO Zld prev_digest={s} new_digest={s}", .{ - std.fmt.fmtSliceHexLower(prev_digest), - std.fmt.fmtSliceHexLower(&digest), - }); - // We are about to change the output file to be different, so we invalidate the build hash now. - cache_dir_handle.deleteFile(id_symlink_basename) catch |err| switch (err) { - error.FileNotFound => {}, - else => |e| return e, - }; - break :cache_miss true; - }; - - if (cache_miss) { - for (self.dylibs.items) |*dylib| { - dylib.deinit(self.base.allocator); - } - self.dylibs.clearRetainingCapacity(); - self.dylibs_map.clearRetainingCapacity(); - self.referenced_dylibs.clearRetainingCapacity(); - - var dependent_libs = std.fifo.LinearFifo(DylibReExportInfo, .Dynamic).init(arena); + for (self.dylibs.items) |*dylib| { + dylib.deinit(gpa); + } + self.dylibs.clearRetainingCapacity(); + self.dylibs_map.clearRetainingCapacity(); + self.referenced_dylibs.clearRetainingCapacity(); - for (libs.keys(), libs.values()) |path, lib| { - const in_file = try std.fs.cwd().openFile(path, .{}); - defer in_file.close(); + var dependent_libs = std.fifo.LinearFifo(DylibReExportInfo, .Dynamic).init(arena); - var parse_ctx = ParseErrorCtx.init(self.base.allocator); - defer parse_ctx.deinit(); + for (libs.keys(), libs.values()) |path, lib| { + const in_file = try std.fs.cwd().openFile(path, .{}); + defer in_file.close(); - self.parseLibrary( - in_file, - path, - lib, - false, - false, - null, - &dependent_libs, - &parse_ctx, - ) catch |err| try self.handleAndReportParseError(path, err, &parse_ctx); - } + var parse_ctx = ParseErrorCtx.init(gpa); + defer parse_ctx.deinit(); - try self.parseDependentLibs(&dependent_libs); + self.parseLibrary( + in_file, + path, + lib, + false, + false, + null, + &dependent_libs, + &parse_ctx, + ) catch |err| try self.handleAndReportParseError(path, err, &parse_ctx); } + try self.parseDependentLibs(&dependent_libs); + try self.resolveSymbols(); if (self.getEntryPoint() == null) { - self.error_flags.no_entry_point_found = true; + comp.link_error_flags.no_entry_point_found = true; } if (self.unresolved.count() > 0) { try self.reportUndefined(); @@ -445,7 +446,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try self.createDyldPrivateAtom(); try self.writeStubHelperPreamble(); - if (self.base.options.output_mode == .Exe and self.getEntryPoint() != null) { + if (output_mode == .Exe and self.getEntryPoint() != null) { const global = self.getEntryPoint().?; if (self.getSymbol(global).undf()) { // We do one additional check here in case the entry point was found in one of the dylibs. @@ -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); @@ -509,21 +510,21 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try self.writeLinkeditSegmentData(); - var codesig: ?CodeSignature = if (requiresCodeSignature(&self.base.options)) blk: { + var codesig: ?CodeSignature = if (self.requiresCodeSignature()) blk: { // Preallocate space for the code signature. // We need to do this at this stage so that we have the load commands with proper values // written out to the file. // The most important here is to have the correct vm and filesize of the __LINKEDIT segment // where the code signature goes into. - 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); + var codesig = CodeSignature.init(getPageSize(target.cpu.arch)); + codesig.code_directory.ident = self.base.emit.sub_path; + if (self.entitlements) |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); @@ -535,7 +536,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try lc_writer.writeStruct(self.dysymtab_cmd); try load_commands.writeDylinkerLC(lc_writer); - switch (self.base.options.output_mode) { + switch (output_mode) { .Exe => blk: { const seg_id = self.header_segment_cmd_index.?; const seg = self.segments.items[seg_id]; @@ -551,22 +552,22 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No try lc_writer.writeStruct(macho.entry_point_command{ .entryoff = @as(u32, @intCast(addr - seg.vmaddr)), - .stacksize = self.base.options.stack_size_override orelse 0, + .stacksize = self.base.stack_size, }); }, - .Lib => if (self.base.options.link_mode == .Dynamic) { - try load_commands.writeDylibIdLC(self.base.allocator, &self.base.options, lc_writer); + .Lib => if (comp.config.link_mode == .Dynamic) { + try load_commands.writeDylibIdLC(self, lc_writer); }, else => {}, } - try load_commands.writeRpathLCs(self.base.allocator, &self.base.options, lc_writer); + try load_commands.writeRpathLCs(self, lc_writer); try lc_writer.writeStruct(macho.source_version_command{ .version = 0, }); { - const platform = Platform.fromTarget(self.base.options.target); - const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(arena, comp); + const platform = Platform.fromTarget(target); + const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(self); if (platform.isBuildVersionCompatible()) { try load_commands.writeBuildVersionLC(platform, sdk_version, lc_writer); } else if (platform.isVersionMinCompatible()) { @@ -590,7 +591,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No if (codesig) |*csig| { try self.writeCodeSignature(comp, csig); // code signing always comes last - const emit = self.base.options.emit.?; + const emit = self.base.emit; try invalidateKernelCache(emit.directory.handle, emit.sub_path); } @@ -598,23 +599,6 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No // Flush debug symbols bundle. try d_sym.flushModule(self); } - - if (cache_miss) { - // Update the file with the digest. If it fails we can continue; it only - // means that the next invocation will have an unnecessary cache miss. - Cache.writeSmallFile(cache_dir_handle, id_symlink_basename, &digest) catch |err| { - log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)}); - }; - // Again failure here only means an unnecessary cache miss. - man.writeManifest() catch |err| { - log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)}); - }; - // We hang on to this lock so that the output file path can be used without - // other processes clobbering it. - self.base.lock = man.toOwnedLock(); - } - - self.cold_start = false; } /// XNU starting with Big Sur running on arm64 is caching inodes of running binaries. @@ -641,33 +625,20 @@ pub fn resolveLibSystem( self: *MachO, arena: Allocator, comp: *Compilation, - search_dirs: []const []const u8, out_libs: anytype, ) !void { - var tmp_arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); - defer tmp_arena_allocator.deinit(); - const tmp_arena = tmp_arena_allocator.allocator(); - - var test_path = std.ArrayList(u8).init(tmp_arena); - var checked_paths = std.ArrayList([]const u8).init(tmp_arena); + var test_path = std.ArrayList(u8).init(arena); + var checked_paths = std.ArrayList([]const u8).init(arena); success: { - for (search_dirs) |dir| if (try accessLibPath( - tmp_arena, - &test_path, - &checked_paths, - dir, - "libSystem", - )) break :success; - - if (self.base.options.darwin_sdk_layout) |sdk_layout| switch (sdk_layout) { + if (self.sdk_layout) |sdk_layout| switch (sdk_layout) { .sdk => { - const dir = try fs.path.join(tmp_arena, &[_][]const u8{ self.base.options.sysroot.?, "usr", "lib" }); - if (try accessLibPath(tmp_arena, &test_path, &checked_paths, dir, "libSystem")) break :success; + const dir = try fs.path.join(arena, &[_][]const u8{ comp.sysroot.?, "usr", "lib" }); + if (try accessLibPath(arena, &test_path, &checked_paths, dir, "libSystem")) break :success; }, .vendored => { - const dir = try comp.zig_lib_directory.join(tmp_arena, &[_][]const u8{ "libc", "darwin" }); - if (try accessLibPath(tmp_arena, &test_path, &checked_paths, dir, "libSystem")) break :success; + const dir = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "darwin" }); + if (try accessLibPath(arena, &test_path, &checked_paths, dir, "libSystem")) break :success; }, }; @@ -775,7 +746,8 @@ fn parseObject( const tracy = trace(@src()); defer tracy.end(); - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; + const target = self.base.comp.root_mod.resolved_target.result; const mtime: u64 = mtime: { const stat = file.stat() catch break :mtime 0; break :mtime @as(u64, @intCast(@divFloor(stat.mtime, 1_000_000_000))); @@ -798,8 +770,8 @@ fn parseObject( else => unreachable, }; const detected_platform = object.getPlatform(); - const this_cpu_arch = self.base.options.target.cpu.arch; - const this_platform = Platform.fromTarget(self.base.options.target); + const this_cpu_arch = target.cpu.arch; + const this_platform = Platform.fromTarget(target); if (this_cpu_arch != detected_cpu_arch or (detected_platform != null and !detected_platform.?.eqlTarget(this_platform))) @@ -826,8 +798,10 @@ pub fn parseLibrary( const tracy = trace(@src()); defer tracy.end(); + const target = self.base.comp.root_mod.resolved_target.result; + if (fat.isFatLibrary(file)) { - const offset = try self.parseFatLibrary(file, self.base.options.target.cpu.arch, ctx); + const offset = try self.parseFatLibrary(file, target.cpu.arch, ctx); try file.seekTo(offset); if (Archive.isArchive(file, offset)) { @@ -868,7 +842,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 +866,8 @@ fn parseArchive( must_link: bool, ctx: *ParseErrorCtx, ) ParseError!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; + const target = self.base.comp.root_mod.resolved_target.result; // 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? @@ -922,8 +897,8 @@ fn parseArchive( else => unreachable, }; const detected_platform = object.getPlatform(); - const this_cpu_arch = self.base.options.target.cpu.arch; - const this_platform = Platform.fromTarget(self.base.options.target); + const this_cpu_arch = target.cpu.arch; + const this_platform = Platform.fromTarget(target); if (this_cpu_arch != detected_cpu_arch or (detected_platform != null and !detected_platform.?.eqlTarget(this_platform))) @@ -973,7 +948,8 @@ fn parseDylib( dylib_options: DylibOpts, ctx: *ParseErrorCtx, ) ParseError!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; + const target = self.base.comp.root_mod.resolved_target.result; const file_stat = try file.stat(); const file_size = math.cast(usize, file_stat.size - offset) orelse return error.Overflow; @@ -997,8 +973,8 @@ fn parseDylib( else => unreachable, }; const detected_platform = dylib.getPlatform(contents); - const this_cpu_arch = self.base.options.target.cpu.arch; - const this_platform = Platform.fromTarget(self.base.options.target); + const this_cpu_arch = target.cpu.arch; + const this_platform = Platform.fromTarget(target); if (this_cpu_arch != detected_cpu_arch or (detected_platform != null and !detected_platform.?.eqlTarget(this_platform))) @@ -1019,7 +995,9 @@ fn parseLibStub( dylib_options: DylibOpts, ctx: *ParseErrorCtx, ) ParseError!void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; + const target = self.base.comp.root_mod.resolved_target.result; + var lib_stub = try LibStub.loadFromFile(gpa, file); defer lib_stub.deinit(); @@ -1027,7 +1005,7 @@ fn parseLibStub( // Verify target { - var matcher = try Dylib.TargetMatcher.init(gpa, self.base.options.target); + var matcher = try Dylib.TargetMatcher.init(gpa, target); defer matcher.deinit(); const first_tbd = lib_stub.inner[0]; @@ -1050,7 +1028,7 @@ fn parseLibStub( try dylib.parseFromStub( gpa, - self.base.options.target, + target, lib_stub, @intCast(self.dylibs.items.len), // TODO defer it till later dependent_libs, @@ -1072,7 +1050,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; @@ -1080,7 +1058,7 @@ fn addDylib(self: *MachO, dylib: Dylib, dylib_options: DylibOpts, ctx: *ParseErr try self.dylibs.append(gpa, dylib); const should_link_dylib_even_if_unreachable = blk: { - if (self.base.options.dead_strip_dylibs and !dylib_options.needed) break :blk false; + if (self.dead_strip_dylibs and !dylib_options.needed) break :blk false; break :blk !(dylib_options.dependent or self.referenced_dylibs.contains(gop.value_ptr.*)); }; @@ -1098,7 +1076,8 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void { // 2) afterwards, we parse dependents of the included dylibs // TODO this should not be performed if the user specifies `-flat_namespace` flag. // See ld64 manpages. - const gpa = self.base.allocator; + const comp = self.base.comp; + const gpa = comp.gpa; while (dependent_libs.readItem()) |dep_id| { defer dep_id.id.deinit(gpa); @@ -1118,7 +1097,7 @@ pub fn parseDependentLibs(self: *MachO, dependent_libs: anytype) !void { var checked_paths = std.ArrayList([]const u8).init(arena); success: { - if (self.base.options.sysroot) |root| { + if (comp.sysroot) |root| { const dir = try fs.path.join(arena, &[_][]const u8{ root, dirname }); if (try accessLibPath(gpa, &test_path, &checked_paths, dir, stem)) break :success; } @@ -1162,7 +1141,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| { @@ -1194,7 +1174,8 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void { fn writeToMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: u8, addr: u64, code: []const u8) !void { const segment = self.segments.items[segment_index]; - const cpu_arch = self.base.options.target.cpu.arch; + const target = self.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; const nwritten = if (!segment.isWriteable()) try task.writeMemProtected(addr, code, cpu_arch) else @@ -1237,8 +1218,9 @@ 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 cpu_arch = self.base.options.target.cpu.arch; + const gpa = self.base.comp.gpa; + const target = self.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; const size = stubs.stubHelperPreambleSize(cpu_arch); var buf = try std.ArrayList(u8).initCapacity(gpa, size); @@ -1264,11 +1246,12 @@ fn writeStubHelperPreamble(self: *MachO) !void { } fn writeStubTableEntry(self: *MachO, index: usize) !void { + const target = self.base.comp.root_mod.resolved_target.result; const stubs_sect_id = self.stubs_section_index.?; const stub_helper_sect_id = self.stub_helper_section_index.?; const laptr_sect_id = self.la_symbol_ptr_section_index.?; - const cpu_arch = self.base.options.target.cpu.arch; + const cpu_arch = target.cpu.arch; const stub_entry_size = stubs.stubSize(cpu_arch); const stub_helper_entry_size = stubs.stubHelperSize(cpu_arch); const stub_helper_preamble_size = stubs.stubHelperPreambleSize(cpu_arch); @@ -1290,7 +1273,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 +1452,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 +1464,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 +1519,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 +1544,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(); @@ -1593,9 +1577,10 @@ 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 output_mode = self.base.comp.config.output_mode; + if (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 +1607,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); @@ -1643,16 +1628,19 @@ pub fn createDsoHandleSymbol(self: *MachO) !void { } pub fn resolveSymbols(self: *MachO) !void { + const comp = self.base.comp; + const output_mode = comp.config.output_mode; // We add the specified entrypoint as the first unresolved symbols so that // we search for it in libraries should there be no object files specified // on the linker line. - if (self.base.options.output_mode == .Exe) { - const entry_name = self.base.options.entry orelse load_commands.default_entry_point; - _ = try self.addUndefined(entry_name, .{}); + if (output_mode == .Exe) { + if (self.entry_name) |entry_name| { + _ = try self.addUndefined(entry_name, .{}); + } } // Force resolution of any symbols requested by the user. - for (self.base.options.force_undefined_symbols.keys()) |sym_name| { + for (comp.force_undefined_symbols.keys()) |sym_name| { _ = try self.addUndefined(sym_name, .{}); } @@ -1667,7 +1655,7 @@ pub fn resolveSymbols(self: *MachO) !void { if (self.unresolved.count() > 0 and self.dyld_stub_binder_index == null) { self.dyld_stub_binder_index = try self.addUndefined("dyld_stub_binder", .{ .add_got = true }); } - if (!self.base.options.single_threaded and self.mode == .incremental) { + if (comp.config.any_non_single_threaded and self.mode == .incremental) { _ = try self.addUndefined("__tlv_bootstrap", .{}); } @@ -1686,7 +1674,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 +1788,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 +1817,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]; @@ -1863,9 +1851,10 @@ fn resolveSymbolsInDylibs(self: *MachO) !void { } fn resolveSymbolsAtLoading(self: *MachO) !void { - const is_lib = self.base.options.output_mode == .Lib; - const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; - const allow_undef = is_dyn_lib and (self.base.options.allow_shlib_undefined orelse false); + const output_mode = self.base.comp.config.output_mode; + const is_lib = output_mode == .Lib; + const is_dyn_lib = self.base.comp.config.link_mode == .Dynamic and is_lib; + const allow_undef = is_dyn_lib and self.base.allow_shlib_undefined; var next_sym: usize = 0; while (next_sym < self.unresolved.count()) { @@ -1899,6 +1888,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 +1899,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 +1919,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(); @@ -2024,15 +2014,10 @@ pub fn deinit(self: *MachO) void { bindings.deinit(gpa); } self.bindings.deinit(gpa); - - for (self.misc_errors.items) |*err| { - err.deinit(gpa); - } - self.misc_errors.deinit(gpa); } 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 +2109,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 +2136,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| { @@ -2169,9 +2156,10 @@ fn allocateGlobal(self: *MachO) !u32 { return index; } -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); +pub fn addGotEntry(self: *MachO, reloc_target: SymbolWithLoc) !void { + if (self.got_table.lookup.contains(reloc_target)) return; + const gpa = self.base.comp.gpa; + const got_index = try self.got_table.allocateEntry(gpa, reloc_target); if (self.got_section_index == null) { self.got_section_index = try self.initSection("__DATA_CONST", "__got", .{ .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, @@ -2180,19 +2168,22 @@ pub fn addGotEntry(self: *MachO, target: SymbolWithLoc) !void { if (self.mode == .incremental) { try self.writeOffsetTableEntry(got_index); self.got_table_count_dirty = true; - self.markRelocsDirtyByTarget(target); + self.markRelocsDirtyByTarget(reloc_target); } } -pub fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void { - if (self.stub_table.lookup.contains(target)) return; - const stub_index = try self.stub_table.allocateEntry(self.base.allocator, target); +pub fn addStubEntry(self: *MachO, reloc_target: SymbolWithLoc) !void { + if (self.stub_table.lookup.contains(reloc_target)) return; + const comp = self.base.comp; + const gpa = comp.gpa; + const cpu_arch = comp.root_mod.resolved_target.result.cpu.arch; + const stub_index = try self.stub_table.allocateEntry(gpa, reloc_target); if (self.stubs_section_index == null) { self.stubs_section_index = try self.initSection("__TEXT", "__stubs", .{ .flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS, - .reserved2 = stubs.stubSize(self.base.options.target.cpu.arch), + .reserved2 = stubs.stubSize(cpu_arch), }); self.stub_helper_section_index = try self.initSection("__TEXT", "__stub_helper", .{ .flags = macho.S_REGULAR | @@ -2206,13 +2197,14 @@ pub fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void { if (self.mode == .incremental) { try self.writeStubTableEntry(stub_index); self.stub_table_count_dirty = true; - self.markRelocsDirtyByTarget(target); + self.markRelocsDirtyByTarget(reloc_target); } } -pub fn addTlvPtrEntry(self: *MachO, target: SymbolWithLoc) !void { - if (self.tlv_ptr_table.lookup.contains(target)) return; - _ = try self.tlv_ptr_table.allocateEntry(self.base.allocator, target); +pub fn addTlvPtrEntry(self: *MachO, reloc_target: SymbolWithLoc) !void { + if (self.tlv_ptr_table.lookup.contains(reloc_target)) return; + const gpa = self.base.comp.gpa; + _ = try self.tlv_ptr_table.allocateEntry(gpa, reloc_target); if (self.tlv_ptr_section_index == null) { self.tlv_ptr_section_index = try self.initSection("__DATA", "__thread_ptrs", .{ .flags = macho.S_THREAD_LOCAL_VARIABLE_POINTERS, @@ -2236,7 +2228,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,8 +2272,8 @@ 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 mod = self.base.options.module.?; + const gpa = self.base.comp.gpa; + const mod = self.base.comp.module.?; const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index); if (!gop.found_existing) { gop.value_ptr.* = .{}; @@ -2318,7 +2311,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 +2359,8 @@ pub fn updateDecl(self: *MachO, mod: *Module, decl_index: InternPool.DeclIndex) const tracy = trace(@src()); defer tracy.end(); + const comp = self.base.comp; + const gpa = comp.gpa; const decl = mod.declPtr(decl_index); if (decl.val.getExternFunc(mod)) |_| { @@ -2375,14 +2370,14 @@ 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; } const is_threadlocal = if (decl.val.getVariable(mod)) |variable| - variable.is_threadlocal and !self.base.options.single_threaded + variable.is_threadlocal and comp.config.any_non_single_threaded else false; if (is_threadlocal) return self.updateThreadlocalVariable(mod, decl_index); @@ -2391,7 +2386,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,8 +2444,8 @@ fn updateLazySymbolAtom( atom_index: Atom.Index, section_index: u8, ) !void { - const gpa = self.base.allocator; - const mod = self.base.options.module.?; + const gpa = self.base.comp.gpa; + const mod = self.base.comp.module.?; var required_alignment: Alignment = .none; var code_buffer = std.ArrayList(u8).init(gpa); @@ -2514,8 +2509,9 @@ 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 mod = self.base.comp.module.?; + 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 +2525,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 => {}, @@ -2545,7 +2541,7 @@ pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.In } fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: InternPool.DeclIndex) !void { - const mod = self.base.options.module.?; + const mod = self.base.comp.module.?; // Lowering a TLV on macOS involves two stages: // 1. first we lower the initializer into appopriate section (__thread_data or __thread_bss) // 2. next, we create a corresponding threadlocal variable descriptor in __thread_vars @@ -2556,7 +2552,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 +2636,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), @@ -2655,17 +2652,17 @@ pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: InternPool.DeclIndex) !A } fn getDeclOutputSection(self: *MachO, decl_index: InternPool.DeclIndex) u8 { - const decl = self.base.options.module.?.declPtr(decl_index); + const decl = self.base.comp.module.?.declPtr(decl_index); const ty = decl.ty; const val = decl.val; - const mod = self.base.options.module.?; + const mod = self.base.comp.module.?; const zig_ty = ty.zigTypeTag(mod); - const mode = self.base.options.optimize_mode; - const single_threaded = self.base.options.single_threaded; + const any_non_single_threaded = self.base.comp.config.any_non_single_threaded; + const optimize_mode = self.base.comp.root_mod.optimize_mode; const sect_id: u8 = blk: { // TODO finish and audit this function if (val.isUndefDeep(mod)) { - if (mode == .ReleaseFast or mode == .ReleaseSmall) { + if (optimize_mode == .ReleaseFast or optimize_mode == .ReleaseSmall) { @panic("TODO __DATA,__bss"); } else { break :blk self.data_section_index.?; @@ -2673,7 +2670,7 @@ fn getDeclOutputSection(self: *MachO, decl_index: InternPool.DeclIndex) u8 { } if (val.getVariable(mod)) |variable| { - if (variable.is_threadlocal and !single_threaded) { + if (variable.is_threadlocal and any_non_single_threaded) { break :blk self.thread_data_section_index.?; } break :blk self.data_section_index.?; @@ -2694,8 +2691,8 @@ 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 mod = self.base.options.module.?; + const gpa = self.base.comp.gpa; + const mod = self.base.comp.module.?; const decl = mod.declPtr(decl_index); const required_alignment = decl.getAlignment(mod); @@ -2782,12 +2779,10 @@ pub fn updateExports( if (self.llvm_object) |llvm_object| return llvm_object.updateExports(mod, exported, exports); - if (self.base.options.emit == null) return; - 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,8 +2907,8 @@ pub fn deleteDeclExport( if (self.llvm_object) |_| return; const metadata = self.decls.getPtr(decl_index) orelse return; - const gpa = self.base.allocator; - const mod = self.base.options.module.?; + const gpa = self.base.comp.gpa; + const mod = self.base.comp.module.?; const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{mod.intern_pool.stringToSlice(name)}); defer gpa.free(exp_name); const sym_index = metadata.getExportPtr(self, exp_name) orelse return; @@ -2941,7 +2936,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,7 +2946,8 @@ 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 mod = self.base.options.module.?; + const gpa = self.base.comp.gpa; + const mod = self.base.comp.module.?; const decl = mod.declPtr(decl_index); log.debug("freeDecl {*}", .{decl}); @@ -2960,7 +2956,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,8 +2989,8 @@ pub fn lowerAnonDecl( explicit_alignment: InternPool.Alignment, src_loc: Module.SrcLoc, ) !codegen.Result { - const gpa = self.base.allocator; - const mod = self.base.options.module.?; + const gpa = self.base.comp.gpa; + const mod = self.base.comp.module.?; const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val)); const decl_alignment = switch (explicit_alignment) { .none => ty.abiAlignment(mod), @@ -3057,11 +3053,18 @@ pub fn getAnonDeclVAddr(self: *MachO, decl_val: InternPool.Index, reloc_info: li return 0; } -fn populateMissingMetadata(self: *MachO) !void { +const PopulateMissingMetadataOptions = struct { + symbol_count_hint: u64, + program_code_size_hint: u64, +}; + +fn populateMissingMetadata(self: *MachO, options: PopulateMissingMetadataOptions) !void { assert(self.mode == .incremental); - const gpa = self.base.allocator; - const cpu_arch = self.base.options.target.cpu.arch; + const comp = self.base.comp; + const gpa = comp.gpa; + const target = comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; const pagezero_vmsize = self.calcPagezeroSize(); if (self.pagezero_segment_cmd_index == null) { @@ -3078,7 +3081,7 @@ fn populateMissingMetadata(self: *MachO) !void { if (self.header_segment_cmd_index == null) { // The first __TEXT segment is immovable and covers MachO header and load commands. self.header_segment_cmd_index = @as(u8, @intCast(self.segments.items.len)); - const ideal_size = @max(self.base.options.headerpad_size orelse 0, default_headerpad_size); + const ideal_size = self.headerpad_size; const needed_size = mem.alignForward(u64, padToIdeal(ideal_size), getPageSize(cpu_arch)); log.debug("found __TEXT segment (header-only) free space 0x{x} to 0x{x}", .{ 0, needed_size }); @@ -3098,7 +3101,7 @@ fn populateMissingMetadata(self: *MachO) !void { if (self.text_section_index == null) { // Sadly, segments need unique string identfiers for some reason. self.text_section_index = try self.allocateSection("__TEXT1", "__text", .{ - .size = self.base.options.program_code_size_hint, + .size = options.program_code_size_hint, .alignment = switch (cpu_arch) { .x86_64 => 1, .aarch64 => @sizeOf(u32), @@ -3134,7 +3137,7 @@ fn populateMissingMetadata(self: *MachO) !void { if (self.got_section_index == null) { self.got_section_index = try self.allocateSection("__DATA_CONST", "__got", .{ - .size = @sizeOf(u64) * self.base.options.symbol_count_hint, + .size = @sizeOf(u64) * options.symbol_count_hint, .alignment = @alignOf(u64), .flags = macho.S_NON_LAZY_SYMBOL_POINTERS, .prot = macho.PROT.READ | macho.PROT.WRITE, @@ -3172,7 +3175,7 @@ fn populateMissingMetadata(self: *MachO) !void { self.segment_table_dirty = true; } - if (!self.base.options.single_threaded) { + if (comp.config.any_non_single_threaded) { if (self.thread_vars_section_index == null) { self.thread_vars_section_index = try self.allocateSection("__DATA2", "__thread_vars", .{ .size = @sizeOf(u64) * 3, @@ -3207,13 +3210,14 @@ fn populateMissingMetadata(self: *MachO) !void { } fn calcPagezeroSize(self: *MachO) u64 { - const pagezero_vmsize = self.base.options.pagezero_size orelse default_pagezero_vmsize; - const page_size = getPageSize(self.base.options.target.cpu.arch); - const aligned_pagezero_vmsize = mem.alignBackward(u64, pagezero_vmsize, page_size); - if (self.base.options.output_mode == .Lib) return 0; + const output_mode = self.base.comp.config.output_mode; + const target = self.base.comp.root_mod.resolved_target.result; + const page_size = getPageSize(target.cpu.arch); + const aligned_pagezero_vmsize = mem.alignBackward(u64, self.pagezero_vmsize, page_size); + if (output_mode == .Lib) return 0; if (aligned_pagezero_vmsize == 0) return 0; - if (aligned_pagezero_vmsize != pagezero_vmsize) { - log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize}); + if (aligned_pagezero_vmsize != self.pagezero_vmsize) { + log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{self.pagezero_vmsize}); log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize}); } return aligned_pagezero_vmsize; @@ -3228,7 +3232,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,8 +3253,9 @@ 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 page_size = getPageSize(self.base.options.target.cpu.arch); + const gpa = self.base.comp.gpa; + const target = self.base.comp.root_mod.resolved_target.result; + const page_size = getPageSize(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. const segment_id = @as(u8, @intCast(self.segments.items.len)); @@ -3304,7 +3310,8 @@ fn growSection(self: *MachO, sect_id: u8, needed_size: u64) !void { const segment = &self.segments.items[segment_index]; const maybe_last_atom_index = self.sections.items(.last_atom_index)[sect_id]; const sect_capacity = self.allocatedSize(header.offset); - const page_size = getPageSize(self.base.options.target.cpu.arch); + const target = self.base.comp.root_mod.resolved_target.result; + const page_size = getPageSize(target.cpu.arch); if (needed_size > sect_capacity) { const new_offset = self.findFreeSpace(needed_size, page_size); @@ -3344,7 +3351,8 @@ fn growSection(self: *MachO, sect_id: u8, needed_size: u64) !void { } fn growSectionVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void { - const page_size = getPageSize(self.base.options.target.cpu.arch); + const target = self.base.comp.root_mod.resolved_target.result; + const page_size = getPageSize(target.cpu.arch); const header = &self.sections.items(.header)[sect_id]; const segment = self.getSegmentPtr(sect_id); const increased_size = padToIdeal(needed_size); @@ -3521,7 +3529,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 }); @@ -3555,7 +3563,8 @@ pub fn writeSegmentHeaders(self: *MachO, writer: anytype) !void { } pub fn writeLinkeditSegmentData(self: *MachO) !void { - const page_size = getPageSize(self.base.options.target.cpu.arch); + const target = self.base.comp.root_mod.resolved_target.result; + const page_size = getPageSize(target.cpu.arch); const seg = self.getLinkeditSegmentPtr(); seg.filesize = 0; seg.vmsize = 0; @@ -3582,7 +3591,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 +3614,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| { @@ -3642,7 +3651,8 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { } // Finally, unpack the rest. - const cpu_arch = self.base.options.target.cpu.arch; + const target = self.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; for (self.objects.items) |*object| { for (object.atoms.items) |atom_index| { const atom = self.getAtom(atom_index); @@ -3688,14 +3698,14 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { }, else => unreachable, } - const target = Atom.parseRelocTarget(self, .{ + const reloc_target = Atom.parseRelocTarget(self, .{ .object_id = atom.getFile().?, .rel = rel, .code = code, .base_offset = ctx.base_offset, .base_addr = ctx.base_addr, }); - const target_sym = self.getSymbol(target); + const target_sym = self.getSymbol(reloc_target); if (target_sym.undf()) continue; const base_offset = @as(i32, @intCast(sym.n_value - segment.vmaddr)); @@ -3715,7 +3725,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 +3756,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| { @@ -3797,7 +3807,8 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { } // Finally, unpack the rest. - const cpu_arch = self.base.options.target.cpu.arch; + const target = self.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; for (self.objects.items) |*object| { for (object.atoms.items) |atom_index| { const atom = self.getAtom(atom_index); @@ -3885,12 +3896,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 +3934,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); @@ -4015,7 +4027,8 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: anytype) !void { const header = self.sections.items(.header)[stub_helper_section_index]; - const cpu_arch = self.base.options.target.cpu.arch; + const target = self.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; const preamble_size = stubs.stubHelperPreambleSize(cpu_arch); const stub_size = stubs.stubHelperSize(cpu_arch); const stub_offset = stubs.stubOffsetInStubHelper(cpu_arch); @@ -4046,7 +4059,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 +4146,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 +4224,15 @@ 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 comp = self.base.comp; + const gpa = comp.gpa; var locals = std.ArrayList(macho.nlist_64).init(gpa); defer locals.deinit(); @@ -4272,7 +4287,7 @@ fn writeSymtab(self: *MachO) !SymtabCtx { // We generate stabs last in order to ensure that the strtab always has debug info // strings trailing - if (!self.base.options.strip) { + if (comp.config.debug_format != .strip) { for (self.objects.items) |object| { assert(self.d_sym == null); // TODO try self.generateSymbolStabs(object, &locals); @@ -4322,7 +4337,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 +4465,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 +4551,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 +4580,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; @@ -4650,13 +4665,14 @@ pub fn writeUuid(self: *MachO, comp: *const Compilation, uuid_cmd_offset: u32, h } pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { + const target = self.base.comp.root_mod.resolved_target.result; const seg = self.getLinkeditSegmentPtr(); // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271 const offset = mem.alignForward(u64, seg.fileoff + seg.filesize, 16); const needed_size = code_sig.estimateSize(offset); seg.filesize = offset + needed_size - seg.fileoff; - seg.vmsize = mem.alignForward(u64, seg.filesize, getPageSize(self.base.options.target.cpu.arch)); + seg.vmsize = mem.alignForward(u64, seg.filesize, getPageSize(target.cpu.arch)); log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ offset, offset + needed_size }); // Pad out the space. We need to do this to calculate valid hashes for everything in the file // except for code signature data. @@ -4667,11 +4683,13 @@ pub fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { } pub fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *CodeSignature) !void { + const output_mode = self.base.comp.config.output_mode; const seg_id = self.header_segment_cmd_index.?; 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, .{ @@ -4679,7 +4697,7 @@ pub fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *Cod .exec_seg_base = seg.fileoff, .exec_seg_limit = seg.filesize, .file_size = offset, - .output_mode = self.base.options.output_mode, + .output_mode = output_mode, }, buffer.writer()); assert(buffer.items.len == code_sig.size()); @@ -4693,10 +4711,13 @@ pub fn writeCodeSignature(self: *MachO, comp: *const Compilation, code_sig: *Cod /// Writes Mach-O file header. pub fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void { + const output_mode = self.base.comp.config.output_mode; + var header: macho.mach_header_64 = .{}; header.flags = macho.MH_NOUNDEFS | macho.MH_DYLDLINK | macho.MH_PIE | macho.MH_TWOLEVEL; - switch (self.base.options.target.cpu.arch) { + const target = self.base.comp.root_mod.resolved_target.result; + switch (target.cpu.arch) { .aarch64 => { header.cputype = macho.CPU_TYPE_ARM64; header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL; @@ -4708,7 +4729,7 @@ pub fn writeHeader(self: *MachO, ncmds: u32, sizeofcmds: u32) !void { else => unreachable, } - switch (self.base.options.output_mode) { + switch (output_mode) { .Exe => { header.filetype = macho.MH_EXECUTE; }, @@ -4817,7 +4838,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 +4863,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 +5044,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); @@ -5060,15 +5082,16 @@ pub fn getTlvPtrEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 { } pub fn getStubsEntryAddress(self: *MachO, sym_with_loc: SymbolWithLoc) ?u64 { + const target = self.base.comp.root_mod.resolved_target.result; const index = self.stub_table.lookup.get(sym_with_loc) orelse return null; const header = self.sections.items(.header)[self.stubs_section_index.?]; - return header.addr + stubs.stubSize(self.base.options.target.cpu.arch) * index; + return header.addr + stubs.stubSize(target.cpu.arch) * index; } /// Returns symbol location corresponding to the set entrypoint if any. /// Asserts output mode is executable. pub fn getEntryPoint(self: MachO) ?SymbolWithLoc { - const entry_name = self.base.options.entry orelse load_commands.default_entry_point; + const entry_name = self.entry_name orelse return null; const global = self.getGlobal(entry_name) orelse return null; return global; } @@ -5086,11 +5109,13 @@ pub inline fn getPageSize(cpu_arch: std.Target.Cpu.Arch) u16 { }; } -pub fn requiresCodeSignature(options: *const link.Options) bool { - if (options.entitlements) |_| return true; - const cpu_arch = options.target.cpu.arch; - const os_tag = options.target.os.tag; - const abi = options.target.abi; +pub fn requiresCodeSignature(m: *MachO) bool { + if (m.entitlements) |_| return true; + const comp = m.base.comp; + const target = comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; + const os_tag = target.os.tag; + const abi = target.abi; if (cpu_arch == .aarch64 and (os_tag == .macos or abi == .simulator)) return true; return false; } @@ -5171,7 +5196,9 @@ pub fn handleAndReportParseError( err: ParseError, ctx: *const ParseErrorCtx, ) error{OutOfMemory}!void { - const cpu_arch = self.base.options.target.cpu.arch; + const target = self.base.comp.root_mod.resolved_target.result; + const gpa = self.base.comp.gpa; + const cpu_arch = target.cpu.arch; switch (err) { error.DylibAlreadyExists => {}, error.IncompatibleDylibVersion => { @@ -5188,7 +5215,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) { @@ -5206,7 +5233,7 @@ pub fn handleAndReportParseError( error.InvalidTarget => try self.reportParseError( path, "invalid target: expected '{}', but found '{s}'", - .{ Platform.fromTarget(self.base.options.target).fmtTarget(cpu_arch), targets_string.items }, + .{ Platform.fromTarget(target).fmtTarget(cpu_arch), targets_string.items }, ), error.InvalidTargetFatLibrary => try self.reportParseError( path, @@ -5226,14 +5253,15 @@ fn reportMissingLibraryError( comptime format: []const u8, args: anytype, ) error{OutOfMemory}!void { - const gpa = self.base.allocator; - try self.misc_errors.ensureUnusedCapacity(gpa, 1); + const comp = self.base.comp; + const gpa = comp.gpa; + try comp.link_errors.ensureUnusedCapacity(gpa, 1); const notes = try gpa.alloc(File.ErrorMsg, checked_paths.len); errdefer gpa.free(notes); for (checked_paths, notes) |path, *note| { note.* = .{ .msg = try std.fmt.allocPrint(gpa, "tried {s}", .{path}) }; } - self.misc_errors.appendAssumeCapacity(.{ + comp.link_errors.appendAssumeCapacity(.{ .msg = try std.fmt.allocPrint(gpa, format, args), .notes = notes, }); @@ -5246,15 +5274,16 @@ fn reportDependencyError( comptime format: []const u8, args: anytype, ) error{OutOfMemory}!void { - const gpa = self.base.allocator; - try self.misc_errors.ensureUnusedCapacity(gpa, 1); + const comp = self.base.comp; + const gpa = comp.gpa; + try comp.link_errors.ensureUnusedCapacity(gpa, 1); var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 2); defer notes.deinit(); if (path) |p| { notes.appendAssumeCapacity(.{ .msg = try std.fmt.allocPrint(gpa, "while parsing {s}", .{p}) }); } notes.appendAssumeCapacity(.{ .msg = try std.fmt.allocPrint(gpa, "a dependency of {s}", .{parent}) }); - self.misc_errors.appendAssumeCapacity(.{ + comp.link_errors.appendAssumeCapacity(.{ .msg = try std.fmt.allocPrint(gpa, format, args), .notes = try notes.toOwnedSlice(), }); @@ -5266,12 +5295,13 @@ pub fn reportParseError( comptime format: []const u8, args: anytype, ) error{OutOfMemory}!void { - const gpa = self.base.allocator; - try self.misc_errors.ensureUnusedCapacity(gpa, 1); + const comp = self.base.comp; + const gpa = comp.gpa; + try comp.link_errors.ensureUnusedCapacity(gpa, 1); var notes = try gpa.alloc(File.ErrorMsg, 1); errdefer gpa.free(notes); notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "while parsing {s}", .{path}) }; - self.misc_errors.appendAssumeCapacity(.{ + comp.link_errors.appendAssumeCapacity(.{ .msg = try std.fmt.allocPrint(gpa, format, args), .notes = notes, }); @@ -5283,21 +5313,23 @@ pub fn reportUnresolvedBoundarySymbol( comptime format: []const u8, args: anytype, ) error{OutOfMemory}!void { - const gpa = self.base.allocator; - try self.misc_errors.ensureUnusedCapacity(gpa, 1); + const comp = self.base.comp; + const gpa = comp.gpa; + try comp.link_errors.ensureUnusedCapacity(gpa, 1); var notes = try gpa.alloc(File.ErrorMsg, 1); errdefer gpa.free(notes); notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "while resolving {s}", .{sym_name}) }; - self.misc_errors.appendAssumeCapacity(.{ + comp.link_errors.appendAssumeCapacity(.{ .msg = try std.fmt.allocPrint(gpa, format, args), .notes = notes, }); } pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void { - const gpa = self.base.allocator; + const comp = self.base.comp; + const gpa = comp.gpa; const count = self.unresolved.count(); - try self.misc_errors.ensureUnusedCapacity(gpa, count); + try comp.link_errors.ensureUnusedCapacity(gpa, count); for (self.unresolved.keys()) |global_index| { const global = self.globals.items[global_index]; @@ -5318,7 +5350,7 @@ pub fn reportUndefined(self: *MachO) error{OutOfMemory}!void { }; err_msg.notes = try notes.toOwnedSlice(); - self.misc_errors.appendAssumeCapacity(err_msg); + comp.link_errors.appendAssumeCapacity(err_msg); } } @@ -5327,8 +5359,9 @@ fn reportSymbolCollision( first: SymbolWithLoc, other: SymbolWithLoc, ) error{OutOfMemory}!void { - const gpa = self.base.allocator; - try self.misc_errors.ensureUnusedCapacity(gpa, 1); + const comp = self.base.comp; + const gpa = comp.gpa; + try comp.link_errors.ensureUnusedCapacity(gpa, 1); var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 2); defer notes.deinit(); @@ -5351,12 +5384,13 @@ fn reportSymbolCollision( }) }; err_msg.notes = try notes.toOwnedSlice(); - self.misc_errors.appendAssumeCapacity(err_msg); + comp.link_errors.appendAssumeCapacity(err_msg); } fn reportUnhandledSymbolType(self: *MachO, sym_with_loc: SymbolWithLoc) error{OutOfMemory}!void { - const gpa = self.base.allocator; - try self.misc_errors.ensureUnusedCapacity(gpa, 1); + const comp = self.base.comp; + const gpa = comp.gpa; + try comp.link_errors.ensureUnusedCapacity(gpa, 1); const notes = try gpa.alloc(File.ErrorMsg, 1); errdefer gpa.free(notes); @@ -5374,7 +5408,7 @@ fn reportUnhandledSymbolType(self: *MachO, sym_with_loc: SymbolWithLoc) error{Ou else unreachable; - self.misc_errors.appendAssumeCapacity(.{ + comp.link_errors.appendAssumeCapacity(.{ .msg = try std.fmt.allocPrint(gpa, "unhandled symbol type: '{s}' has type {s}", .{ self.getSymbolName(sym_with_loc), sym_type, @@ -5623,6 +5657,8 @@ pub fn logAtom(self: *MachO, atom_index: Atom.Index, logger: anytype) void { } } +const default_entry_symbol_name = "_main"; + pub const base_tag: File.Tag = File.Tag.macho; pub const N_DEAD: u16 = @as(u16, @bitCast(@as(i16, -1))); pub const N_BOUNDARY: u16 = @as(u16, @bitCast(@as(i16, -2))); diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig index 290c67c45e2e..d76a6de841ce 100644 --- a/src/link/MachO/Atom.zig +++ b/src/link/MachO/Atom.zig @@ -253,7 +253,8 @@ pub fn addRelocation(macho_file: *MachO, atom_index: Index, reloc: Relocation) ! } pub fn addRelocations(macho_file: *MachO, atom_index: Index, relocs: []const Relocation) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const gop = try macho_file.relocs.getOrPut(gpa, atom_index); if (!gop.found_existing) { gop.value_ptr.* = .{}; @@ -269,7 +270,8 @@ pub fn addRelocations(macho_file: *MachO, atom_index: Index, relocs: []const Rel } pub fn addRebase(macho_file: *MachO, atom_index: Index, offset: u32) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const atom = macho_file.getAtom(atom_index); log.debug(" (adding rebase at offset 0x{x} in %{?d})", .{ offset, atom.getSymbolIndex() }); const gop = try macho_file.rebases.getOrPut(gpa, atom_index); @@ -280,7 +282,8 @@ pub fn addRebase(macho_file: *MachO, atom_index: Index, offset: u32) !void { } pub fn addBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const atom = macho_file.getAtom(atom_index); log.debug(" (adding binding to symbol {s} at offset 0x{x} in %{?d})", .{ macho_file.getSymbolName(binding.target), @@ -307,7 +310,8 @@ pub fn resolveRelocations( } pub fn freeRelocations(macho_file: *MachO, atom_index: Index) void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; var removed_relocs = macho_file.relocs.fetchOrderedRemove(atom_index); if (removed_relocs) |*relocs| relocs.value.deinit(gpa); var removed_rebases = macho_file.rebases.fetchOrderedRemove(atom_index); @@ -387,7 +391,8 @@ pub fn calcInnerSymbolOffset(macho_file: *MachO, atom_index: Index, sym_index: u } pub fn scanAtomRelocs(macho_file: *MachO, atom_index: Index, relocs: []align(1) const macho.relocation_info) !void { - const arch = macho_file.base.options.target.cpu.arch; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const arch = target.cpu.arch; const atom = macho_file.getAtom(atom_index); assert(atom.getFile() != null); // synthetic atoms do not have relocs @@ -434,6 +439,7 @@ pub fn parseRelocTarget(macho_file: *MachO, ctx: struct { const tracy = trace(@src()); defer tracy.end(); + const target = macho_file.base.comp.root_mod.resolved_target.result; const object = &macho_file.objects.items[ctx.object_id]; log.debug("parsing reloc target in object({d}) '{s}' ", .{ ctx.object_id, object.name }); @@ -447,7 +453,7 @@ pub fn parseRelocTarget(macho_file: *MachO, ctx: struct { else mem.readInt(u32, ctx.code[rel_offset..][0..4], .little); } else blk: { - assert(macho_file.base.options.target.cpu.arch == .x86_64); + assert(target.cpu.arch == .x86_64); const correction: u3 = switch (@as(macho.reloc_type_x86_64, @enumFromInt(ctx.rel.r_type))) { .X86_64_RELOC_SIGNED => 0, .X86_64_RELOC_SIGNED_1 => 1, @@ -467,18 +473,18 @@ pub fn parseRelocTarget(macho_file: *MachO, ctx: struct { const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = ctx.object_id + 1 }; const sym = macho_file.getSymbol(sym_loc); - const target = if (sym.sect() and !sym.ext()) + const reloc_target = if (sym.sect() and !sym.ext()) sym_loc else if (object.getGlobal(sym_index)) |global_index| macho_file.globals.items[global_index] else sym_loc; log.debug(" | target %{d} ('{s}') in object({?d})", .{ - target.sym_index, - macho_file.getSymbolName(target), - target.getFile(), + reloc_target.sym_index, + macho_file.getSymbolName(reloc_target), + reloc_target.getFile(), }); - return target; + return reloc_target; } pub fn getRelocTargetAtomIndex(macho_file: *MachO, target: SymbolWithLoc) ?Index { @@ -599,7 +605,8 @@ pub fn resolveRelocs( atom_code: []u8, atom_relocs: []align(1) const macho.relocation_info, ) !void { - const arch = macho_file.base.options.target.cpu.arch; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const arch = target.cpu.arch; const atom = macho_file.getAtom(atom_index); assert(atom.getFile() != null); // synthetic atoms do not have relocs @@ -1192,7 +1199,8 @@ pub fn getAtomRelocs(macho_file: *MachO, atom_index: Index) []const macho.reloca } pub fn relocRequiresGot(macho_file: *MachO, rel: macho.relocation_info) bool { - switch (macho_file.base.options.target.cpu.arch) { + const target = macho_file.base.comp.root_mod.resolved_target.result; + switch (target.cpu.arch) { .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) { .ARM64_RELOC_GOT_LOAD_PAGE21, .ARM64_RELOC_GOT_LOAD_PAGEOFF12, @@ -1211,7 +1219,8 @@ pub fn relocRequiresGot(macho_file: *MachO, rel: macho.relocation_info) bool { } pub fn relocIsTlv(macho_file: *MachO, rel: macho.relocation_info) bool { - switch (macho_file.base.options.target.cpu.arch) { + const target = macho_file.base.comp.root_mod.resolved_target.result; + switch (target.cpu.arch) { .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) { .ARM64_RELOC_TLVP_LOAD_PAGE21, .ARM64_RELOC_TLVP_LOAD_PAGEOFF12, @@ -1227,7 +1236,8 @@ pub fn relocIsTlv(macho_file: *MachO, rel: macho.relocation_info) bool { } pub fn relocIsStub(macho_file: *MachO, rel: macho.relocation_info) bool { - switch (macho_file.base.options.target.cpu.arch) { + const target = macho_file.base.comp.root_mod.resolved_target.result; + switch (target.cpu.arch) { .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) { .ARM64_RELOC_BRANCH26 => return true, else => return false, diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig index f204093290e3..74a4afeb5415 100644 --- a/src/link/MachO/DebugSymbols.zig +++ b/src/link/MachO/DebugSymbols.zig @@ -39,10 +39,12 @@ pub const Reloc = struct { /// You must call this function *after* `MachO.populateMissingMetadata()` /// has been called to get a viable debug symbols output. pub fn populateMissingMetadata(self: *DebugSymbols, macho_file: *MachO) !void { + const target = macho_file.base.comp.root_mod.resolved_target.result; + if (self.dwarf_segment_cmd_index == null) { self.dwarf_segment_cmd_index = @as(u8, @intCast(self.segments.items.len)); - const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch); + const page_size = MachO.getPageSize(target.cpu.arch); const off = @as(u64, @intCast(page_size)); const ideal_size: u16 = 200 + 128 + 160 + 250; const needed_size = mem.alignForward(u64, padToIdeal(ideal_size), page_size); @@ -196,10 +198,10 @@ fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 } pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { + const comp = macho_file.base.comp; // TODO This linker code currently assumes there is only 1 compilation unit // and it corresponds to the Zig source code. - const options = macho_file.base.options; - const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + const zcu = comp.module orelse return error.LinkingWithoutZigSourceUnimplemented; for (self.relocs.items) |*reloc| { const sym = switch (reloc.type) { @@ -243,7 +245,7 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { const text_section = macho_file.sections.items(.header)[macho_file.text_section_index.?]; const low_pc = text_section.addr; const high_pc = text_section.addr + text_section.size; - try self.dwarf.writeDbgInfoHeader(module, low_pc, high_pc); + try self.dwarf.writeDbgInfoHeader(zcu, low_pc, high_pc); self.debug_info_header_dirty = false; } @@ -332,7 +334,8 @@ fn finalizeDwarfSegment(self: *DebugSymbols, macho_file: *MachO) void { file_size = @max(file_size, header.offset + header.size); } - const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch); + const target = macho_file.base.comp.root_mod.resolved_target.result; + const page_size = MachO.getPageSize(target.cpu.arch); const aligned_size = mem.alignForward(u64, file_size, page_size); dwarf_segment.vmaddr = base_vmaddr; dwarf_segment.filesize = aligned_size; @@ -394,10 +397,12 @@ fn writeSegmentHeaders(self: *DebugSymbols, macho_file: *MachO, writer: anytype) } fn writeHeader(self: *DebugSymbols, macho_file: *MachO, ncmds: u32, sizeofcmds: u32) !void { + const target = macho_file.base.comp.root_mod.resolved_target.result; + var header: macho.mach_header_64 = .{}; header.filetype = macho.MH_DSYM; - switch (macho_file.base.options.target.cpu.arch) { + switch (target.cpu.arch) { .aarch64 => { header.cputype = macho.CPU_TYPE_ARM64; header.cpusubtype = macho.CPU_SUBTYPE_ARM_ALL; @@ -435,7 +440,8 @@ fn writeLinkeditSegmentData(self: *DebugSymbols, macho_file: *MachO) !void { try self.writeSymtab(macho_file); try self.writeStrtab(); - const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch); + const target = macho_file.base.comp.root_mod.resolved_target.result; + const page_size = MachO.getPageSize(target.cpu.arch); const seg = &self.segments.items[self.linkedit_segment_cmd_index.?]; const aligned_size = mem.alignForward(u64, seg.filesize, page_size); seg.vmsize = aligned_size; @@ -566,6 +572,5 @@ const trace = @import("../../tracy.zig").trace; const Allocator = mem.Allocator; const Dwarf = @import("../Dwarf.zig"); const MachO = @import("../MachO.zig"); -const Module = @import("../../Module.zig"); const StringTable = @import("../StringTable.zig"); const Type = @import("../../type.zig").Type; diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 0c68d706446f..ad069b845e4d 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -342,19 +342,23 @@ pub const SplitIntoAtomsError = error{ }; pub fn splitIntoAtoms(self: *Object, macho_file: *MachO, object_id: u32) SplitIntoAtomsError!void { + const comp = macho_file.base.comp; + const gpa = comp.gpa; log.debug("splitting object({d}, {s}) into atoms", .{ object_id, self.name }); try self.splitRegularSections(macho_file, object_id); try self.parseEhFrameSection(macho_file, object_id); try self.parseUnwindInfo(macho_file, object_id); - try self.parseDataInCode(macho_file.base.allocator); + try self.parseDataInCode(gpa); } /// Splits input regular sections into Atoms. /// If the Object was compiled with `MH_SUBSECTIONS_VIA_SYMBOLS`, splits section /// into subsections where each subsection then represents an Atom. pub fn splitRegularSections(self: *Object, macho_file: *MachO, object_id: u32) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; + const target = macho_file.base.comp.root_mod.resolved_target.result; const sections = self.getSourceSections(); for (sections, 0..) |sect, id| { @@ -448,7 +452,7 @@ pub fn splitRegularSections(self: *Object, macho_file: *MachO, object_id: u32) ! try self.parseRelocs(gpa, section.id); - const cpu_arch = macho_file.base.options.target.cpu.arch; + const cpu_arch = target.cpu.arch; const sect_loc = filterSymbolsBySection(symtab[sect_sym_index..], sect_id + 1); const sect_start_index = sect_sym_index + sect_loc.index; @@ -554,7 +558,8 @@ fn createAtomFromSubsection( alignment: Alignment, out_sect_id: u8, ) !Atom.Index { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const atom_index = try macho_file.createAtom(sym_index, .{ .size = size, .alignment = alignment, @@ -670,13 +675,15 @@ fn parseEhFrameSection(self: *Object, macho_file: *MachO, object_id: u32) !void log.debug("parsing __TEXT,__eh_frame section", .{}); - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; if (macho_file.eh_frame_section_index == null) { macho_file.eh_frame_section_index = try macho_file.initSection("__TEXT", "__eh_frame", .{}); } - const cpu_arch = macho_file.base.options.target.cpu.arch; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; try self.parseRelocs(gpa, sect_id); const relocs = self.getRelocs(sect_id); @@ -704,7 +711,7 @@ fn parseEhFrameSection(self: *Object, macho_file: *MachO, object_id: u32) !void }); if (record.tag == .fde) { - const target = blk: { + const reloc_target = blk: { switch (cpu_arch) { .aarch64 => { assert(rel_pos.len > 0); // TODO convert to an error as the FDE eh frame is malformed @@ -714,13 +721,13 @@ fn parseEhFrameSection(self: *Object, macho_file: *MachO, object_id: u32) !void @as(macho.reloc_type_arm64, @enumFromInt(rel.r_type)) == .ARM64_RELOC_UNSIGNED) break rel; } else unreachable; - const target = Atom.parseRelocTarget(macho_file, .{ + const reloc_target = Atom.parseRelocTarget(macho_file, .{ .object_id = object_id, .rel = rel, .code = it.data[offset..], .base_offset = @as(i32, @intCast(offset)), }); - break :blk target; + break :blk reloc_target; }, .x86_64 => { const target_address = record.getTargetSymbolAddress(.{ @@ -728,16 +735,16 @@ fn parseEhFrameSection(self: *Object, macho_file: *MachO, object_id: u32) !void .base_offset = offset, }); const target_sym_index = self.getSymbolByAddress(target_address, null); - const target = if (self.getGlobal(target_sym_index)) |global_index| + const reloc_target = if (self.getGlobal(target_sym_index)) |global_index| macho_file.globals.items[global_index] else SymbolWithLoc{ .sym_index = target_sym_index, .file = object_id + 1 }; - break :blk target; + break :blk reloc_target; }, else => unreachable, } }; - if (target.getFile() != object_id) { + if (reloc_target.getFile() != object_id) { log.debug("FDE at offset {x} marked DEAD", .{offset}); self.eh_frame_relocs_lookup.getPtr(offset).?.dead = true; } else { @@ -746,12 +753,12 @@ fn parseEhFrameSection(self: *Object, macho_file: *MachO, object_id: u32) !void // very problematic when using Zig's @export feature to re-export symbols under // additional names. For that reason, we need to ensure we record aliases here // too so that we can tie them with their matching unwind records and vice versa. - const aliases = self.getSymbolAliases(target.sym_index); + const aliases = self.getSymbolAliases(reloc_target.sym_index); var i: u32 = 0; while (i < aliases.len) : (i += 1) { const actual_target = SymbolWithLoc{ .sym_index = i + aliases.start, - .file = target.file, + .file = reloc_target.file, }; log.debug("FDE at offset {x} tracks {s}", .{ offset, @@ -765,8 +772,10 @@ fn parseEhFrameSection(self: *Object, macho_file: *MachO, object_id: u32) !void } fn parseUnwindInfo(self: *Object, macho_file: *MachO, object_id: u32) !void { - const gpa = macho_file.base.allocator; - const cpu_arch = macho_file.base.options.target.cpu.arch; + const comp = macho_file.base.comp; + const gpa = comp.gpa; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; const sect_id = self.unwind_info_sect_id orelse { // If it so happens that the object had `__eh_frame` section defined but no `__compact_unwind`, // we will try fully synthesising unwind info records to somewhat match Apple ld's @@ -818,13 +827,13 @@ fn parseUnwindInfo(self: *Object, macho_file: *MachO, object_id: u32) !void { // Find function symbol that this record describes const rel = relocs[rel_pos.start..][rel_pos.len - 1]; - const target = Atom.parseRelocTarget(macho_file, .{ + const reloc_target = Atom.parseRelocTarget(macho_file, .{ .object_id = object_id, .rel = rel, .code = mem.asBytes(&record), .base_offset = @as(i32, @intCast(offset)), }); - if (target.getFile() != object_id) { + if (reloc_target.getFile() != object_id) { log.debug("unwind record {d} marked DEAD", .{record_id}); self.unwind_relocs_lookup[record_id].dead = true; } else { @@ -833,12 +842,12 @@ fn parseUnwindInfo(self: *Object, macho_file: *MachO, object_id: u32) !void { // very problematic when using Zig's @export feature to re-export symbols under // additional names. For that reason, we need to ensure we record aliases here // too so that we can tie them with their matching unwind records and vice versa. - const aliases = self.getSymbolAliases(target.sym_index); + const aliases = self.getSymbolAliases(reloc_target.sym_index); var i: u32 = 0; while (i < aliases.len) : (i += 1) { const actual_target = SymbolWithLoc{ .sym_index = i + aliases.start, - .file = target.file, + .file = reloc_target.file, }; log.debug("unwind record {d} tracks {s}", .{ record_id, diff --git a/src/link/MachO/Relocation.zig b/src/link/MachO/Relocation.zig index 74be9eb0d561..85c19c7608f3 100644 --- a/src/link/MachO/Relocation.zig +++ b/src/link/MachO/Relocation.zig @@ -58,11 +58,12 @@ pub fn isStubTrampoline(self: Relocation, macho_file: *MachO) bool { } pub fn getTargetBaseAddress(self: Relocation, macho_file: *MachO) ?u64 { + const target = macho_file.base.comp.root_mod.resolved_target.result; if (self.isStubTrampoline(macho_file)) { const index = macho_file.stub_table.lookup.get(self.target) orelse return null; const header = macho_file.sections.items(.header)[macho_file.stubs_section_index.?]; return header.addr + - index * @import("stubs.zig").stubSize(macho_file.base.options.target.cpu.arch); + index * @import("stubs.zig").stubSize(target.cpu.arch); } switch (self.type) { .got, .got_page, .got_pageoff => { @@ -84,7 +85,8 @@ pub fn getTargetBaseAddress(self: Relocation, macho_file: *MachO) ?u64 { } pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, code: []u8) void { - const arch = macho_file.base.options.target.cpu.arch; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const arch = target.cpu.arch; const atom = macho_file.getAtom(atom_index); const source_sym = atom.getSymbol(macho_file); const source_addr = source_sym.n_value + self.offset; diff --git a/src/link/MachO/UnwindInfo.zig b/src/link/MachO/UnwindInfo.zig index be6c9dbb3453..7223b5555f7e 100644 --- a/src/link/MachO/UnwindInfo.zig +++ b/src/link/MachO/UnwindInfo.zig @@ -184,7 +184,8 @@ pub fn deinit(info: *UnwindInfo) void { pub fn scanRelocs(macho_file: *MachO) !void { if (macho_file.unwind_info_section_index == null) return; - const cpu_arch = macho_file.base.options.target.cpu.arch; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; for (macho_file.objects.items, 0..) |*object, object_id| { const unwind_records = object.getUnwindRecords(); for (object.exec_atoms.items) |atom_index| { @@ -196,13 +197,13 @@ pub fn scanRelocs(macho_file: *MachO) !void { if (!UnwindEncoding.isDwarf(record.compactUnwindEncoding, cpu_arch)) { if (getPersonalityFunctionReloc(macho_file, @as(u32, @intCast(object_id)), record_id)) |rel| { // Personality function; add GOT pointer. - const target = Atom.parseRelocTarget(macho_file, .{ + const reloc_target = Atom.parseRelocTarget(macho_file, .{ .object_id = @as(u32, @intCast(object_id)), .rel = rel, .code = mem.asBytes(&record), .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), }); - try macho_file.addGotEntry(target); + try macho_file.addGotEntry(reloc_target); } } } @@ -213,7 +214,8 @@ pub fn scanRelocs(macho_file: *MachO) !void { pub fn collect(info: *UnwindInfo, macho_file: *MachO) !void { if (macho_file.unwind_info_section_index == null) return; - const cpu_arch = macho_file.base.options.target.cpu.arch; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; var records = std.ArrayList(macho.compact_unwind_entry).init(info.gpa); defer records.deinit(); @@ -247,15 +249,15 @@ pub fn collect(info: *UnwindInfo, macho_file: *MachO) !void { @as(u32, @intCast(object_id)), record_id, )) |rel| { - const target = Atom.parseRelocTarget(macho_file, .{ + const reloc_target = Atom.parseRelocTarget(macho_file, .{ .object_id = @as(u32, @intCast(object_id)), .rel = rel, .code = mem.asBytes(&record), .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), }); - const personality_index = info.getPersonalityFunction(target) orelse inner: { + const personality_index = info.getPersonalityFunction(reloc_target) orelse inner: { const personality_index = info.personalities_count; - info.personalities[personality_index] = target; + info.personalities[personality_index] = reloc_target; info.personalities_count += 1; break :inner personality_index; }; @@ -265,13 +267,13 @@ pub fn collect(info: *UnwindInfo, macho_file: *MachO) !void { } if (getLsdaReloc(macho_file, @as(u32, @intCast(object_id)), record_id)) |rel| { - const target = Atom.parseRelocTarget(macho_file, .{ + const reloc_target = Atom.parseRelocTarget(macho_file, .{ .object_id = @as(u32, @intCast(object_id)), .rel = rel, .code = mem.asBytes(&record), .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), }); - record.lsda = @as(u64, @bitCast(target)); + record.lsda = @as(u64, @bitCast(reloc_target)); } } break :blk record; @@ -557,13 +559,14 @@ pub fn write(info: *UnwindInfo, macho_file: *MachO) !void { const text_sect = macho_file.sections.items(.header)[text_sect_id]; var personalities: [max_personalities]u32 = undefined; - const cpu_arch = macho_file.base.options.target.cpu.arch; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; log.debug("Personalities:", .{}); - for (info.personalities[0..info.personalities_count], 0..) |target, i| { - const addr = macho_file.getGotEntryAddress(target).?; + for (info.personalities[0..info.personalities_count], 0..) |reloc_target, i| { + const addr = macho_file.getGotEntryAddress(reloc_target).?; personalities[i] = @as(u32, @intCast(addr - seg.vmaddr)); - log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], macho_file.getSymbolName(target) }); + log.debug(" {d}: 0x{x} ({s})", .{ i, personalities[i], macho_file.getSymbolName(reloc_target) }); } for (info.records.items) |*rec| { diff --git a/src/link/MachO/dead_strip.zig b/src/link/MachO/dead_strip.zig index dda82385e185..fe3740e82667 100644 --- a/src/link/MachO/dead_strip.zig +++ b/src/link/MachO/dead_strip.zig @@ -1,7 +1,8 @@ //! An algorithm for dead stripping of unreferenced Atoms. pub fn gcAtoms(macho_file: *MachO) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; var arena = std.heap.ArenaAllocator.init(gpa); defer arena.deinit(); @@ -33,7 +34,9 @@ fn addRoot(macho_file: *MachO, roots: *AtomTable, file: u32, sym_loc: SymbolWith fn collectRoots(macho_file: *MachO, roots: *AtomTable) !void { log.debug("collecting roots", .{}); - switch (macho_file.base.options.output_mode) { + const comp = macho_file.base.comp; + + switch (comp.config.output_mode) { .Exe => { // Add entrypoint as GC root if (macho_file.getEntryPoint()) |global| { @@ -60,7 +63,7 @@ fn collectRoots(macho_file: *MachO, roots: *AtomTable) !void { } // Add all symbols force-defined by the user. - for (macho_file.base.options.force_undefined_symbols.keys()) |sym_name| { + for (comp.force_undefined_symbols.keys()) |sym_name| { const global_index = macho_file.resolver.get(sym_name).?; const global = macho_file.globals.items[global_index]; const sym = macho_file.getSymbol(global); @@ -118,7 +121,8 @@ fn markLive(macho_file: *MachO, atom_index: Atom.Index, alive: *AtomTable) void alive.putAssumeCapacityNoClobber(atom_index, {}); - const cpu_arch = macho_file.base.options.target.cpu.arch; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; const sym = macho_file.getSymbol(atom.getSymbolWithLoc()); const header = macho_file.sections.items(.header)[sym.n_sect - 1]; @@ -129,7 +133,7 @@ fn markLive(macho_file: *MachO, atom_index: Atom.Index, alive: *AtomTable) void const ctx = Atom.getRelocContext(macho_file, atom_index); for (relocs) |rel| { - const target = switch (cpu_arch) { + const reloc_target = switch (cpu_arch) { .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) { .ARM64_RELOC_ADDEND => continue, else => Atom.parseRelocTarget(macho_file, .{ @@ -149,19 +153,19 @@ fn markLive(macho_file: *MachO, atom_index: Atom.Index, alive: *AtomTable) void }), else => unreachable, }; - const target_sym = macho_file.getSymbol(target); + const target_sym = macho_file.getSymbol(reloc_target); if (target_sym.undf()) continue; - if (target.getFile() == null) { - const target_sym_name = macho_file.getSymbolName(target); + if (reloc_target.getFile() == null) { + const target_sym_name = macho_file.getSymbolName(reloc_target); if (mem.eql(u8, "__mh_execute_header", target_sym_name)) continue; if (mem.eql(u8, "___dso_handle", target_sym_name)) continue; unreachable; // referenced symbol not found } - const object = macho_file.objects.items[target.getFile().?]; - const target_atom_index = object.getAtomIndexForSymbol(target.sym_index).?; + const object = macho_file.objects.items[reloc_target.getFile().?]; + const target_atom_index = object.getAtomIndexForSymbol(reloc_target.sym_index).?; log.debug(" following ATOM({d}, %{d}, {?d})", .{ target_atom_index, macho_file.getAtom(target_atom_index).sym_index, @@ -178,7 +182,8 @@ fn refersLive(macho_file: *MachO, atom_index: Atom.Index, alive: AtomTable) bool log.debug("refersLive(ATOM({d}, %{d}, {?d}))", .{ atom_index, sym_loc.sym_index, sym_loc.getFile() }); - const cpu_arch = macho_file.base.options.target.cpu.arch; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; const sym = macho_file.getSymbol(sym_loc); const header = macho_file.sections.items(.header)[sym.n_sect - 1]; @@ -189,7 +194,7 @@ fn refersLive(macho_file: *MachO, atom_index: Atom.Index, alive: AtomTable) bool const ctx = Atom.getRelocContext(macho_file, atom_index); for (relocs) |rel| { - const target = switch (cpu_arch) { + const reloc_target = switch (cpu_arch) { .aarch64 => switch (@as(macho.reloc_type_arm64, @enumFromInt(rel.r_type))) { .ARM64_RELOC_ADDEND => continue, else => Atom.parseRelocTarget(macho_file, .{ @@ -210,9 +215,9 @@ fn refersLive(macho_file: *MachO, atom_index: Atom.Index, alive: AtomTable) bool else => unreachable, }; - const object = macho_file.objects.items[target.getFile().?]; - const target_atom_index = object.getAtomIndexForSymbol(target.sym_index) orelse { - log.debug("atom for symbol '{s}' not found; skipping...", .{macho_file.getSymbolName(target)}); + const object = macho_file.objects.items[reloc_target.getFile().?]; + const target_atom_index = object.getAtomIndexForSymbol(reloc_target.sym_index) orelse { + log.debug("atom for symbol '{s}' not found; skipping...", .{macho_file.getSymbolName(reloc_target)}); continue; }; if (alive.contains(target_atom_index)) { @@ -271,7 +276,8 @@ fn mark(macho_file: *MachO, roots: AtomTable, alive: *AtomTable) void { fn markUnwindRecords(macho_file: *MachO, object_id: u32, alive: *AtomTable) void { const object = &macho_file.objects.items[object_id]; - const cpu_arch = macho_file.base.options.target.cpu.arch; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; const unwind_records = object.getUnwindRecords(); @@ -310,29 +316,29 @@ fn markUnwindRecords(macho_file: *MachO, object_id: u32, alive: *AtomTable) void markEhFrameRecords(macho_file, object_id, atom_index, alive); } else { if (UnwindInfo.getPersonalityFunctionReloc(macho_file, object_id, record_id)) |rel| { - const target = Atom.parseRelocTarget(macho_file, .{ + const reloc_target = Atom.parseRelocTarget(macho_file, .{ .object_id = object_id, .rel = rel, .code = mem.asBytes(&record), .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), }); - const target_sym = macho_file.getSymbol(target); + const target_sym = macho_file.getSymbol(reloc_target); if (!target_sym.undf()) { - const target_object = macho_file.objects.items[target.getFile().?]; - const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?; + const target_object = macho_file.objects.items[reloc_target.getFile().?]; + const target_atom_index = target_object.getAtomIndexForSymbol(reloc_target.sym_index).?; markLive(macho_file, target_atom_index, alive); } } if (UnwindInfo.getLsdaReloc(macho_file, object_id, record_id)) |rel| { - const target = Atom.parseRelocTarget(macho_file, .{ + const reloc_target = Atom.parseRelocTarget(macho_file, .{ .object_id = object_id, .rel = rel, .code = mem.asBytes(&record), .base_offset = @as(i32, @intCast(record_id * @sizeOf(macho.compact_unwind_entry))), }); - const target_object = macho_file.objects.items[target.getFile().?]; - const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?; + const target_object = macho_file.objects.items[reloc_target.getFile().?]; + const target_atom_index = target_object.getAtomIndexForSymbol(reloc_target.sym_index).?; markLive(macho_file, target_atom_index, alive); } } @@ -341,7 +347,8 @@ fn markUnwindRecords(macho_file: *MachO, object_id: u32, alive: *AtomTable) void } fn markEhFrameRecords(macho_file: *MachO, object_id: u32, atom_index: Atom.Index, alive: *AtomTable) void { - const cpu_arch = macho_file.base.options.target.cpu.arch; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; const object = &macho_file.objects.items[object_id]; var it = object.getEhFrameRecordsIterator(); var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index); @@ -361,16 +368,16 @@ fn markEhFrameRecords(macho_file: *MachO, object_id: u32, atom_index: Atom.Index // Mark FDE references which should include any referenced LSDA record const relocs = eh_frame.getRelocs(macho_file, object_id, fde_offset); for (relocs) |rel| { - const target = Atom.parseRelocTarget(macho_file, .{ + const reloc_target = Atom.parseRelocTarget(macho_file, .{ .object_id = object_id, .rel = rel, .code = fde.data, .base_offset = @as(i32, @intCast(fde_offset)) + 4, }); - const target_sym = macho_file.getSymbol(target); + const target_sym = macho_file.getSymbol(reloc_target); if (!target_sym.undf()) blk: { - const target_object = macho_file.objects.items[target.getFile().?]; - const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index) orelse + const target_object = macho_file.objects.items[reloc_target.getFile().?]; + const target_atom_index = target_object.getAtomIndexForSymbol(reloc_target.sym_index) orelse break :blk; markLive(macho_file, target_atom_index, alive); } @@ -394,11 +401,11 @@ fn markEhFrameRecords(macho_file: *MachO, object_id: u32, atom_index: Atom.Index // Mark CIE references which should include any referenced personalities // that are defined locally. - if (cie.getPersonalityPointerReloc(macho_file, object_id, cie_offset)) |target| { - const target_sym = macho_file.getSymbol(target); + if (cie.getPersonalityPointerReloc(macho_file, object_id, cie_offset)) |reloc_target| { + const target_sym = macho_file.getSymbol(reloc_target); if (!target_sym.undf()) { - const target_object = macho_file.objects.items[target.getFile().?]; - const target_atom_index = target_object.getAtomIndexForSymbol(target.sym_index).?; + const target_object = macho_file.objects.items[reloc_target.getFile().?]; + const target_atom_index = target_object.getAtomIndexForSymbol(reloc_target.sym_index).?; markLive(macho_file, target_atom_index, alive); } } diff --git a/src/link/MachO/eh_frame.zig b/src/link/MachO/eh_frame.zig index 0f021a569cc1..4b51d09683d5 100644 --- a/src/link/MachO/eh_frame.zig +++ b/src/link/MachO/eh_frame.zig @@ -1,5 +1,6 @@ pub fn scanRelocs(macho_file: *MachO) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; for (macho_file.objects.items, 0..) |*object, object_id| { var cies = std.AutoHashMap(u32, void).init(gpa); @@ -35,8 +36,10 @@ pub fn calcSectionSize(macho_file: *MachO, unwind_info: *const UnwindInfo) error sect.@"align" = 3; sect.size = 0; - const cpu_arch = macho_file.base.options.target.cpu.arch; - const gpa = macho_file.base.allocator; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; + const comp = macho_file.base.comp; + const gpa = comp.gpa; var size: u32 = 0; for (macho_file.objects.items, 0..) |*object, object_id| { @@ -86,8 +89,10 @@ pub fn write(macho_file: *MachO, unwind_info: *UnwindInfo) !void { const seg_id = macho_file.sections.items(.segment_index)[sect_id]; const seg = macho_file.segments.items[seg_id]; - const cpu_arch = macho_file.base.options.target.cpu.arch; - const gpa = macho_file.base.allocator; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; + const comp = macho_file.base.comp; + const gpa = comp.gpa; var eh_records = std.AutoArrayHashMap(u32, EhFrameRecord(true)).init(gpa); defer { @@ -109,11 +114,11 @@ pub fn write(macho_file: *MachO, unwind_info: *UnwindInfo) !void { for (object.exec_atoms.items) |atom_index| { var inner_syms_it = Atom.getInnerSymbolsIterator(macho_file, atom_index); - while (inner_syms_it.next()) |target| { - const fde_record_offset = object.eh_frame_records_lookup.get(target) orelse continue; + while (inner_syms_it.next()) |reloc_target| { + const fde_record_offset = object.eh_frame_records_lookup.get(reloc_target) orelse continue; if (object.eh_frame_relocs_lookup.get(fde_record_offset).?.dead) continue; - const record_id = unwind_info.records_lookup.get(target) orelse continue; + const record_id = unwind_info.records_lookup.get(reloc_target) orelse continue; const record = &unwind_info.records.items[record_id]; // TODO skip this check if no __compact_unwind is present @@ -153,7 +158,7 @@ pub fn write(macho_file: *MachO, unwind_info: *UnwindInfo) !void { .aarch64 => {}, // relocs take care of LSDA pointers .x86_64 => { // We need to relocate target symbol address ourselves. - const atom_sym = macho_file.getSymbol(target); + const atom_sym = macho_file.getSymbol(reloc_target); try fde_record.setTargetSymbolAddress(atom_sym.n_value, .{ .base_addr = sect.addr, .base_offset = eh_frame_offset, @@ -278,7 +283,8 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { object_id: u32, source_offset: u32, ) ?SymbolWithLoc { - const cpu_arch = macho_file.base.options.target.cpu.arch; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; const relocs = getRelocs(macho_file, object_id, source_offset); for (relocs) |rel| { switch (cpu_arch) { @@ -301,13 +307,13 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { }, else => unreachable, } - const target = Atom.parseRelocTarget(macho_file, .{ + const reloc_target = Atom.parseRelocTarget(macho_file, .{ .object_id = object_id, .rel = rel, .code = rec.data, .base_offset = @as(i32, @intCast(source_offset)) + 4, }); - return target; + return reloc_target; } return null; } @@ -319,11 +325,12 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { }) !void { comptime assert(is_mutable); - const cpu_arch = macho_file.base.options.target.cpu.arch; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; const relocs = getRelocs(macho_file, object_id, ctx.source_offset); for (relocs) |rel| { - const target = Atom.parseRelocTarget(macho_file, .{ + const reloc_target = Atom.parseRelocTarget(macho_file, .{ .object_id = object_id, .rel = rel, .code = rec.data, @@ -340,14 +347,14 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { // Address of the __eh_frame in the source object file }, .ARM64_RELOC_POINTER_TO_GOT => { - const target_addr = macho_file.getGotEntryAddress(target).?; + const target_addr = macho_file.getGotEntryAddress(reloc_target).?; const result = math.cast(i32, @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr))) orelse return error.Overflow; mem.writeInt(i32, rec.data[rel_offset..][0..4], result, .little); }, .ARM64_RELOC_UNSIGNED => { assert(rel.r_extern == 1); - const target_addr = Atom.getRelocTargetAddress(macho_file, target, false); + const target_addr = Atom.getRelocTargetAddress(macho_file, reloc_target, false); const result = @as(i64, @intCast(target_addr)) - @as(i64, @intCast(source_addr)); mem.writeInt(i64, rec.data[rel_offset..][0..8], @as(i64, @intCast(result)), .little); }, @@ -358,7 +365,7 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { const rel_type = @as(macho.reloc_type_x86_64, @enumFromInt(rel.r_type)); switch (rel_type) { .X86_64_RELOC_GOT => { - const target_addr = macho_file.getGotEntryAddress(target).?; + const target_addr = macho_file.getGotEntryAddress(reloc_target).?; const addend = mem.readInt(i32, rec.data[rel_offset..][0..4], .little); const adjusted_target_addr = @as(u64, @intCast(@as(i64, @intCast(target_addr)) + addend)); const disp = try Relocation.calcPcRelativeDisplacementX86(source_addr, adjusted_target_addr, 0); @@ -374,7 +381,8 @@ pub fn EhFrameRecord(comptime is_mutable: bool) type { pub fn getCiePointerSource(rec: Record, object_id: u32, macho_file: *MachO, offset: u32) u32 { assert(rec.tag == .fde); - const cpu_arch = macho_file.base.options.target.cpu.arch; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; const addend = mem.readInt(u32, rec.data[0..4], .little); switch (cpu_arch) { .aarch64 => { diff --git a/src/link/MachO/load_commands.zig b/src/link/MachO/load_commands.zig index f06441573997..e155a7a8ed34 100644 --- a/src/link/MachO/load_commands.zig +++ b/src/link/MachO/load_commands.zig @@ -1,6 +1,3 @@ -/// Default implicit entrypoint symbol name. -pub const default_entry_point: []const u8 = "_main"; - /// Default path to dyld. pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld"; @@ -17,7 +14,9 @@ const CalcLCsSizeCtx = struct { wants_function_starts: bool = true, }; -fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx, assume_max_path_len: bool) !u32 { +fn calcLCsSize(m: *MachO, ctx: CalcLCsSizeCtx, assume_max_path_len: bool) !u32 { + const comp = m.base.comp; + const gpa = comp.gpa; var has_text_segment: bool = false; var sizeofcmds: u64 = 0; for (ctx.segments) |seg| { @@ -46,15 +45,15 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx false, ); // LC_MAIN - if (options.output_mode == .Exe) { + if (comp.config.output_mode == .Exe) { sizeofcmds += @sizeOf(macho.entry_point_command); } // LC_ID_DYLIB - if (options.output_mode == .Lib and options.link_mode == .Dynamic) { + if (comp.config.output_mode == .Lib and comp.config.link_mode == .Dynamic) { sizeofcmds += blk: { - const emit = options.emit.?; - const install_name = options.install_name orelse try emit.directory.join(gpa, &.{emit.sub_path}); - defer if (options.install_name == null) gpa.free(install_name); + const emit = m.base.emit; + const install_name = m.install_name orelse try emit.directory.join(gpa, &.{emit.sub_path}); + defer if (m.install_name == null) gpa.free(install_name); break :blk calcInstallNameLen( @sizeOf(macho.dylib_command), install_name, @@ -64,7 +63,7 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx } // LC_RPATH { - var it = RpathIterator.init(gpa, options.rpath_list); + var it = RpathIterator.init(gpa, m.base.rpath_list); defer it.deinit(); while (try it.next()) |rpath| { sizeofcmds += calcInstallNameLen( @@ -78,7 +77,8 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx sizeofcmds += @sizeOf(macho.source_version_command); // LC_BUILD_VERSION or LC_VERSION_MIN_ or nothing { - const platform = Platform.fromTarget(options.target); + const target = comp.root_mod.resolved_target.result; + const platform = Platform.fromTarget(target); if (platform.isBuildVersionCompatible()) { // LC_BUILD_VERSION sizeofcmds += @sizeOf(macho.build_version_command) + @sizeOf(macho.build_tool_version); @@ -100,19 +100,19 @@ fn calcLCsSize(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx ); } // LC_CODE_SIGNATURE - if (MachO.requiresCodeSignature(options)) { + if (m.requiresCodeSignature()) { sizeofcmds += @sizeOf(macho.linkedit_data_command); } - return @as(u32, @intCast(sizeofcmds)); + return @intCast(sizeofcmds); } -pub fn calcMinHeaderPad(gpa: Allocator, options: *const link.Options, ctx: CalcLCsSizeCtx) !u64 { - var padding: u32 = (try calcLCsSize(gpa, options, ctx, false)) + (options.headerpad_size orelse 0); +pub fn calcMinHeaderPad(m: *MachO, ctx: CalcLCsSizeCtx) !u64 { + var padding: u32 = (try calcLCsSize(m, ctx, false)) + m.headerpad_size; log.debug("minimum requested headerpad size 0x{x}", .{padding + @sizeOf(macho.mach_header_64)}); - if (options.headerpad_max_install_names) { - const min_headerpad_size: u32 = try calcLCsSize(gpa, options, ctx, true); + if (m.headerpad_max_install_names) { + const min_headerpad_size: u32 = try calcLCsSize(m, ctx, true); log.debug("headerpad_max_install_names minimum headerpad size 0x{x}", .{ min_headerpad_size + @sizeOf(macho.mach_header_64), }); @@ -189,17 +189,20 @@ fn writeDylibLC(ctx: WriteDylibLCCtx, lc_writer: anytype) !void { } } -pub fn writeDylibIdLC(gpa: Allocator, options: *const link.Options, lc_writer: anytype) !void { - assert(options.output_mode == .Lib and options.link_mode == .Dynamic); - const emit = options.emit.?; - const install_name = options.install_name orelse try emit.directory.join(gpa, &.{emit.sub_path}); - defer if (options.install_name == null) gpa.free(install_name); - const curr = options.version orelse std.SemanticVersion{ +pub fn writeDylibIdLC(macho_file: *MachO, lc_writer: anytype) !void { + const comp = macho_file.base.comp; + const gpa = comp.gpa; + assert(comp.config.output_mode == .Lib and comp.config.link_mode == .Dynamic); + const emit = macho_file.base.emit; + const install_name = macho_file.install_name orelse + try emit.directory.join(gpa, &.{emit.sub_path}); + defer if (macho_file.install_name == null) gpa.free(install_name); + const curr = comp.version orelse std.SemanticVersion{ .major = 1, .minor = 0, .patch = 0, }; - const compat = options.compatibility_version orelse std.SemanticVersion{ + const compat = macho_file.compatibility_version orelse std.SemanticVersion{ .major = 1, .minor = 0, .patch = 0, @@ -237,8 +240,11 @@ const RpathIterator = struct { } }; -pub fn writeRpathLCs(gpa: Allocator, options: *const link.Options, lc_writer: anytype) !void { - var it = RpathIterator.init(gpa, options.rpath_list); +pub fn writeRpathLCs(macho_file: *MachO, lc_writer: anytype) !void { + const comp = macho_file.base.comp; + const gpa = comp.gpa; + + var it = RpathIterator.init(gpa, macho_file.base.rpath_list); defer it.deinit(); while (try it.next()) |rpath| { @@ -467,16 +473,17 @@ pub inline fn appleVersionToSemanticVersion(version: u32) std.SemanticVersion { }; } -pub fn inferSdkVersion(gpa: Allocator, comp: *const Compilation) ?std.SemanticVersion { +pub fn inferSdkVersion(macho_file: *MachO) ?std.SemanticVersion { + const comp = macho_file.base.comp; + const gpa = comp.gpa; + var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const options = comp.bin_file.options; - - const sdk_layout = options.darwin_sdk_layout orelse return null; + const sdk_layout = macho_file.sdk_layout orelse return null; const sdk_dir = switch (sdk_layout) { - .sdk => options.sysroot.?, + .sdk => comp.sysroot.?, .vendored => std.fs.path.join(arena, &.{ comp.zig_lib_directory.path.?, "libc", "darwin" }) catch return null, }; if (readSdkVersionFromSettings(arena, sdk_dir)) |ver| { diff --git a/src/link/MachO/thunks.zig b/src/link/MachO/thunks.zig index 774a8f73449c..f080de7f80df 100644 --- a/src/link/MachO/thunks.zig +++ b/src/link/MachO/thunks.zig @@ -68,7 +68,8 @@ pub fn createThunks(macho_file: *MachO, sect_id: u8) !void { const header = &macho_file.sections.items(.header)[sect_id]; if (header.size == 0) return; - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const first_atom_index = macho_file.sections.items(.first_atom_index)[sect_id].?; header.size = 0; @@ -245,7 +246,8 @@ fn scanRelocs( macho_file.getSymbol(target).n_value, }); - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const target_sym = macho_file.getSymbol(target); const thunk = &macho_file.thunks.items[thunk_index]; diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig index e627b91f11da..b266bfeb47b8 100644 --- a/src/link/MachO/zld.zig +++ b/src/link/MachO/zld.zig @@ -1,31 +1,29 @@ pub fn linkWithZld( macho_file: *MachO, - comp: *Compilation, + arena: Allocator, prog_node: *std.Progress.Node, ) link.File.FlushError!void { const tracy = trace(@src()); defer tracy.end(); - const gpa = macho_file.base.allocator; - const options = &macho_file.base.options; - const target = options.target; + const comp = macho_file.base.comp; + const gpa = comp.gpa; + const target = comp.root_mod.resolved_target.result; + const emit = macho_file.base.emit; - var arena_allocator = std.heap.ArenaAllocator.init(gpa); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); - - const directory = options.emit.?.directory; // Just an alias to make it shorter to type. - const full_out_path = try directory.join(arena, &[_][]const u8{options.emit.?.sub_path}); + const directory = emit.directory; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{emit.sub_path}); + const opt_zcu = comp.module; // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. - const module_obj_path: ?[]const u8 = if (options.module != null) blk: { - try macho_file.flushModule(comp, prog_node); + const module_obj_path: ?[]const u8 = if (opt_zcu != null) blk: { + try macho_file.flushModule(arena, prog_node); if (fs.path.dirname(full_out_path)) |dirname| { - break :blk try fs.path.join(arena, &.{ dirname, macho_file.base.intermediary_basename.? }); + break :blk try fs.path.join(arena, &.{ dirname, macho_file.base.zcu_object_sub_path.? }); } else { - break :blk macho_file.base.intermediary_basename.?; + break :blk macho_file.base.zcu_object_sub_path.?; } } else null; @@ -34,22 +32,24 @@ pub fn linkWithZld( sub_prog_node.context.refresh(); defer sub_prog_node.end(); + const output_mode = comp.config.output_mode; + const link_mode = comp.config.link_mode; const cpu_arch = target.cpu.arch; - const is_lib = options.output_mode == .Lib; - const is_dyn_lib = options.link_mode == .Dynamic and is_lib; - const is_exe_or_dyn_lib = is_dyn_lib or options.output_mode == .Exe; - const stack_size = options.stack_size_override orelse 0; - const is_debug_build = options.optimize_mode == .Debug; - const gc_sections = options.gc_sections orelse !is_debug_build; + const is_lib = output_mode == .Lib; + const is_dyn_lib = link_mode == .Dynamic and is_lib; + const is_exe_or_dyn_lib = is_dyn_lib or output_mode == .Exe; + const stack_size = macho_file.base.stack_size; const id_symlink_basename = "zld.id"; var man: Cache.Manifest = undefined; - defer if (!options.disable_lld_caching) man.deinit(); + defer if (!macho_file.base.disable_lld_caching) man.deinit(); var digest: [Cache.hex_digest_len]u8 = undefined; - if (!options.disable_lld_caching) { + const objects = comp.objects; + + if (!macho_file.base.disable_lld_caching) { man = comp.cache_parent.obtain(); // We are about to obtain this lock, so here we give other processes a chance first. @@ -57,7 +57,7 @@ pub fn linkWithZld( comptime assert(Compilation.link_hash_implementation_version == 10); - for (options.objects) |obj| { + for (objects) |obj| { _ = try man.addFile(obj.path, null); man.hash.add(obj.must_link); } @@ -68,24 +68,22 @@ pub fn linkWithZld( // We can skip hashing libc and libc++ components that we are in charge of building from Zig // installation sources because they are always a product of the compiler version + target information. man.hash.add(stack_size); - man.hash.addOptional(options.pagezero_size); - man.hash.addOptional(options.headerpad_size); - man.hash.add(options.headerpad_max_install_names); - man.hash.add(gc_sections); - man.hash.add(options.dead_strip_dylibs); - man.hash.add(options.strip); - man.hash.addListOfBytes(options.lib_dirs); - man.hash.addListOfBytes(options.framework_dirs); - try link.hashAddFrameworks(&man, options.frameworks); - man.hash.addListOfBytes(options.rpath_list); + man.hash.add(macho_file.pagezero_vmsize); + man.hash.add(macho_file.headerpad_size); + man.hash.add(macho_file.headerpad_max_install_names); + man.hash.add(macho_file.base.gc_sections); + man.hash.add(macho_file.dead_strip_dylibs); + man.hash.add(comp.root_mod.strip); + try MachO.hashAddFrameworks(&man, macho_file.frameworks); + man.hash.addListOfBytes(macho_file.base.rpath_list); if (is_dyn_lib) { - man.hash.addOptionalBytes(options.install_name); - man.hash.addOptional(options.version); + man.hash.addOptionalBytes(macho_file.install_name); + man.hash.addOptional(comp.version); } - try link.hashAddSystemLibs(&man, options.system_libs); - man.hash.addOptionalBytes(options.sysroot); - man.hash.addListOfBytes(options.force_undefined_symbols.keys()); - try man.addOptionalFile(options.entitlements); + try link.hashAddSystemLibs(&man, comp.system_libs); + man.hash.addOptionalBytes(comp.sysroot); + man.hash.addListOfBytes(comp.force_undefined_symbols.keys()); + try man.addOptionalFile(macho_file.entitlements); // We don't actually care whether it's a cache hit or miss; we just // need the digest and the lock. @@ -125,13 +123,13 @@ pub fn linkWithZld( }; } - if (options.output_mode == .Obj) { + if (output_mode == .Obj) { // LLD's MachO driver does not support the equivalent of `-r` so we do a simple file copy // here. TODO: think carefully about how we can avoid this redundant operation when doing // build-obj. See also the corresponding TODO in linkAsArchive. const the_object_path = blk: { - if (options.objects.len != 0) { - break :blk options.objects[0].path; + if (objects.len != 0) { + break :blk objects[0].path; } if (comp.c_object_table.count() != 0) @@ -150,7 +148,7 @@ pub fn linkWithZld( try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); } } else { - const sub_path = options.emit.?.sub_path; + const sub_path = emit.sub_path; const old_file = macho_file.base.file; // TODO is this needed at all? defer macho_file.base.file = old_file; @@ -158,7 +156,7 @@ pub fn linkWithZld( const file = try directory.handle.createFile(sub_path, .{ .truncate = true, .read = true, - .mode = link.determineMode(options.*), + .mode = link.File.determineMode(false, output_mode, link_mode), }); defer file.close(); macho_file.base.file = file; @@ -175,8 +173,8 @@ pub fn linkWithZld( // Positional arguments to the linker such as object files and static archives. var positionals = std.ArrayList(Compilation.LinkObject).init(arena); - try positionals.ensureUnusedCapacity(options.objects.len); - positionals.appendSliceAssumeCapacity(options.objects); + try positionals.ensureUnusedCapacity(objects.len); + positionals.appendSliceAssumeCapacity(objects); for (comp.c_object_table.keys()) |key| { try positionals.append(.{ .path = key.status.success.object_path }); @@ -190,7 +188,7 @@ pub fn linkWithZld( if (comp.compiler_rt_obj) |obj| try positionals.append(.{ .path = obj.full_object_path }); // libc++ dep - if (options.link_libcpp) { + if (comp.config.link_libcpp) { try positionals.ensureUnusedCapacity(2); positionals.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path }); positionals.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path }); @@ -199,23 +197,23 @@ pub fn linkWithZld( var libs = std.StringArrayHashMap(link.SystemLib).init(arena); { - const vals = options.system_libs.values(); + const vals = comp.system_libs.values(); try libs.ensureUnusedCapacity(vals.len); for (vals) |v| libs.putAssumeCapacity(v.path.?, v); } { - try libs.ensureUnusedCapacity(options.frameworks.len); - for (options.frameworks) |v| libs.putAssumeCapacity(v.path, .{ + try libs.ensureUnusedCapacity(macho_file.frameworks.len); + for (macho_file.frameworks) |v| libs.putAssumeCapacity(v.path, .{ .needed = v.needed, .weak = v.weak, .path = v.path, }); } - try macho_file.resolveLibSystem(arena, comp, options.lib_dirs, &libs); + try macho_file.resolveLibSystem(arena, comp, &libs); - if (options.verbose_link) { + if (comp.verbose_link) { var argv = std.ArrayList([]const u8).init(arena); try argv.append("zig"); @@ -228,19 +226,19 @@ pub fn linkWithZld( if (is_dyn_lib) { try argv.append("-dylib"); - if (options.install_name) |install_name| { + if (macho_file.install_name) |install_name| { try argv.append("-install_name"); try argv.append(install_name); } } { - const platform = Platform.fromTarget(options.target); + const platform = Platform.fromTarget(target); try argv.append("-platform_version"); try argv.append(@tagName(platform.os_tag)); try argv.append(try std.fmt.allocPrint(arena, "{}", .{platform.version})); - const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(arena, comp); + const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(macho_file); if (sdk_version) |ver| { try argv.append(try std.fmt.allocPrint(arena, "{d}.{d}", .{ ver.major, ver.minor })); } else { @@ -248,44 +246,38 @@ pub fn linkWithZld( } } - if (options.sysroot) |syslibroot| { + if (comp.sysroot) |syslibroot| { try argv.append("-syslibroot"); try argv.append(syslibroot); } - for (options.rpath_list) |rpath| { + for (macho_file.base.rpath_list) |rpath| { try argv.append("-rpath"); try argv.append(rpath); } - if (options.pagezero_size) |pagezero_size| { - try argv.append("-pagezero_size"); - try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size})); - } - - if (options.headerpad_size) |headerpad_size| { - try argv.append("-headerpad_size"); - try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{headerpad_size})); - } + try argv.appendSlice(&.{ + "-pagezero_size", try std.fmt.allocPrint(arena, "0x{x}", .{macho_file.pagezero_vmsize}), + "-headerpad_size", try std.fmt.allocPrint(arena, "0x{x}", .{macho_file.headerpad_size}), + }); - if (options.headerpad_max_install_names) { + if (macho_file.headerpad_max_install_names) { try argv.append("-headerpad_max_install_names"); } - if (gc_sections) { + if (macho_file.base.gc_sections) { try argv.append("-dead_strip"); } - if (options.dead_strip_dylibs) { + if (macho_file.dead_strip_dylibs) { try argv.append("-dead_strip_dylibs"); } - if (options.entry) |entry| { - try argv.append("-e"); - try argv.append(entry); + if (macho_file.entry_name) |entry_name| { + try argv.appendSlice(&.{ "-e", entry_name }); } - for (options.objects) |obj| { + for (objects) |obj| { if (obj.must_link) { try argv.append("-force_load"); } @@ -303,7 +295,7 @@ pub fn linkWithZld( if (comp.compiler_rt_lib) |lib| try argv.append(lib.full_object_path); if (comp.compiler_rt_obj) |obj| try argv.append(obj.full_object_path); - if (options.link_libcpp) { + if (comp.config.link_libcpp) { try argv.append(comp.libcxxabi_static_lib.?.full_object_path); try argv.append(comp.libcxx_static_lib.?.full_object_path); } @@ -313,8 +305,8 @@ pub fn linkWithZld( try argv.append("-lSystem"); - for (options.system_libs.keys()) |l_name| { - const info = options.system_libs.get(l_name).?; + for (comp.system_libs.keys()) |l_name| { + const info = comp.system_libs.get(l_name).?; const arg = if (info.needed) try std.fmt.allocPrint(arena, "-needed-l{s}", .{l_name}) else if (info.weak) @@ -324,11 +316,7 @@ pub fn linkWithZld( try argv.append(arg); } - for (options.lib_dirs) |lib_dir| { - try argv.append(try std.fmt.allocPrint(arena, "-L{s}", .{lib_dir})); - } - - for (options.frameworks) |framework| { + for (macho_file.frameworks) |framework| { const name = std.fs.path.stem(framework.path); const arg = if (framework.needed) try std.fmt.allocPrint(arena, "-needed_framework {s}", .{name}) @@ -339,11 +327,7 @@ pub fn linkWithZld( try argv.append(arg); } - for (options.framework_dirs) |framework_dir| { - try argv.append(try std.fmt.allocPrint(arena, "-F{s}", .{framework_dir})); - } - - if (is_dyn_lib and (options.allow_shlib_undefined orelse false)) { + if (is_dyn_lib and macho_file.base.allow_shlib_undefined) { try argv.append("-undefined"); try argv.append("dynamic_lookup"); } @@ -412,14 +396,14 @@ pub fn linkWithZld( }; } - if (gc_sections) { + if (macho_file.base.gc_sections) { try dead_strip.gcAtoms(macho_file); } try macho_file.createDyldPrivateAtom(); try macho_file.createTentativeDefAtoms(); - if (macho_file.base.options.output_mode == .Exe) { + if (comp.config.output_mode == .Exe) { const global = macho_file.getEntryPoint().?; if (macho_file.getSymbol(global).undf()) { // We do one additional check here in case the entry point was found in one of the dylibs. @@ -470,7 +454,7 @@ pub fn linkWithZld( } try writeAtoms(macho_file); - if (macho_file.base.options.target.cpu.arch == .aarch64) try writeThunks(macho_file); + if (target.cpu.arch == .aarch64) try writeThunks(macho_file); try writeDyldPrivateAtom(macho_file); if (macho_file.stubs_section_index) |_| { @@ -511,7 +495,7 @@ pub fn linkWithZld( } // Write code signature padding if required - var codesig: ?CodeSignature = if (MachO.requiresCodeSignature(&macho_file.base.options)) blk: { + var codesig: ?CodeSignature = if (macho_file.requiresCodeSignature()) blk: { // Preallocate space for the code signature. // We need to do this at this stage so that we have the load commands with proper values // written out to the file. @@ -519,7 +503,7 @@ pub fn linkWithZld( // where the code signature goes into. var codesig = CodeSignature.init(MachO.getPageSize(cpu_arch)); codesig.code_directory.ident = fs.path.basename(full_out_path); - if (options.entitlements) |path| { + if (macho_file.entitlements) |path| { try codesig.addEntitlements(gpa, path); } try macho_file.writeCodeSignaturePadding(&codesig); @@ -539,7 +523,7 @@ pub fn linkWithZld( try lc_writer.writeStruct(macho_file.dysymtab_cmd); try load_commands.writeDylinkerLC(lc_writer); - switch (macho_file.base.options.output_mode) { + switch (output_mode) { .Exe => blk: { const seg_id = macho_file.header_segment_cmd_index.?; const seg = macho_file.segments.items[seg_id]; @@ -555,22 +539,22 @@ pub fn linkWithZld( try lc_writer.writeStruct(macho.entry_point_command{ .entryoff = @as(u32, @intCast(addr - seg.vmaddr)), - .stacksize = macho_file.base.options.stack_size_override orelse 0, + .stacksize = macho_file.base.stack_size, }); }, - .Lib => if (macho_file.base.options.link_mode == .Dynamic) { - try load_commands.writeDylibIdLC(gpa, &macho_file.base.options, lc_writer); + .Lib => if (link_mode == .Dynamic) { + try load_commands.writeDylibIdLC(macho_file, lc_writer); }, else => {}, } - try load_commands.writeRpathLCs(gpa, &macho_file.base.options, lc_writer); + try load_commands.writeRpathLCs(macho_file, lc_writer); try lc_writer.writeStruct(macho.source_version_command{ .version = 0, }); { - const platform = Platform.fromTarget(macho_file.base.options.target); - const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(arena, comp); + const platform = Platform.fromTarget(target); + const sdk_version: ?std.SemanticVersion = load_commands.inferSdkVersion(macho_file); if (platform.isBuildVersionCompatible()) { try load_commands.writeBuildVersionLC(platform, sdk_version, lc_writer); } else { @@ -598,11 +582,11 @@ pub fn linkWithZld( if (codesig) |*csig| { try macho_file.writeCodeSignature(comp, csig); // code signing always comes last - try MachO.invalidateKernelCache(directory.handle, macho_file.base.options.emit.?.sub_path); + try MachO.invalidateKernelCache(directory.handle, macho_file.base.emit.sub_path); } } - if (!options.disable_lld_caching) { + if (!macho_file.base.disable_lld_caching) { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { @@ -621,13 +605,14 @@ pub fn linkWithZld( } fn createSegments(macho_file: *MachO) !void { - const gpa = macho_file.base.allocator; - const pagezero_vmsize = macho_file.base.options.pagezero_size orelse MachO.default_pagezero_vmsize; - const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch); - const aligned_pagezero_vmsize = mem.alignBackward(u64, pagezero_vmsize, page_size); - if (macho_file.base.options.output_mode != .Lib and aligned_pagezero_vmsize > 0) { - if (aligned_pagezero_vmsize != pagezero_vmsize) { - log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{pagezero_vmsize}); + const comp = macho_file.base.comp; + const gpa = comp.gpa; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const page_size = MachO.getPageSize(target.cpu.arch); + const aligned_pagezero_vmsize = mem.alignBackward(u64, macho_file.pagezero_vmsize, page_size); + if (macho_file.base.comp.config.output_mode != .Lib and aligned_pagezero_vmsize > 0) { + if (aligned_pagezero_vmsize != macho_file.pagezero_vmsize) { + log.warn("requested __PAGEZERO size (0x{x}) is not page aligned", .{macho_file.pagezero_vmsize}); log.warn(" rounding down to 0x{x}", .{aligned_pagezero_vmsize}); } macho_file.pagezero_segment_cmd_index = @intCast(macho_file.segments.items.len); @@ -695,7 +680,8 @@ fn createSegments(macho_file: *MachO) !void { } fn writeAtoms(macho_file: *MachO) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const slice = macho_file.sections.slice(); for (slice.items(.first_atom_index), 0..) |first_atom_index, sect_id| { @@ -768,8 +754,10 @@ fn writeDyldPrivateAtom(macho_file: *MachO) !void { } fn writeThunks(macho_file: *MachO) !void { - assert(macho_file.base.options.target.cpu.arch == .aarch64); - const gpa = macho_file.base.allocator; + const target = macho_file.base.comp.root_mod.resolved_target.result; + assert(target.cpu.arch == .aarch64); + const comp = macho_file.base.comp; + const gpa = comp.gpa; const sect_id = macho_file.text_section_index orelse return; const header = macho_file.sections.items(.header)[sect_id]; @@ -789,7 +777,8 @@ fn writeThunks(macho_file: *MachO) !void { } fn writePointerEntries(macho_file: *MachO, sect_id: u8, table: anytype) !void { - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; const header = macho_file.sections.items(.header)[sect_id]; const capacity = math.cast(usize, header.size) orelse return error.Overflow; var buffer = try std.ArrayList(u8).initCapacity(gpa, capacity); @@ -803,8 +792,10 @@ fn writePointerEntries(macho_file: *MachO, sect_id: u8, table: anytype) !void { } fn writeStubs(macho_file: *MachO) !void { - const gpa = macho_file.base.allocator; - const cpu_arch = macho_file.base.options.target.cpu.arch; + const comp = macho_file.base.comp; + const gpa = comp.gpa; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; const stubs_header = macho_file.sections.items(.header)[macho_file.stubs_section_index.?]; const la_symbol_ptr_header = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_section_index.?]; @@ -825,8 +816,10 @@ fn writeStubs(macho_file: *MachO) !void { } fn writeStubHelpers(macho_file: *MachO) !void { - const gpa = macho_file.base.allocator; - const cpu_arch = macho_file.base.options.target.cpu.arch; + const comp = macho_file.base.comp; + const gpa = comp.gpa; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; const stub_helper_header = macho_file.sections.items(.header)[macho_file.stub_helper_section_index.?]; const capacity = math.cast(usize, stub_helper_header.size) orelse return error.Overflow; @@ -868,8 +861,10 @@ fn writeStubHelpers(macho_file: *MachO) !void { } fn writeLaSymbolPtrs(macho_file: *MachO) !void { - const gpa = macho_file.base.allocator; - const cpu_arch = macho_file.base.options.target.cpu.arch; + const comp = macho_file.base.comp; + const gpa = comp.gpa; + const target = macho_file.base.comp.root_mod.resolved_target.result; + const cpu_arch = target.cpu.arch; const la_symbol_ptr_header = macho_file.sections.items(.header)[macho_file.la_symbol_ptr_section_index.?]; const stub_helper_header = macho_file.sections.items(.header)[macho_file.stub_helper_section_index.?]; @@ -900,7 +895,8 @@ fn pruneAndSortSections(macho_file: *MachO) !void { } }; - const gpa = macho_file.base.allocator; + const comp = macho_file.base.comp; + const gpa = comp.gpa; var entries = try std.ArrayList(Entry).initCapacity(gpa, macho_file.sections.slice().len); defer entries.deinit(); @@ -977,11 +973,12 @@ fn pruneAndSortSections(macho_file: *MachO) !void { } fn calcSectionSizes(macho_file: *MachO) !void { + const target = macho_file.base.comp.root_mod.resolved_target.result; const slice = macho_file.sections.slice(); for (slice.items(.header), 0..) |*header, sect_id| { if (header.size == 0) continue; if (macho_file.text_section_index) |txt| { - if (txt == sect_id and macho_file.base.options.target.cpu.arch == .aarch64) continue; + if (txt == sect_id and target.cpu.arch == .aarch64) continue; } var atom_index = slice.items(.first_atom_index)[sect_id] orelse continue; @@ -1004,7 +1001,7 @@ fn calcSectionSizes(macho_file: *MachO) !void { } } - if (macho_file.text_section_index != null and macho_file.base.options.target.cpu.arch == .aarch64) { + if (macho_file.text_section_index != null and target.cpu.arch == .aarch64) { // Create jump/branch range extenders if needed. try thunks.createThunks(macho_file, macho_file.text_section_index.?); } @@ -1056,7 +1053,7 @@ fn calcSectionSizes(macho_file: *MachO) !void { header.@"align" = 3; } - const cpu_arch = macho_file.base.options.target.cpu.arch; + const cpu_arch = target.cpu.arch; if (macho_file.stubs_section_index) |sect_id| { const header = &macho_file.sections.items(.header)[sect_id]; @@ -1079,11 +1076,10 @@ fn calcSectionSizes(macho_file: *MachO) !void { } fn allocateSegments(macho_file: *MachO) !void { - const gpa = macho_file.base.allocator; for (macho_file.segments.items, 0..) |*segment, segment_index| { const is_text_segment = mem.eql(u8, segment.segName(), "__TEXT"); const base_size = if (is_text_segment) - try load_commands.calcMinHeaderPad(gpa, &macho_file.base.options, .{ + try load_commands.calcMinHeaderPad(macho_file, .{ .segments = macho_file.segments.items, .dylibs = macho_file.dylibs.items, .referenced_dylibs = macho_file.referenced_dylibs.keys(), @@ -1106,6 +1102,7 @@ fn getSegmentAllocBase(macho_file: *MachO, segment_index: u8) struct { vmaddr: u } fn allocateSegment(macho_file: *MachO, segment_index: u8, init_size: u64) !void { + const target = macho_file.base.comp.root_mod.resolved_target.result; const segment = &macho_file.segments.items[segment_index]; if (mem.eql(u8, segment.segName(), "__PAGEZERO")) return; // allocated upon creation @@ -1188,7 +1185,7 @@ fn allocateSegment(macho_file: *MachO, segment_index: u8, init_size: u64) !void segment.vmsize = start; } - const page_size = MachO.getPageSize(macho_file.base.options.target.cpu.arch); + const page_size = MachO.getPageSize(target.cpu.arch); segment.filesize = mem.alignForward(u64, segment.filesize, page_size); segment.vmsize = mem.alignForward(u64, segment.vmsize, page_size); } diff --git a/src/link/NvPtx.zig b/src/link/NvPtx.zig index 606c5f123754..111b59fc3ba5 100644 --- a/src/link/NvPtx.zig +++ b/src/link/NvPtx.zig @@ -24,46 +24,62 @@ const LlvmObject = @import("../codegen/llvm.zig").Object; base: link.File, llvm_object: *LlvmObject, -ptx_file_name: []const u8, -pub fn createEmpty(gpa: Allocator, options: link.Options) !*NvPtx { - if (!options.use_llvm) return error.PtxArchNotSupported; - - if (!options.target.cpu.arch.isNvptx()) return error.PtxArchNotSupported; - - switch (options.target.os.tag) { +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*NvPtx { + const target = comp.root_mod.resolved_target.result; + const use_lld = build_options.have_llvm and comp.config.use_lld; + const use_llvm = comp.config.use_llvm; + + assert(use_llvm); // Caught by Compilation.Config.resolve. + assert(!use_lld); // Caught by Compilation.Config.resolve. + assert(target.cpu.arch.isNvptx()); // Caught by Compilation.Config.resolve. + + switch (target.os.tag) { // TODO: does it also work with nvcl ? .cuda => {}, else => return error.PtxArchNotSupported, } - const llvm_object = try LlvmObject.create(gpa, options); - const nvptx = try gpa.create(NvPtx); + const llvm_object = try LlvmObject.create(arena, comp); + const nvptx = try arena.create(NvPtx); nvptx.* = .{ .base = .{ .tag = .nvptx, - .options = options, + .comp = comp, + .emit = emit, + .gc_sections = options.gc_sections orelse false, + .print_gc_sections = options.print_gc_sections, + .stack_size = options.stack_size orelse 0, + .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, - .allocator = gpa, + .disable_lld_caching = options.disable_lld_caching, + .build_id = options.build_id, + .rpath_list = options.rpath_list, }, .llvm_object = llvm_object, - .ptx_file_name = try std.mem.join(gpa, "", &[_][]const u8{ options.root_name, ".ptx" }), }; return nvptx; } -pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*NvPtx { - if (!options.use_llvm) return error.PtxArchNotSupported; - assert(options.target.ofmt == .nvptx); - - log.debug("Opening .ptx target file {s}", .{sub_path}); - return createEmpty(allocator, options); +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*NvPtx { + const target = comp.root_mod.resolved_target.result; + assert(target.ofmt == .nvptx); + return createEmpty(arena, comp, emit, options); } pub fn deinit(self: *NvPtx) void { - self.llvm_object.destroy(self.base.allocator); - self.base.allocator.free(self.ptx_file_name); + self.llvm_object.deinit(); } pub fn updateFunc(self: *NvPtx, module: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) !void { @@ -80,9 +96,9 @@ pub fn updateExports( exported: Module.Exported, exports: []const *Module.Export, ) !void { - if (build_options.skip_non_native and builtin.object_format != .nvptx) { + if (build_options.skip_non_native and builtin.object_format != .nvptx) @panic("Attempted to compile for object format that was disabled by build configuration"); - } + return self.llvm_object.updateExports(module, exported, exports); } @@ -90,32 +106,18 @@ pub fn freeDecl(self: *NvPtx, decl_index: InternPool.DeclIndex) void { return self.llvm_object.freeDecl(decl_index); } -pub fn flush(self: *NvPtx, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { - return self.flushModule(comp, prog_node); +pub fn flush(self: *NvPtx, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { + return self.flushModule(arena, prog_node); } -pub fn flushModule(self: *NvPtx, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { - if (build_options.skip_non_native) { +pub fn flushModule(self: *NvPtx, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { + if (build_options.skip_non_native) @panic("Attempted to compile for architecture that was disabled by build configuration"); - } - const outfile = comp.bin_file.options.emit orelse return; - - const tracy = trace(@src()); - defer tracy.end(); - - // We modify 'comp' before passing it to LLVM, but restore value afterwards. - // We tell LLVM to not try to build a .o, only an "assembly" file. - // This is required by the LLVM PTX backend. - comp.bin_file.options.emit = null; - comp.emit_asm = .{ - // 'null' means using the default cache dir: zig-cache/o/... - .directory = null, - .basename = self.ptx_file_name, - }; - defer { - comp.bin_file.options.emit = outfile; - comp.emit_asm = null; - } - try self.llvm_object.flushModule(comp, prog_node); + // The code that was here before mutated the Compilation's file emission mechanism. + // That's not supposed to happen in flushModule, so I deleted the code. + _ = arena; + _ = self; + _ = prog_node; + @panic("TODO: rewrite the NvPtx.flushModule function"); } diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index b608b291615e..a635b0bf5b23 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -28,7 +28,6 @@ pub const base_tag = .plan9; base: link.File, sixtyfour_bit: bool, -error_flags: File.ErrorFlags = File.ErrorFlags{}, bases: Bases, /// A symbol's value is just casted down when compiling @@ -174,7 +173,7 @@ pub const Atom = struct { return .{ .code_ptr = slice.ptr, .other = .{ .code_len = slice.len } }; } fn getCode(self: CodePtr, plan9: *const Plan9) []u8 { - const mod = plan9.base.options.module.?; + const mod = plan9.base.comp.module.?; return if (self.code_ptr) |p| p[0..self.other.code_len] else blk: { const decl_index = self.other.decl_index; const decl = mod.declPtr(decl_index); @@ -206,7 +205,8 @@ pub const Atom = struct { // asserts that self.got_index != null pub fn getOffsetTableAddress(self: Atom, plan9: *Plan9) u64 { - const ptr_bytes = @divExact(plan9.base.options.target.ptrBitWidth(), 8); + const target = plan9.base.comp.root_mod.resolved_target.result; + const ptr_bytes = @divExact(target.ptrBitWidth(), 8); const got_addr = plan9.bases.data; const got_index = self.got_index.?; return got_addr + got_index * ptr_bytes; @@ -293,38 +293,51 @@ pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases { }; } -pub fn createEmpty(gpa: Allocator, options: link.Options) !*Plan9 { - if (options.use_llvm) - return error.LLVMBackendDoesNotSupportPlan9; - const sixtyfour_bit: bool = switch (options.target.ptrBitWidth()) { +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*Plan9 { + const target = comp.root_mod.resolved_target.result; + const gpa = comp.gpa; + const optimize_mode = comp.root_mod.optimize_mode; + const output_mode = comp.config.output_mode; + + const sixtyfour_bit: bool = switch (target.ptrBitWidth()) { 0...32 => false, 33...64 => true, else => return error.UnsupportedP9Architecture, }; - const arena_allocator = std.heap.ArenaAllocator.init(gpa); - - const self = try gpa.create(Plan9); + const self = try arena.create(Plan9); self.* = .{ - .path_arena = arena_allocator, + .path_arena = std.heap.ArenaAllocator.init(gpa), .base = .{ .tag = .plan9, - .options = options, - .allocator = gpa, + .comp = comp, + .emit = emit, + .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj), + .print_gc_sections = options.print_gc_sections, + .stack_size = options.stack_size orelse 16777216, + .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, + .disable_lld_caching = options.disable_lld_caching, + .build_id = options.build_id, + .rpath_list = options.rpath_list, }, .sixtyfour_bit = sixtyfour_bit, .bases = undefined, - .magic = try aout.magicFromArch(self.base.options.target.cpu.arch), + .magic = try aout.magicFromArch(target.cpu.arch), }; // a / will always be in a file path - try self.file_segments.put(self.base.allocator, "/", 1); + try self.file_segments.put(gpa, "/", 1); return self; } fn putFn(self: *Plan9, decl_index: InternPool.DeclIndex, out: FnDeclOutput) !void { - const gpa = self.base.allocator; - const mod = self.base.options.module.?; + const gpa = self.base.comp.gpa; + const mod = self.base.comp.module.?; const decl = mod.declPtr(decl_index); const fn_map_res = try self.fn_decl_table.getOrPut(gpa, decl.getFileScope(mod)); if (fn_map_res.found_existing) { @@ -379,6 +392,7 @@ fn putFn(self: *Plan9, decl_index: InternPool.DeclIndex, out: FnDeclOutput) !voi } fn addPathComponents(self: *Plan9, path: []const u8, a: *std.ArrayList(u8)) !void { + const gpa = self.base.comp.gpa; const sep = std.fs.path.sep; var it = std.mem.tokenizeScalar(u8, path, sep); while (it.next()) |component| { @@ -386,7 +400,7 @@ fn addPathComponents(self: *Plan9, path: []const u8, a: *std.ArrayList(u8)) !voi try a.writer().writeInt(u16, num, .big); } else { self.file_segments_i += 1; - try self.file_segments.put(self.base.allocator, component, self.file_segments_i); + try self.file_segments.put(gpa, component, self.file_segments_i); try a.writer().writeInt(u16, self.file_segments_i, .big); } } @@ -397,6 +411,8 @@ pub fn updateFunc(self: *Plan9, mod: *Module, func_index: InternPool.Index, air: @panic("Attempted to compile for object format that was disabled by build configuration"); } + const gpa = self.base.comp.gpa; + const target = self.base.comp.root_mod.resolved_target.result; const func = mod.funcInfo(func_index); const decl_index = func.owner_decl; const decl = mod.declPtr(decl_index); @@ -404,15 +420,15 @@ pub fn updateFunc(self: *Plan9, mod: *Module, func_index: InternPool.Index, air: const atom_idx = try self.seeDecl(decl_index); - var code_buffer = std.ArrayList(u8).init(self.base.allocator); + var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); var dbg_info_output: DebugInfoOutput = .{ - .dbg_line = std.ArrayList(u8).init(self.base.allocator), + .dbg_line = std.ArrayList(u8).init(gpa), .start_line = null, .end_line = undefined, .pcop_change_index = null, // we have already checked the target in the linker to make sure it is compatable - .pc_quanta = aout.getPCQuant(self.base.options.target.cpu.arch) catch unreachable, + .pc_quanta = aout.getPCQuant(target.cpu.arch) catch unreachable, }; defer dbg_info_output.dbg_line.deinit(); @@ -448,14 +464,15 @@ pub fn updateFunc(self: *Plan9, mod: *Module, func_index: InternPool.Index, air: } pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: InternPool.DeclIndex) !u32 { + const gpa = self.base.comp.gpa; _ = try self.seeDecl(decl_index); - var code_buffer = std.ArrayList(u8).init(self.base.allocator); + var code_buffer = std.ArrayList(u8).init(gpa); defer code_buffer.deinit(); - const mod = self.base.options.module.?; + const mod = self.base.comp.module.?; const decl = mod.declPtr(decl_index); - const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl_index); + const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index); if (!gop.found_existing) { gop.value_ptr.* = .{}; } @@ -465,7 +482,7 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: InternPool.De const index = unnamed_consts.items.len; // name is freed when the unnamed const is freed - const name = try std.fmt.allocPrint(self.base.allocator, "__unnamed_{s}_{d}", .{ decl_name, index }); + const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index }); const sym_index = try self.allocateSymbolIndex(); const new_atom_idx = try self.createAtom(); @@ -498,17 +515,18 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: InternPool.De }, }; // duped_code is freed when the unnamed const is freed - const duped_code = try self.base.allocator.dupe(u8, code); - errdefer self.base.allocator.free(duped_code); + const duped_code = try gpa.dupe(u8, code); + errdefer gpa.free(duped_code); const new_atom = self.getAtomPtr(new_atom_idx); new_atom.* = info; new_atom.code = .{ .code_ptr = duped_code.ptr, .other = .{ .code_len = duped_code.len } }; - try unnamed_consts.append(self.base.allocator, new_atom_idx); + try unnamed_consts.append(gpa, new_atom_idx); // we return the new_atom_idx to codegen return new_atom_idx; } pub fn updateDecl(self: *Plan9, mod: *Module, decl_index: InternPool.DeclIndex) !void { + const gpa = self.base.comp.gpa; const decl = mod.declPtr(decl_index); if (decl.isExtern(mod)) { @@ -517,7 +535,7 @@ pub fn updateDecl(self: *Plan9, mod: *Module, decl_index: InternPool.DeclIndex) } const atom_idx = try self.seeDecl(decl_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; // TODO we need the symbol index for symbol in the table of locals for the containing atom @@ -535,17 +553,18 @@ pub fn updateDecl(self: *Plan9, mod: *Module, decl_index: InternPool.DeclIndex) return; }, }; - try self.data_decl_table.ensureUnusedCapacity(self.base.allocator, 1); - const duped_code = try self.base.allocator.dupe(u8, code); + try self.data_decl_table.ensureUnusedCapacity(gpa, 1); + const duped_code = try gpa.dupe(u8, code); self.getAtomPtr(self.decls.get(decl_index).?.index).code = .{ .code_ptr = null, .other = .{ .decl_index = decl_index } }; if (self.data_decl_table.fetchPutAssumeCapacity(decl_index, duped_code)) |old_entry| { - self.base.allocator.free(old_entry.value); + gpa.free(old_entry.value); } return self.updateFinish(decl_index); } /// called at the end of update{Decl,Func} fn updateFinish(self: *Plan9, decl_index: InternPool.DeclIndex) !void { - const mod = self.base.options.module.?; + const gpa = self.base.comp.gpa; + const mod = self.base.comp.module.?; const decl = mod.declPtr(decl_index); const is_fn = (decl.ty.zigTypeTag(mod) == .Fn); const sym_t: aout.Sym.Type = if (is_fn) .t else .d; @@ -558,7 +577,7 @@ fn updateFinish(self: *Plan9, decl_index: InternPool.DeclIndex) !void { const sym: aout.Sym = .{ .value = undefined, // the value of stuff gets filled in in flushModule .type = atom.type, - .name = try self.base.allocator.dupe(u8, mod.intern_pool.stringToSlice(decl.name)), + .name = try gpa.dupe(u8, mod.intern_pool.stringToSlice(decl.name)), }; if (atom.sym_index) |s| { @@ -571,10 +590,11 @@ fn updateFinish(self: *Plan9, decl_index: InternPool.DeclIndex) !void { } fn allocateSymbolIndex(self: *Plan9) !usize { + const gpa = self.base.comp.gpa; if (self.syms_index_free_list.popOrNull()) |i| { return i; } else { - _ = try self.syms.addOne(self.base.allocator); + _ = try self.syms.addOne(gpa); return self.syms.items.len - 1; } } @@ -588,16 +608,18 @@ fn allocateGotIndex(self: *Plan9) usize { } } -pub fn flush(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { - assert(!self.base.options.use_lld); +pub fn flush(self: *Plan9, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { + const comp = self.base.comp; + const use_lld = build_options.have_llvm and comp.config.use_lld; + assert(!use_lld); - switch (self.base.options.effectiveOutputMode()) { + switch (link.File.effectiveOutputMode(use_lld, comp.config.output_mode)) { .Exe => {}, // plan9 object files are totally different .Obj => return error.TODOImplementPlan9Objs, .Lib => return error.TODOImplementWritingLibFiles, } - return self.flushModule(comp, prog_node); + return self.flushModule(arena, prog_node); } pub fn changeLine(l: *std.ArrayList(u8), delta_line: i32) !void { @@ -645,12 +667,17 @@ fn atomCount(self: *Plan9) usize { return data_decl_count + fn_decl_count + unnamed_const_count + lazy_atom_count + extern_atom_count + anon_atom_count; } -pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { +pub fn flushModule(self: *Plan9, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { if (build_options.skip_non_native and builtin.object_format != .plan9) { @panic("Attempted to compile for object format that was disabled by build configuration"); } - _ = comp; + _ = arena; // Has the same lifetime as the call to Compilation.update. + + const comp = self.base.comp; + const gpa = comp.gpa; + const target = comp.root_mod.resolved_target.result; + const tracy = trace(@src()); defer tracy.end(); @@ -662,7 +689,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No defer assert(self.hdr.entry != 0x0); - const mod = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + const mod = self.base.comp.module orelse return error.LinkingWithoutZigSourceUnimplemented; // finish up the lazy syms if (self.lazy_syms.getPtr(.none)) |metadata| { @@ -691,12 +718,12 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No const atom_count = self.atomCount(); assert(self.got_len == atom_count + self.got_index_free_list.items.len); const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8; - var got_table = try self.base.allocator.alloc(u8, got_size); - defer self.base.allocator.free(got_table); + var got_table = try gpa.alloc(u8, got_size); + defer gpa.free(got_table); // + 4 for header, got, symbols, linecountinfo - var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.atomCount() + 4 - self.externCount()); - defer self.base.allocator.free(iovecs); + var iovecs = try gpa.alloc(std.os.iovec_const, self.atomCount() + 4 - self.externCount()); + defer gpa.free(iovecs); const file = self.base.file.?; @@ -709,7 +736,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No var iovecs_i: usize = 1; var text_i: u64 = 0; - var linecountinfo = std.ArrayList(u8).init(self.base.allocator); + var linecountinfo = std.ArrayList(u8).init(gpa); defer linecountinfo.deinit(); // text { @@ -741,9 +768,9 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No atom.offset = off; log.debug("write text decl {*} ({}), lines {d} to {d}.;__GOT+0x{x} vaddr: 0x{x}", .{ decl, decl.name.fmt(&mod.intern_pool), out.start_line + 1, out.end_line, atom.got_index.? * 8, off }); if (!self.sixtyfour_bit) { - mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), self.base.options.target.cpu.arch.endian()); + mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), target.cpu.arch.endian()); } else { - mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); + mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, target.cpu.arch.endian()); } self.syms.items[atom.sym_index.?].value = off; if (mod.decl_exports.get(decl_index)) |exports| { @@ -770,9 +797,9 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No text_i += code.len; text_atom.offset = off; if (!self.sixtyfour_bit) { - mem.writeInt(u32, got_table[text_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), self.base.options.target.cpu.arch.endian()); + mem.writeInt(u32, got_table[text_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), target.cpu.arch.endian()); } else { - mem.writeInt(u64, got_table[text_atom.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); + mem.writeInt(u64, got_table[text_atom.got_index.? * 8 ..][0..8], off, target.cpu.arch.endian()); } self.syms.items[text_atom.sym_index.?].value = off; } @@ -783,9 +810,9 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No const val = self.getAddr(text_i, .t); self.syms.items[etext_atom.sym_index.?].value = val; if (!self.sixtyfour_bit) { - mem.writeInt(u32, got_table[etext_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(val)), self.base.options.target.cpu.arch.endian()); + mem.writeInt(u32, got_table[etext_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(val)), target.cpu.arch.endian()); } else { - mem.writeInt(u64, got_table[etext_atom.got_index.? * 8 ..][0..8], val, self.base.options.target.cpu.arch.endian()); + mem.writeInt(u64, got_table[etext_atom.got_index.? * 8 ..][0..8], val, target.cpu.arch.endian()); } } // global offset table is in data @@ -807,9 +834,9 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No data_i += code.len; atom.offset = off; if (!self.sixtyfour_bit) { - mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), self.base.options.target.cpu.arch.endian()); + mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), target.cpu.arch.endian()); } else { - mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); + mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, target.cpu.arch.endian()); } self.syms.items[atom.sym_index.?].value = off; if (mod.decl_exports.get(decl_index)) |exports| { @@ -830,9 +857,9 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No data_i += code.len; atom.offset = off; if (!self.sixtyfour_bit) { - mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), self.base.options.target.cpu.arch.endian()); + mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), target.cpu.arch.endian()); } else { - mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); + mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, target.cpu.arch.endian()); } self.syms.items[atom.sym_index.?].value = off; } @@ -851,9 +878,9 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No data_i += code.len; atom.offset = off; if (!self.sixtyfour_bit) { - mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), self.base.options.target.cpu.arch.endian()); + mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), target.cpu.arch.endian()); } else { - mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); + mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, target.cpu.arch.endian()); } self.syms.items[atom.sym_index.?].value = off; } @@ -871,9 +898,9 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No data_i += code.len; data_atom.offset = off; if (!self.sixtyfour_bit) { - mem.writeInt(u32, got_table[data_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), self.base.options.target.cpu.arch.endian()); + mem.writeInt(u32, got_table[data_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), target.cpu.arch.endian()); } else { - mem.writeInt(u64, got_table[data_atom.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); + mem.writeInt(u64, got_table[data_atom.got_index.? * 8 ..][0..8], off, target.cpu.arch.endian()); } self.syms.items[data_atom.sym_index.?].value = off; } @@ -883,9 +910,9 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No const val = self.getAddr(data_i, .b); self.syms.items[edata_atom.sym_index.?].value = val; if (!self.sixtyfour_bit) { - mem.writeInt(u32, got_table[edata_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(val)), self.base.options.target.cpu.arch.endian()); + mem.writeInt(u32, got_table[edata_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(val)), target.cpu.arch.endian()); } else { - mem.writeInt(u64, got_table[edata_atom.got_index.? * 8 ..][0..8], val, self.base.options.target.cpu.arch.endian()); + mem.writeInt(u64, got_table[edata_atom.got_index.? * 8 ..][0..8], val, target.cpu.arch.endian()); } } // end symbol (same as edata because native backends don't do .bss yet) @@ -894,17 +921,17 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No const val = self.getAddr(data_i, .b); self.syms.items[end_atom.sym_index.?].value = val; if (!self.sixtyfour_bit) { - mem.writeInt(u32, got_table[end_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(val)), self.base.options.target.cpu.arch.endian()); + mem.writeInt(u32, got_table[end_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(val)), target.cpu.arch.endian()); } else { log.debug("write end (got_table[0x{x}] = 0x{x})", .{ end_atom.got_index.? * 8, val }); - mem.writeInt(u64, got_table[end_atom.got_index.? * 8 ..][0..8], val, self.base.options.target.cpu.arch.endian()); + mem.writeInt(u64, got_table[end_atom.got_index.? * 8 ..][0..8], val, target.cpu.arch.endian()); } } } - var sym_buf = std.ArrayList(u8).init(self.base.allocator); + var sym_buf = std.ArrayList(u8).init(gpa); try self.writeSyms(&sym_buf); const syms = try sym_buf.toOwnedSlice(); - defer self.base.allocator.free(syms); + defer gpa.free(syms); assert(2 + self.atomCount() - self.externCount() == iovecs_i); // we didn't write all the decls iovecs[iovecs_i] = .{ .iov_base = syms.ptr, .iov_len = syms.len }; iovecs_i += 1; @@ -934,7 +961,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No const source_atom = self.getAtom(source_atom_index); const source_atom_symbol = self.syms.items[source_atom.sym_index.?]; const code = source_atom.code.getCode(self); - const endian = self.base.options.target.cpu.arch.endian(); + const endian = target.cpu.arch.endian(); for (kv.value_ptr.items) |reloc| { const offset = reloc.offset; const addend = reloc.addend; @@ -985,6 +1012,7 @@ fn addDeclExports( decl_index: InternPool.DeclIndex, exports: []const *Module.Export, ) !void { + const gpa = self.base.comp.gpa; const metadata = self.decls.getPtr(decl_index).?; const atom = self.getAtom(metadata.index); @@ -994,7 +1022,7 @@ fn addDeclExports( if (exp.opts.section.unwrap()) |section_name| { if (!mod.intern_pool.stringEqlSlice(section_name, ".text") and !mod.intern_pool.stringEqlSlice(section_name, ".data")) { try mod.failed_exports.put(mod.gpa, exp, try Module.ErrorMsg.create( - self.base.allocator, + gpa, mod.declPtr(decl_index).srcLoc(mod), "plan9 does not support extra sections", .{}, @@ -1005,41 +1033,42 @@ fn addDeclExports( const sym = .{ .value = atom.offset.?, .type = atom.type.toGlobal(), - .name = try self.base.allocator.dupe(u8, exp_name), + .name = try gpa.dupe(u8, exp_name), }; if (metadata.getExport(self, exp_name)) |i| { self.syms.items[i] = sym; } else { - try self.syms.append(self.base.allocator, sym); - try metadata.exports.append(self.base.allocator, self.syms.items.len - 1); + try self.syms.append(gpa, sym); + try metadata.exports.append(gpa, self.syms.items.len - 1); } } } pub fn freeDecl(self: *Plan9, decl_index: InternPool.DeclIndex) void { + const gpa = self.base.comp.gpa; // TODO audit the lifetimes of decls table entries. It's possible to get // freeDecl without any updateDecl in between. // However that is planned to change, see the TODO comment in Module.zig // in the deleteUnusedDecl function. - const mod = self.base.options.module.?; + const mod = self.base.comp.module.?; const decl = mod.declPtr(decl_index); const is_fn = decl.val.isFuncBody(mod); if (is_fn) { const symidx_and_submap = self.fn_decl_table.get(decl.getFileScope(mod)).?; var submap = symidx_and_submap.functions; if (submap.fetchSwapRemove(decl_index)) |removed_entry| { - self.base.allocator.free(removed_entry.value.code); - self.base.allocator.free(removed_entry.value.lineinfo); + gpa.free(removed_entry.value.code); + gpa.free(removed_entry.value.lineinfo); } if (submap.count() == 0) { self.syms.items[symidx_and_submap.sym_index] = aout.Sym.undefined_symbol; - self.syms_index_free_list.append(self.base.allocator, symidx_and_submap.sym_index) catch {}; - submap.deinit(self.base.allocator); + self.syms_index_free_list.append(gpa, symidx_and_submap.sym_index) catch {}; + submap.deinit(gpa); } } else { if (self.data_decl_table.fetchSwapRemove(decl_index)) |removed_entry| { - self.base.allocator.free(removed_entry.value); + gpa.free(removed_entry.value); } } if (self.decls.fetchRemove(decl_index)) |const_kv| { @@ -1047,35 +1076,36 @@ pub fn freeDecl(self: *Plan9, decl_index: InternPool.DeclIndex) void { const atom = self.getAtom(kv.value.index); if (atom.got_index) |i| { // TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length - self.got_index_free_list.append(self.base.allocator, i) catch {}; + self.got_index_free_list.append(gpa, i) catch {}; } if (atom.sym_index) |i| { - self.syms_index_free_list.append(self.base.allocator, i) catch {}; + self.syms_index_free_list.append(gpa, i) catch {}; self.syms.items[i] = aout.Sym.undefined_symbol; } - kv.value.exports.deinit(self.base.allocator); + kv.value.exports.deinit(gpa); } self.freeUnnamedConsts(decl_index); { const atom_index = self.decls.get(decl_index).?.index; const relocs = self.relocs.getPtr(atom_index) orelse return; - relocs.clearAndFree(self.base.allocator); + relocs.clearAndFree(gpa); assert(self.relocs.remove(atom_index)); } } fn freeUnnamedConsts(self: *Plan9, decl_index: InternPool.DeclIndex) void { + const gpa = self.base.comp.gpa; const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; for (unnamed_consts.items) |atom_idx| { const atom = self.getAtom(atom_idx); - self.base.allocator.free(self.syms.items[atom.sym_index.?].name); + gpa.free(self.syms.items[atom.sym_index.?].name); self.syms.items[atom.sym_index.?] = aout.Sym.undefined_symbol; - self.syms_index_free_list.append(self.base.allocator, atom.sym_index.?) catch {}; + self.syms_index_free_list.append(gpa, atom.sym_index.?) catch {}; } - unnamed_consts.clearAndFree(self.base.allocator); + unnamed_consts.clearAndFree(gpa); } fn createAtom(self: *Plan9) !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.* = .{ @@ -1089,7 +1119,8 @@ fn createAtom(self: *Plan9) !Atom.Index { } pub fn seeDecl(self: *Plan9, 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 index = try self.createAtom(); self.getAtomPtr(index).got_index = self.allocateGotIndex(); @@ -1100,7 +1131,7 @@ pub fn seeDecl(self: *Plan9, decl_index: InternPool.DeclIndex) !Atom.Index { } const atom_idx = gop.value_ptr.index; // handle externs here because they might not get updateDecl called on them - const mod = self.base.options.module.?; + const mod = self.base.comp.module.?; const decl = mod.declPtr(decl_index); const name = mod.intern_pool.stringToSlice(decl.name); if (decl.isExtern(mod)) { @@ -1134,7 +1165,8 @@ pub fn updateExports( } pub fn getOrCreateAtomForLazySymbol(self: *Plan9, sym: File.LazySymbol) !Atom.Index { - const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(self.base.options.module.?)); + const gpa = self.base.comp.gpa; + const gop = try self.lazy_syms.getOrPut(gpa, sym.getDecl(self.base.comp.module.?)); errdefer _ = if (!gop.found_existing) self.lazy_syms.pop(); if (!gop.found_existing) gop.value_ptr.* = .{}; @@ -1153,15 +1185,15 @@ pub fn getOrCreateAtomForLazySymbol(self: *Plan9, sym: File.LazySymbol) !Atom.In _ = try self.getAtomPtr(atom).getOrCreateSymbolTableEntry(self); _ = self.getAtomPtr(atom).getOrCreateOffsetTableEntry(self); // anyerror needs to be deferred until flushModule - if (sym.getDecl(self.base.options.module.?) != .none) { + if (sym.getDecl(self.base.comp.module.?) != .none) { try self.updateLazySymbolAtom(sym, atom); } return atom; } fn updateLazySymbolAtom(self: *Plan9, sym: File.LazySymbol, atom_index: Atom.Index) !void { - const gpa = self.base.allocator; - const mod = self.base.options.module.?; + const gpa = self.base.comp.gpa; + const mod = self.base.comp.module.?; var required_alignment: InternPool.Alignment = .none; var code_buffer = std.ArrayList(u8).init(gpa); @@ -1206,8 +1238,8 @@ fn updateLazySymbolAtom(self: *Plan9, sym: File.LazySymbol, atom_index: Atom.Ind }, }; // duped_code is freed when the atom is freed - const duped_code = try self.base.allocator.dupe(u8, code); - errdefer self.base.allocator.free(duped_code); + const duped_code = try gpa.dupe(u8, code); + errdefer gpa.free(duped_code); self.getAtomPtr(atom_index).code = .{ .code_ptr = duped_code.ptr, .other = .{ .code_len = duped_code.len }, @@ -1215,13 +1247,13 @@ fn updateLazySymbolAtom(self: *Plan9, sym: File.LazySymbol, atom_index: Atom.Ind } pub fn deinit(self: *Plan9) void { - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; { var it = self.relocs.valueIterator(); while (it.next()) |relocs| { - relocs.deinit(self.base.allocator); + relocs.deinit(gpa); } - self.relocs.deinit(self.base.allocator); + self.relocs.deinit(gpa); } // free the unnamed consts var it_unc = self.unnamed_const_atoms.iterator(); @@ -1280,24 +1312,39 @@ pub fn deinit(self: *Plan9) void { } } -pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Plan9 { - if (options.use_llvm) - return error.LLVMBackendDoesNotSupportPlan9; - assert(options.target.ofmt == .plan9); - - const self = try createEmpty(allocator, options); +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*Plan9 { + const target = comp.root_mod.resolved_target.result; + const use_lld = build_options.have_llvm and comp.config.use_lld; + const use_llvm = comp.config.use_llvm; + + assert(!use_llvm); // Caught by Compilation.Config.resolve. + assert(!use_lld); // Caught by Compilation.Config.resolve. + assert(target.ofmt == .plan9); + + const self = try createEmpty(arena, comp, emit, options); errdefer self.base.destroy(); - const file = try options.emit.?.directory.handle.createFile(sub_path, .{ + const file = try emit.directory.handle.createFile(emit.sub_path, .{ .read = true, - .mode = link.determineMode(options), + .mode = link.File.determineMode( + use_lld, + comp.config.output_mode, + comp.config.link_mode, + ), }); errdefer file.close(); self.base.file = file; - self.bases = defaultBaseAddrs(options.target.cpu.arch); + self.bases = defaultBaseAddrs(target.cpu.arch); - try self.syms.appendSlice(self.base.allocator, &.{ + const gpa = comp.gpa; + + try self.syms.appendSlice(gpa, &.{ // we include the global offset table to make it easier for debugging .{ .value = self.getAddr(0, .d), // the global offset table starts at 0 @@ -1323,7 +1370,7 @@ pub fn writeSym(self: *Plan9, w: anytype, sym: aout.Sym) !void { } pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { - const mod = self.base.options.module.?; + const mod = self.base.comp.module.?; const ip = &mod.intern_pool; const writer = buf.writer(); // write __GOT @@ -1349,7 +1396,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { const atom = self.getAtom(decl_metadata.index); const sym = self.syms.items[atom.sym_index.?]; try self.writeSym(writer, sym); - if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| { + if (self.base.comp.module.?.decl_exports.get(decl_index)) |exports| { for (exports.items) |e| if (decl_metadata.getExport(self, ip.stringToSlice(e.opts.name))) |exp_i| { try self.writeSym(writer, self.syms.items[exp_i]); }; @@ -1396,7 +1443,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { const atom = self.getAtom(decl_metadata.index); const sym = self.syms.items[atom.sym_index.?]; try self.writeSym(writer, sym); - if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| { + if (self.base.comp.module.?.decl_exports.get(decl_index)) |exports| { for (exports.items) |e| if (decl_metadata.getExport(self, ip.stringToSlice(e.opts.name))) |exp_i| { const s = self.syms.items[exp_i]; if (mem.eql(u8, s.name, "_start")) @@ -1439,7 +1486,7 @@ pub fn getDeclVAddr( decl_index: InternPool.DeclIndex, reloc_info: link.File.RelocInfo, ) !u64 { - const mod = self.base.options.module.?; + const mod = self.base.comp.module.?; const decl = mod.declPtr(decl_index); log.debug("getDeclVAddr for {s}", .{mod.intern_pool.stringToSlice(decl.name)}); if (decl.isExtern(mod)) { @@ -1480,7 +1527,13 @@ pub fn getDeclVAddr( return undefined; } -pub fn lowerAnonDecl(self: *Plan9, decl_val: InternPool.Index, src_loc: Module.SrcLoc) !codegen.Result { +pub fn lowerAnonDecl( + self: *Plan9, + decl_val: InternPool.Index, + explicit_alignment: InternPool.Alignment, + src_loc: Module.SrcLoc, +) !codegen.Result { + _ = explicit_alignment; // This is basically the same as lowerUnnamedConst. // example: // const ty = mod.intern_pool.typeOf(decl_val).toType(); @@ -1490,9 +1543,9 @@ pub fn lowerAnonDecl(self: *Plan9, decl_val: InternPool.Index, src_loc: Module.S // be used by more than one function, however, its address is being used so we need // to put it in some location. // ... - const gpa = self.base.allocator; + const gpa = self.base.comp.gpa; const gop = try self.anon_decls.getOrPut(gpa, decl_val); - const mod = self.base.options.module.?; + const mod = self.base.comp.module.?; if (!gop.found_existing) { const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val)); const val = Value.fromInterned(decl_val); @@ -1538,11 +1591,12 @@ pub fn getAnonDeclVAddr(self: *Plan9, decl_val: InternPool.Index, reloc_info: li } pub fn addReloc(self: *Plan9, parent_index: Atom.Index, reloc: Reloc) !void { - const gop = try self.relocs.getOrPut(self.base.allocator, parent_index); + const gpa = self.base.comp.gpa; + const gop = try self.relocs.getOrPut(gpa, parent_index); if (!gop.found_existing) { gop.value_ptr.* = .{}; } - try gop.value_ptr.append(self.base.allocator, reloc); + try gop.value_ptr.append(gpa, reloc); } pub fn getAtom(self: *const Plan9, index: Atom.Index) Atom { diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index ac36a391ecdd..7b66d914bf1b 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -24,7 +24,6 @@ const SpirV = @This(); const std = @import("std"); const Allocator = std.mem.Allocator; -const ArenaAllocator = std.heap.ArenaAllocator; const assert = std.debug.assert; const log = std.log.scoped(.link); @@ -47,48 +46,73 @@ base: link.File, object: codegen.Object, -pub fn createEmpty(gpa: Allocator, options: link.Options) !*SpirV { - const self = try gpa.create(SpirV); +pub const base_tag: link.File.Tag = .spirv; + +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*SpirV { + const gpa = comp.gpa; + const target = comp.root_mod.resolved_target.result; + + const self = try arena.create(SpirV); self.* = .{ .base = .{ .tag = .spirv, - .options = options, + .comp = comp, + .emit = emit, + .gc_sections = options.gc_sections orelse false, + .print_gc_sections = options.print_gc_sections, + .stack_size = options.stack_size orelse 0, + .allow_shlib_undefined = options.allow_shlib_undefined orelse false, .file = null, - .allocator = gpa, + .disable_lld_caching = options.disable_lld_caching, + .build_id = options.build_id, + .rpath_list = options.rpath_list, }, .object = codegen.Object.init(gpa), }; errdefer self.deinit(); - // TODO: Figure out where to put all of these - switch (options.target.cpu.arch) { + switch (target.cpu.arch) { .spirv32, .spirv64 => {}, - else => return error.TODOArchNotSupported, + else => unreachable, // Caught by Compilation.Config.resolve. } - switch (options.target.os.tag) { + switch (target.os.tag) { .opencl, .glsl450, .vulkan => {}, - else => return error.TODOOsNotSupported, + else => unreachable, // Caught by Compilation.Config.resolve. } - if (options.target.abi != .none) { - return error.TODOAbiNotSupported; - } + assert(target.abi != .none); // Caught by Compilation.Config.resolve. return self; } -pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*SpirV { - assert(options.target.ofmt == .spirv); - - if (options.use_llvm) return error.LLVM_BackendIsTODO_ForSpirV; // TODO: LLVM Doesn't support SpirV at all. - if (options.use_lld) return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all. - - const spirv = try createEmpty(allocator, options); +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*SpirV { + const target = comp.root_mod.resolved_target.result; + const use_lld = build_options.have_llvm and comp.config.use_lld; + const use_llvm = comp.config.use_llvm; + + assert(!use_llvm); // Caught by Compilation.Config.resolve. + assert(!use_lld); // Caught by Compilation.Config.resolve. + assert(target.ofmt == .spirv); // Caught by Compilation.Config.resolve. + + const spirv = try createEmpty(arena, comp, emit, options); errdefer spirv.base.destroy(); // TODO: read the file and keep valid parts instead of truncating - const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true }); + const file = try emit.directory.handle.createFile(emit.sub_path, .{ + .truncate = true, + .read = true, + }); spirv.base.file = file; return spirv; } @@ -149,19 +173,17 @@ pub fn freeDecl(self: *SpirV, decl_index: InternPool.DeclIndex) void { _ = decl_index; } -pub fn flush(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { - if (build_options.have_llvm and self.base.options.use_lld) { - return error.LLD_LinkingIsTODO_ForSpirV; // TODO: LLD Doesn't support SpirV at all. - } else { - return self.flushModule(comp, prog_node); - } +pub fn flush(self: *SpirV, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { + return self.flushModule(arena, prog_node); } -pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { +pub fn flushModule(self: *SpirV, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { if (build_options.skip_non_native) { @panic("Attempted to compile for architecture that was disabled by build configuration"); } + _ = arena; // Has the same lifetime as the call to Compilation.update. + const tracy = trace(@src()); defer tracy.end(); @@ -171,7 +193,10 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No const spv = &self.object.spv; + const comp = self.base.comp; + const gpa = comp.gpa; const target = comp.getTarget(); + try writeCapabilities(spv, target); try writeMemoryModel(spv, target); @@ -183,7 +208,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No defer error_info.deinit(); try error_info.appendSlice("zig_errors"); - const module = self.base.options.module.?; + const module = self.base.comp.module.?; for (module.global_error_set.keys()) |name_nts| { const name = module.intern_pool.stringToSlice(name_nts); // Errors can contain pretty much any character - to encode them in a string we must escape @@ -191,11 +216,11 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No // name if it contains no strange characters is nice for debugging. URI encoding fits the bill. // We're using : as separator, which is a reserved character. - const escaped_name = try std.Uri.escapeString(self.base.allocator, name); - defer self.base.allocator.free(escaped_name); + const escaped_name = try std.Uri.escapeString(gpa, name); + defer gpa.free(escaped_name); try error_info.writer().print(":{s}", .{escaped_name}); } - try spv.sections.debug_strings.emit(spv.gpa, .OpSourceExtension, .{ + try spv.sections.debug_strings.emit(gpa, .OpSourceExtension, .{ .extension = error_info.items, }); @@ -203,6 +228,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No } fn writeCapabilities(spv: *SpvModule, target: std.Target) !void { + const gpa = spv.gpa; // TODO: Integrate with a hypothetical feature system const caps: []const spec.Capability = switch (target.os.tag) { .opencl => &.{ .Kernel, .Addresses, .Int8, .Int16, .Int64, .Float64, .Float16, .GenericPointer }, @@ -212,13 +238,15 @@ fn writeCapabilities(spv: *SpvModule, target: std.Target) !void { }; for (caps) |cap| { - try spv.sections.capabilities.emit(spv.gpa, .OpCapability, .{ + try spv.sections.capabilities.emit(gpa, .OpCapability, .{ .capability = cap, }); } } fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void { + const gpa = spv.gpa; + const addressing_model = switch (target.os.tag) { .opencl => switch (target.cpu.arch) { .spirv32 => spec.AddressingModel.Physical32, @@ -237,7 +265,7 @@ fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void { }; // TODO: Put this in a proper section. - try spv.sections.extensions.emit(spv.gpa, .OpMemoryModel, .{ + try spv.sections.extensions.emit(gpa, .OpMemoryModel, .{ .addressing_model = addressing_model, .memory_model = memory_model, }); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 513922f24777..185d80258882 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -37,6 +37,12 @@ pub const Relocation = types.Relocation; pub const base_tag: link.File.Tag = .wasm; base: link.File, +entry_name: ?[]const u8, +import_symbols: bool, +export_symbol_names: []const []const u8, +global_base: ?u64, +initial_memory: ?u64, +max_memory: ?u64, /// Output name of the file name: []const u8, /// If this is not null, an object file is created by LLVM and linked with LLD afterwards. @@ -191,6 +197,9 @@ synthetic_functions: std.ArrayListUnmanaged(Atom.Index) = .{}, /// Map for storing anonymous declarations. Each anonymous decl maps to its Atom's index. anon_decls: std.AutoArrayHashMapUnmanaged(InternPool.Index, Atom.Index) = .{}, +import_table: bool, +export_table: bool, + pub const Alignment = types.Alignment; pub const Segment = struct { @@ -363,63 +372,126 @@ pub const StringTable = struct { } }; -pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Wasm { - assert(options.target.ofmt == .wasm); +pub fn open( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*Wasm { + // TODO: restore saved linker state, don't truncate the file, and + // participate in incremental compilation. + return createEmpty(arena, comp, emit, options); +} - if (options.use_llvm and options.use_lld) { - return createEmpty(allocator, options); +pub fn createEmpty( + arena: Allocator, + comp: *Compilation, + emit: Compilation.Emit, + options: link.File.OpenOptions, +) !*Wasm { + const gpa = comp.gpa; + const target = comp.root_mod.resolved_target.result; + assert(target.ofmt == .wasm); + + const use_lld = build_options.have_llvm and comp.config.use_lld; + const use_llvm = comp.config.use_llvm; + const output_mode = comp.config.output_mode; + const shared_memory = comp.config.shared_memory; + const wasi_exec_model = comp.config.wasi_exec_model; + + // If using LLD to link, this code should produce an object file so that it + // can be passed to LLD. + // If using LLVM to generate the object file for the zig compilation unit, + // we need a place to put the object file so that it can be subsequently + // handled. + const zcu_object_sub_path = if (!use_lld and !use_llvm) + null + else + try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path}); + + const wasm = try arena.create(Wasm); + wasm.* = .{ + .base = .{ + .tag = .wasm, + .comp = comp, + .emit = emit, + .zcu_object_sub_path = zcu_object_sub_path, + .gc_sections = options.gc_sections orelse (output_mode != .Obj), + .print_gc_sections = options.print_gc_sections, + .stack_size = options.stack_size orelse std.wasm.page_size * 16, // 1MB + .allow_shlib_undefined = options.allow_shlib_undefined orelse false, + .file = null, + .disable_lld_caching = options.disable_lld_caching, + .build_id = options.build_id, + .rpath_list = options.rpath_list, + }, + .name = undefined, + .import_table = options.import_table, + .export_table = options.export_table, + .import_symbols = options.import_symbols, + .export_symbol_names = options.export_symbol_names, + .global_base = options.global_base, + .initial_memory = options.initial_memory, + .max_memory = options.max_memory, + + .entry_name = switch (options.entry) { + .disabled => null, + .default => if (output_mode != .Exe) null else defaultEntrySymbolName(wasi_exec_model), + .enabled => defaultEntrySymbolName(wasi_exec_model), + .named => |name| name, + }, + }; + if (use_llvm and comp.config.have_zcu) { + wasm.llvm_object = try LlvmObject.create(arena, comp); } + errdefer wasm.base.destroy(); - const wasm_bin = try createEmpty(allocator, options); - errdefer wasm_bin.base.destroy(); - - // We are not using LLD at this point, so ensure we set the intermediary basename - if (build_options.have_llvm and options.use_llvm and options.module != null) { - // 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. - wasm_bin.base.intermediary_basename = try std.fmt.allocPrint(allocator, "{s}{s}", .{ - options.emit.?.sub_path, options.target.ofmt.fileExt(options.target.cpu.arch), - }); + if (use_lld and (use_llvm or !comp.config.have_zcu)) { + // LLVM emits the object file (if any); LLD links it into the final product. + return wasm; } - // TODO: read the file and keep valid parts instead of truncating - const file = try options.emit.?.directory.handle.createFile(sub_path, .{ + // What path should this Wasm linker code output to? + // If using LLD to link, this code should produce an object file so that it + // can be passed to LLD. + const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path; + + const file = try emit.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = if (fs.has_executable_bit) - if (options.target.os.tag == .wasi and options.output_mode == .Exe) + if (target.os.tag == .wasi and output_mode == .Exe) fs.File.default_mode | 0b001_000_000 else fs.File.default_mode else 0, }); - wasm_bin.base.file = file; - wasm_bin.name = sub_path; + wasm.base.file = file; + wasm.name = sub_path; // create stack pointer symbol { - const loc = try wasm_bin.createSyntheticSymbol("__stack_pointer", .global); - const symbol = loc.getSymbol(wasm_bin); + const loc = try wasm.createSyntheticSymbol("__stack_pointer", .global); + const symbol = loc.getSymbol(wasm); // For object files we will import the stack pointer symbol - if (options.output_mode == .Obj) { + if (output_mode == .Obj) { symbol.setUndefined(true); - symbol.index = @as(u32, @intCast(wasm_bin.imported_globals_count)); - wasm_bin.imported_globals_count += 1; - try wasm_bin.imports.putNoClobber( - allocator, + symbol.index = @intCast(wasm.imported_globals_count); + wasm.imported_globals_count += 1; + try wasm.imports.putNoClobber( + gpa, loc, .{ - .module_name = try wasm_bin.string_table.put(allocator, wasm_bin.host_name), + .module_name = try wasm.string_table.put(gpa, wasm.host_name), .name = symbol.name, .kind = .{ .global = .{ .valtype = .i32, .mutable = true } }, }, ); } else { - symbol.index = @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len); + symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); - const global = try wasm_bin.wasm_globals.addOne(allocator); + const global = try wasm.wasm_globals.addOne(gpa); global.* = .{ .global_type = .{ .valtype = .i32, @@ -432,25 +504,25 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option // create indirect function pointer symbol { - const loc = try wasm_bin.createSyntheticSymbol("__indirect_function_table", .table); - const symbol = loc.getSymbol(wasm_bin); + const loc = try wasm.createSyntheticSymbol("__indirect_function_table", .table); + const symbol = loc.getSymbol(wasm); const table: std.wasm.Table = .{ .limits = .{ .flags = 0, .min = 0, .max = undefined }, // will be overwritten during `mapFunctionTable` .reftype = .funcref, }; - if (options.output_mode == .Obj or options.import_table) { + if (output_mode == .Obj or options.import_table) { symbol.setUndefined(true); - symbol.index = @intCast(wasm_bin.imported_tables_count); - wasm_bin.imported_tables_count += 1; - try wasm_bin.imports.put(allocator, loc, .{ - .module_name = try wasm_bin.string_table.put(allocator, wasm_bin.host_name), + symbol.index = @intCast(wasm.imported_tables_count); + wasm.imported_tables_count += 1; + try wasm.imports.put(gpa, loc, .{ + .module_name = try wasm.string_table.put(gpa, wasm.host_name), .name = symbol.name, .kind = .{ .table = table }, }); } else { - symbol.index = @as(u32, @intCast(wasm_bin.imported_tables_count + wasm_bin.tables.items.len)); - try wasm_bin.tables.append(allocator, table); - if (options.export_table) { + symbol.index = @as(u32, @intCast(wasm.imported_tables_count + wasm.tables.items.len)); + try wasm.tables.append(gpa, table); + if (wasm.export_table) { symbol.setFlag(.WASM_SYM_EXPORTED); } else { symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); @@ -460,8 +532,8 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option // create __wasm_call_ctors { - const loc = try wasm_bin.createSyntheticSymbol("__wasm_call_ctors", .function); - const symbol = loc.getSymbol(wasm_bin); + const loc = try wasm.createSyntheticSymbol("__wasm_call_ctors", .function); + const symbol = loc.getSymbol(wasm); symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); // we do not know the function index until after we merged all sections. // Therefore we set `symbol.index` and create its corresponding references @@ -469,90 +541,68 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option } // shared-memory symbols for TLS support - if (wasm_bin.base.options.shared_memory) { + if (shared_memory) { { - const loc = try wasm_bin.createSyntheticSymbol("__tls_base", .global); - const symbol = loc.getSymbol(wasm_bin); + const loc = try wasm.createSyntheticSymbol("__tls_base", .global); + const symbol = loc.getSymbol(wasm); symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); - symbol.index = @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len); - try wasm_bin.wasm_globals.append(wasm_bin.base.allocator, .{ + symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); + try wasm.wasm_globals.append(gpa, .{ .global_type = .{ .valtype = .i32, .mutable = true }, .init = .{ .i32_const = undefined }, }); } { - const loc = try wasm_bin.createSyntheticSymbol("__tls_size", .global); - const symbol = loc.getSymbol(wasm_bin); + const loc = try wasm.createSyntheticSymbol("__tls_size", .global); + const symbol = loc.getSymbol(wasm); symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); - symbol.index = @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len); - try wasm_bin.wasm_globals.append(wasm_bin.base.allocator, .{ + symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); + try wasm.wasm_globals.append(gpa, .{ .global_type = .{ .valtype = .i32, .mutable = false }, .init = .{ .i32_const = undefined }, }); } { - const loc = try wasm_bin.createSyntheticSymbol("__tls_align", .global); - const symbol = loc.getSymbol(wasm_bin); + const loc = try wasm.createSyntheticSymbol("__tls_align", .global); + const symbol = loc.getSymbol(wasm); symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); - symbol.index = @intCast(wasm_bin.imported_globals_count + wasm_bin.wasm_globals.items.len); - try wasm_bin.wasm_globals.append(wasm_bin.base.allocator, .{ + symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); + try wasm.wasm_globals.append(gpa, .{ .global_type = .{ .valtype = .i32, .mutable = false }, .init = .{ .i32_const = undefined }, }); } { - const loc = try wasm_bin.createSyntheticSymbol("__wasm_init_tls", .function); - const symbol = loc.getSymbol(wasm_bin); + const loc = try wasm.createSyntheticSymbol("__wasm_init_tls", .function); + const symbol = loc.getSymbol(wasm); symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); } } - // if (!options.strip and options.module != null) { - // wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, .dwarf32); - // try wasm_bin.initDebugSections(); - // } - - return wasm_bin; -} - -pub fn createEmpty(gpa: Allocator, options: link.Options) !*Wasm { - const wasm = try gpa.create(Wasm); - errdefer gpa.destroy(wasm); - wasm.* = .{ - .base = .{ - .tag = .wasm, - .options = options, - .file = null, - .allocator = gpa, - }, - .name = undefined, - }; - - if (options.use_llvm) { - wasm.llvm_object = try LlvmObject.create(gpa, options); - } return wasm; } /// For a given name, creates a new global synthetic symbol. /// Leaves index undefined and the default flags (0). fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !SymbolLoc { - const name_offset = try wasm.string_table.put(wasm.base.allocator, name); + const gpa = wasm.base.comp.gpa; + const name_offset = try wasm.string_table.put(gpa, name); return wasm.createSyntheticSymbolOffset(name_offset, tag); } fn createSyntheticSymbolOffset(wasm: *Wasm, name_offset: u32, tag: Symbol.Tag) !SymbolLoc { const sym_index = @as(u32, @intCast(wasm.symbols.items.len)); const loc: SymbolLoc = .{ .index = sym_index, .file = null }; - try wasm.symbols.append(wasm.base.allocator, .{ + const gpa = wasm.base.comp.gpa; + try wasm.symbols.append(gpa, .{ .name = name_offset, .flags = 0, .tag = tag, .index = undefined, .virtual_address = undefined, }); - try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, loc, {}); - try wasm.globals.put(wasm.base.allocator, name_offset, loc); + try wasm.resolved_symbols.putNoClobber(gpa, loc, {}); + try wasm.globals.put(gpa, name_offset, loc); return loc; } @@ -589,12 +639,13 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool { const file = try fs.cwd().openFile(path, .{}); errdefer file.close(); - var object = Object.create(wasm.base.allocator, file, path, null) catch |err| switch (err) { + const gpa = wasm.base.comp.gpa; + var object = Object.create(gpa, file, path, null) catch |err| switch (err) { error.InvalidMagicByte, error.NotObjectFile => return false, else => |e| return e, }; - errdefer object.deinit(wasm.base.allocator); - try wasm.objects.append(wasm.base.allocator, object); + errdefer object.deinit(gpa); + try wasm.objects.append(gpa, object); return true; } @@ -602,27 +653,29 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool { /// When the index was not found, a new `Atom` will be created, and its index will be returned. /// The newly created Atom is empty with default fields as specified by `Atom.empty`. pub fn getOrCreateAtomForDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) !Atom.Index { - const gop = try wasm.decls.getOrPut(wasm.base.allocator, decl_index); + const gpa = wasm.base.comp.gpa; + const gop = try wasm.decls.getOrPut(gpa, decl_index); if (!gop.found_existing) { const atom_index = try wasm.createAtom(); gop.value_ptr.* = atom_index; const atom = wasm.getAtom(atom_index); const symbol = atom.symbolLoc().getSymbol(wasm); - const mod = wasm.base.options.module.?; + const mod = wasm.base.comp.module.?; const decl = mod.declPtr(decl_index); const full_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); - symbol.name = try wasm.string_table.put(wasm.base.allocator, full_name); + symbol.name = try wasm.string_table.put(gpa, full_name); } return gop.value_ptr.*; } /// Creates a new empty `Atom` and returns its `Atom.Index` fn createAtom(wasm: *Wasm) !Atom.Index { - const index = @as(Atom.Index, @intCast(wasm.managed_atoms.items.len)); - const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); + const gpa = wasm.base.comp.gpa; + const index: Atom.Index = @intCast(wasm.managed_atoms.items.len); + const atom = try wasm.managed_atoms.addOne(gpa); atom.* = Atom.empty; atom.sym_index = try wasm.allocateSymbol(); - try wasm.symbol_atom.putNoClobber(wasm.base.allocator, .{ .file = null, .index = atom.sym_index }, index); + try wasm.symbol_atom.putNoClobber(gpa, .{ .file = null, .index = atom.sym_index }, index); return index; } @@ -644,6 +697,8 @@ pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom { /// When false, it will only link with object files that contain symbols that /// are referenced by other object files or Zig code. fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { + const gpa = wasm.base.comp.gpa; + const file = try fs.cwd().openFile(path, .{}); errdefer file.close(); @@ -651,25 +706,25 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { .file = file, .name = path, }; - archive.parse(wasm.base.allocator) catch |err| switch (err) { + archive.parse(gpa) catch |err| switch (err) { error.EndOfStream, error.NotArchive => { - archive.deinit(wasm.base.allocator); + archive.deinit(gpa); return false; }, else => |e| return e, }; if (!force_load) { - errdefer archive.deinit(wasm.base.allocator); - try wasm.archives.append(wasm.base.allocator, archive); + errdefer archive.deinit(gpa); + try wasm.archives.append(gpa, archive); return true; } - defer archive.deinit(wasm.base.allocator); + defer archive.deinit(gpa); // In this case we must force link all embedded object files within the archive // We loop over all symbols, and then group them by offset as the offset // notates where the object file starts. - var offsets = std.AutoArrayHashMap(u32, void).init(wasm.base.allocator); + var offsets = std.AutoArrayHashMap(u32, void).init(gpa); defer offsets.deinit(); for (archive.toc.values()) |symbol_offsets| { for (symbol_offsets.items) |sym_offset| { @@ -678,8 +733,8 @@ fn parseArchive(wasm: *Wasm, path: []const u8, force_load: bool) !bool { } for (offsets.keys()) |file_offset| { - const object = try wasm.objects.addOne(wasm.base.allocator); - object.* = try archive.parseObject(wasm.base.allocator, file_offset); + const object = try wasm.objects.addOne(gpa); + object.* = try archive.parseObject(gpa, file_offset); } return true; @@ -695,6 +750,7 @@ fn requiresTLSReloc(wasm: *const Wasm) bool { } fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { + const gpa = wasm.base.comp.gpa; const object: Object = wasm.objects.items[object_index]; log.debug("Resolving symbols in object: '{s}'", .{object.name}); @@ -708,7 +764,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { if (mem.eql(u8, sym_name, "__indirect_function_table")) { continue; } - const sym_name_index = try wasm.string_table.put(wasm.base.allocator, sym_name); + const sym_name_index = try wasm.string_table.put(gpa, sym_name); if (symbol.isLocal()) { if (symbol.isUndefined()) { @@ -716,17 +772,17 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { log.err(" symbol '{s}' defined in '{s}'", .{ sym_name, object.name }); return error.UndefinedLocal; } - try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, location, {}); + try wasm.resolved_symbols.putNoClobber(gpa, location, {}); continue; } - const maybe_existing = try wasm.globals.getOrPut(wasm.base.allocator, sym_name_index); + const maybe_existing = try wasm.globals.getOrPut(gpa, sym_name_index); if (!maybe_existing.found_existing) { maybe_existing.value_ptr.* = location; - try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, location, {}); + try wasm.resolved_symbols.putNoClobber(gpa, location, {}); if (symbol.isUndefined()) { - try wasm.undefs.putNoClobber(wasm.base.allocator, sym_name_index, location); + try wasm.undefs.putNoClobber(gpa, sym_name_index, location); } continue; } @@ -753,7 +809,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { return error.SymbolCollision; } - try wasm.discarded.put(wasm.base.allocator, location, existing_loc); + try wasm.discarded.put(gpa, location, existing_loc); continue; // Do not overwrite defined symbols with undefined symbols } @@ -791,7 +847,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { } // both undefined so skip overwriting existing symbol and discard the new symbol - try wasm.discarded.put(wasm.base.allocator, location, existing_loc); + try wasm.discarded.put(gpa, location, existing_loc); continue; } @@ -822,7 +878,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { // symbol is weak and the new one isn't, in which case we *do* overwrite it. if (existing_sym.isWeak() and symbol.isWeak()) blk: { if (existing_sym.isUndefined() and !symbol.isUndefined()) break :blk; - try wasm.discarded.put(wasm.base.allocator, location, existing_loc); + try wasm.discarded.put(gpa, location, existing_loc); continue; } @@ -830,10 +886,10 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { log.debug("Overwriting symbol '{s}'", .{sym_name}); log.debug(" old definition in '{s}'", .{existing_file_path}); log.debug(" new definition in '{s}'", .{object.name}); - try wasm.discarded.putNoClobber(wasm.base.allocator, existing_loc, location); + try wasm.discarded.putNoClobber(gpa, existing_loc, location); maybe_existing.value_ptr.* = location; - try wasm.globals.put(wasm.base.allocator, sym_name_index, location); - try wasm.resolved_symbols.put(wasm.base.allocator, location, {}); + try wasm.globals.put(gpa, sym_name_index, location); + try wasm.resolved_symbols.put(gpa, location, {}); assert(wasm.resolved_symbols.swapRemove(existing_loc)); if (existing_sym.isUndefined()) { _ = wasm.undefs.swapRemove(sym_name_index); @@ -842,6 +898,7 @@ fn resolveSymbolsInObject(wasm: *Wasm, object_index: u16) !void { } fn resolveSymbolsInArchives(wasm: *Wasm) !void { + const gpa = wasm.base.comp.gpa; if (wasm.archives.items.len == 0) return; log.debug("Resolving symbols in archives", .{}); @@ -860,9 +917,9 @@ fn resolveSymbolsInArchives(wasm: *Wasm) !void { // Symbol is found in unparsed object file within current archive. // Parse object and and resolve symbols again before we check remaining // undefined symbols. - const object_file_index = @as(u16, @intCast(wasm.objects.items.len)); - const object = try archive.parseObject(wasm.base.allocator, offset.items[0]); - try wasm.objects.append(wasm.base.allocator, object); + const object_file_index: u16 = @intCast(wasm.objects.items.len); + const object = try archive.parseObject(gpa, offset.items[0]); + try wasm.objects.append(gpa, object); try wasm.resolveSymbolsInObject(object_file_index); // continue loop for any remaining undefined symbols that still exist @@ -880,6 +937,11 @@ fn writeI32Const(writer: anytype, val: u32) !void { } fn setupInitMemoryFunction(wasm: *Wasm) !void { + const comp = wasm.base.comp; + const gpa = comp.gpa; + const shared_memory = comp.config.shared_memory; + const import_memory = comp.config.import_memory; + // Passive segments are used to avoid memory being reinitialized on each // thread's instantiation. These passive segments are initialized and // dropped in __wasm_init_memory, which is registered as the start function @@ -889,21 +951,21 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { return; } - const flag_address: u32 = if (wasm.base.options.shared_memory) address: { + const flag_address: u32 = if (shared_memory) address: { // when we have passive initialization segments and shared memory // `setupMemory` will create this symbol and set its virtual address. const loc = wasm.findGlobalSymbol("__wasm_init_memory_flag").?; break :address loc.getSymbol(wasm).virtual_address; } else 0; - var function_body = std.ArrayList(u8).init(wasm.base.allocator); + var function_body = std.ArrayList(u8).init(gpa); defer function_body.deinit(); const writer = function_body.writer(); // we have 0 locals try leb.writeULEB128(writer, @as(u32, 0)); - if (wasm.base.options.shared_memory) { + if (shared_memory) { // destination blocks // based on values we jump to corresponding label try writer.writeByte(std.wasm.opcode(.block)); // $drop @@ -937,14 +999,14 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { var segment_index: u32 = 0; while (it.next()) |entry| : (segment_index += 1) { const segment: Segment = wasm.segments.items[entry.value_ptr.*]; - if (segment.needsPassiveInitialization(wasm.base.options.import_memory, entry.key_ptr.*)) { + if (segment.needsPassiveInitialization(import_memory, entry.key_ptr.*)) { // For passive BSS segments we can simple issue a memory.fill(0). // For non-BSS segments we do a memory.init. Both these // instructions take as their first argument the destination // address. try writeI32Const(writer, segment.offset); - if (wasm.base.options.shared_memory and std.mem.eql(u8, entry.key_ptr.*, ".tdata")) { + if (shared_memory and std.mem.eql(u8, entry.key_ptr.*, ".tdata")) { // When we initialize the TLS segment we also set the `__tls_base` // global. This allows the runtime to use this static copy of the // TLS data for the first/main thread. @@ -969,7 +1031,7 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { } } - if (wasm.base.options.shared_memory) { + if (shared_memory) { // we set the init memory flag to value '2' try writeI32Const(writer, flag_address); try writeI32Const(writer, 2); @@ -1012,12 +1074,12 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { while (it.next()) |entry| : (segment_index += 1) { const name = entry.key_ptr.*; const segment: Segment = wasm.segments.items[entry.value_ptr.*]; - if (segment.needsPassiveInitialization(wasm.base.options.import_memory, name) and + if (segment.needsPassiveInitialization(import_memory, name) and !std.mem.eql(u8, name, ".bss")) { // The TLS region should not be dropped since its is needed // during the initialization of each thread (__wasm_init_tls). - if (wasm.base.options.shared_memory and std.mem.eql(u8, name, ".tdata")) { + if (shared_memory and std.mem.eql(u8, name, ".tdata")) { continue; } @@ -1040,14 +1102,18 @@ fn setupInitMemoryFunction(wasm: *Wasm) !void { /// Constructs a synthetic function that performs runtime relocations for /// TLS symbols. This function is called by `__wasm_init_tls`. fn setupTLSRelocationsFunction(wasm: *Wasm) !void { + const comp = wasm.base.comp; + const gpa = comp.gpa; + const shared_memory = comp.config.shared_memory; + // When we have TLS GOT entries and shared memory is enabled, // we must perform runtime relocations or else we don't create the function. - if (!wasm.base.options.shared_memory or !wasm.requiresTLSReloc()) { + if (!shared_memory or !wasm.requiresTLSReloc()) { return; } // const loc = try wasm.createSyntheticSymbol("__wasm_apply_global_tls_relocs"); - var function_body = std.ArrayList(u8).init(wasm.base.allocator); + var function_body = std.ArrayList(u8).init(gpa); defer function_body.deinit(); const writer = function_body.writer(); @@ -1086,7 +1152,10 @@ fn validateFeatures( to_emit: *[@typeInfo(types.Feature.Tag).Enum.fields.len]bool, emit_features_count: *u32, ) !void { - const cpu_features = wasm.base.options.target.cpu.features; + const comp = wasm.base.comp; + const target = comp.root_mod.resolved_target.result; + const shared_memory = comp.config.shared_memory; + const cpu_features = target.cpu.features; const infer = cpu_features.isEmpty(); // when the user did not define any features, we infer them from linked objects. const known_features_count = @typeInfo(types.Feature.Tag).Enum.fields.len; @@ -1156,7 +1225,7 @@ fn validateFeatures( return error.InvalidFeatureSet; } - if (wasm.base.options.shared_memory) { + if (shared_memory) { const disallowed_feature = disallowed[@intFromEnum(types.Feature.Tag.shared_mem)]; if (@as(u1, @truncate(disallowed_feature)) != 0) { log.err( @@ -1221,10 +1290,14 @@ fn validateFeatures( /// if one or multiple undefined references exist. When none exist, the symbol will /// not be created, ensuring we don't unneccesarily emit unreferenced symbols. fn resolveLazySymbols(wasm: *Wasm) !void { + const comp = wasm.base.comp; + const gpa = comp.gpa; + const shared_memory = comp.config.shared_memory; + if (wasm.string_table.getOffset("__heap_base")) |name_offset| { if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| { const loc = try wasm.createSyntheticSymbolOffset(name_offset, .data); - try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); + try wasm.discarded.putNoClobber(gpa, kv.value, loc); _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations. } } @@ -1232,21 +1305,21 @@ fn resolveLazySymbols(wasm: *Wasm) !void { if (wasm.string_table.getOffset("__heap_end")) |name_offset| { if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| { const loc = try wasm.createSyntheticSymbolOffset(name_offset, .data); - try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); + try wasm.discarded.putNoClobber(gpa, kv.value, loc); _ = wasm.resolved_symbols.swapRemove(loc); } } - if (!wasm.base.options.shared_memory) { + if (!shared_memory) { if (wasm.string_table.getOffset("__tls_base")) |name_offset| { if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| { const loc = try wasm.createSyntheticSymbolOffset(name_offset, .global); - try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); + try wasm.discarded.putNoClobber(gpa, kv.value, loc); _ = wasm.resolved_symbols.swapRemove(kv.value); const symbol = loc.getSymbol(wasm); symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); symbol.index = @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len); - try wasm.wasm_globals.append(wasm.base.allocator, .{ + try wasm.wasm_globals.append(gpa, .{ .global_type = .{ .valtype = .i32, .mutable = true }, .init = .{ .i32_const = undefined }, }); @@ -1256,7 +1329,7 @@ fn resolveLazySymbols(wasm: *Wasm) !void { if (wasm.string_table.getOffset("__zig_errors_len")) |name_offset| { if (wasm.undefs.fetchSwapRemove(name_offset)) |kv| { const loc = try wasm.createSyntheticSymbolOffset(name_offset, .data); - try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); + try wasm.discarded.putNoClobber(gpa, kv.value, loc); _ = wasm.resolved_symbols.swapRemove(kv.value); } } @@ -1270,8 +1343,9 @@ pub fn findGlobalSymbol(wasm: *Wasm, name: []const u8) ?SymbolLoc { } fn checkUndefinedSymbols(wasm: *const Wasm) !void { - if (wasm.base.options.output_mode == .Obj) return; - if (wasm.base.options.import_symbols) return; + const comp = wasm.base.comp; + if (comp.config.output_mode == .Obj) return; + if (wasm.import_symbols) return; var found_undefined_symbols = false; for (wasm.undefs.values()) |undef| { @@ -1292,8 +1366,8 @@ fn checkUndefinedSymbols(wasm: *const Wasm) !void { } pub fn deinit(wasm: *Wasm) void { - const gpa = wasm.base.allocator; - if (wasm.llvm_object) |llvm_object| llvm_object.destroy(gpa); + const gpa = wasm.base.comp.gpa; + if (wasm.llvm_object) |llvm_object| llvm_object.deinit(); for (wasm.func_types.items) |*func_type| { func_type.deinit(gpa); @@ -1378,7 +1452,9 @@ pub fn deinit(wasm: *Wasm) void { /// Allocates a new symbol and returns its index. /// Will re-use slots when a symbol was freed at an earlier stage. pub fn allocateSymbol(wasm: *Wasm) !u32 { - try wasm.symbols.ensureUnusedCapacity(wasm.base.allocator, 1); + const gpa = wasm.base.comp.gpa; + + try wasm.symbols.ensureUnusedCapacity(gpa, 1); const symbol: Symbol = .{ .name = std.math.maxInt(u32), // will be set after updateDecl as well as during atom creation for decls .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL), @@ -1404,6 +1480,7 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func_index: InternPool.Index, air: const tracy = trace(@src()); defer tracy.end(); + const gpa = wasm.base.comp.gpa; const func = mod.funcInfo(func_index); const decl_index = func.owner_decl; const decl = mod.declPtr(decl_index); @@ -1414,7 +1491,7 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func_index: InternPool.Index, air: // var decl_state: ?Dwarf.DeclState = if (wasm.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl_index) else null; // defer if (decl_state) |*ds| ds.deinit(); - var code_writer = std.ArrayList(u8).init(wasm.base.allocator); + var code_writer = std.ArrayList(u8).init(gpa); defer code_writer.deinit(); // const result = try codegen.generateFunction( // &wasm.base, @@ -1477,6 +1554,7 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: InternPool.DeclIndex) ! return; } + const gpa = wasm.base.comp.gpa; const atom_index = try wasm.getOrCreateAtomForDecl(decl_index); const atom = wasm.getAtomPtr(atom_index); atom.clear(); @@ -1489,7 +1567,7 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: InternPool.DeclIndex) ! } const val = if (decl.val.getVariable(mod)) |variable| Value.fromInterned(variable.init) else decl.val; - var code_writer = std.ArrayList(u8).init(wasm.base.allocator); + var code_writer = std.ArrayList(u8).init(gpa); defer code_writer.deinit(); const res = try codegen.generateSymbol( @@ -1528,16 +1606,17 @@ pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl_index: InternPool.De } fn finishUpdateDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex, code: []const u8, symbol_tag: Symbol.Tag) !void { - const mod = wasm.base.options.module.?; + const gpa = wasm.base.comp.gpa; + const mod = wasm.base.comp.module.?; const decl = mod.declPtr(decl_index); const atom_index = wasm.decls.get(decl_index).?; const atom = wasm.getAtomPtr(atom_index); const symbol = &wasm.symbols.items[atom.sym_index]; const full_name = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); - symbol.name = try wasm.string_table.put(wasm.base.allocator, full_name); + symbol.name = try wasm.string_table.put(gpa, full_name); symbol.tag = symbol_tag; - try atom.code.appendSlice(wasm.base.allocator, code); - try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {}); + try atom.code.appendSlice(gpa, code); + try wasm.resolved_symbols.put(gpa, atom.symbolLoc(), {}); atom.size = @intCast(code.len); if (code.len == 0) return; @@ -1591,7 +1670,8 @@ fn getFunctionSignature(wasm: *const Wasm, loc: SymbolLoc) std.wasm.Type { /// Returns the symbol index of the local /// The given `decl` is the parent decl whom owns the constant. pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: InternPool.DeclIndex) !u32 { - const mod = wasm.base.options.module.?; + const gpa = wasm.base.comp.gpa; + const mod = wasm.base.comp.module.?; assert(tv.ty.zigTypeTag(mod) != .Fn); // cannot create local symbols for functions const decl = mod.declPtr(decl_index); @@ -1599,14 +1679,14 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: InternPool.Dec const parent_atom = wasm.getAtom(parent_atom_index); const local_index = parent_atom.locals.items.len; const fqn = mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)); - const name = try std.fmt.allocPrintZ(wasm.base.allocator, "__unnamed_{s}_{d}", .{ + const name = try std.fmt.allocPrintZ(gpa, "__unnamed_{s}_{d}", .{ fqn, local_index, }); - defer wasm.base.allocator.free(name); + defer gpa.free(name); switch (try wasm.lowerConst(name, tv, decl.srcLoc(mod))) { .ok => |atom_index| { - try wasm.getAtomPtr(parent_atom_index).locals.append(wasm.base.allocator, atom_index); + try wasm.getAtomPtr(parent_atom_index).locals.append(gpa, atom_index); return wasm.getAtom(atom_index).getSymbolIndex().?; }, .fail => |em| { @@ -1623,24 +1703,25 @@ const LowerConstResult = union(enum) { }; fn lowerConst(wasm: *Wasm, name: []const u8, tv: TypedValue, src_loc: Module.SrcLoc) !LowerConstResult { - const mod = wasm.base.options.module.?; + const gpa = wasm.base.comp.gpa; + const mod = wasm.base.comp.module.?; // Create and initialize a new local symbol and atom const atom_index = try wasm.createAtom(); - var value_bytes = std.ArrayList(u8).init(wasm.base.allocator); + var value_bytes = std.ArrayList(u8).init(gpa); defer value_bytes.deinit(); const code = code: { const atom = wasm.getAtomPtr(atom_index); atom.alignment = tv.ty.abiAlignment(mod); wasm.symbols.items[atom.sym_index] = .{ - .name = try wasm.string_table.put(wasm.base.allocator, name), + .name = try wasm.string_table.put(gpa, name), .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL), .tag = .data, .index = undefined, .virtual_address = undefined, }; - try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {}); + try wasm.resolved_symbols.putNoClobber(gpa, atom.symbolLoc(), {}); const result = try codegen.generateSymbol( &wasm.base, @@ -1663,7 +1744,7 @@ fn lowerConst(wasm: *Wasm, name: []const u8, tv: TypedValue, src_loc: Module.Src const atom = wasm.getAtomPtr(atom_index); atom.size = @intCast(code.len); - try atom.code.appendSlice(wasm.base.allocator, code); + try atom.code.appendSlice(gpa, code); return .{ .ok = atom_index }; } @@ -1673,8 +1754,9 @@ fn lowerConst(wasm: *Wasm, name: []const u8, tv: TypedValue, src_loc: Module.Src /// and then returns the index to it. pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8, lib_name: ?[]const u8) !u32 { _ = lib_name; - const name_index = try wasm.string_table.put(wasm.base.allocator, name); - const gop = try wasm.globals.getOrPut(wasm.base.allocator, name_index); + const gpa = wasm.base.comp.gpa; + const name_index = try wasm.string_table.put(gpa, name); + const gop = try wasm.globals.getOrPut(gpa, name_index); if (gop.found_existing) { return gop.value_ptr.*.index; } @@ -1691,14 +1773,14 @@ pub fn getGlobalSymbol(wasm: *Wasm, name: []const u8, lib_name: ?[]const u8) !u3 const sym_index = if (wasm.symbols_free_list.popOrNull()) |index| index else blk: { const index: u32 = @intCast(wasm.symbols.items.len); - try wasm.symbols.ensureUnusedCapacity(wasm.base.allocator, 1); + try wasm.symbols.ensureUnusedCapacity(gpa, 1); wasm.symbols.items.len += 1; break :blk index; }; wasm.symbols.items[sym_index] = symbol; gop.value_ptr.* = .{ .index = sym_index, .file = null }; - try wasm.resolved_symbols.put(wasm.base.allocator, gop.value_ptr.*, {}); - try wasm.undefs.putNoClobber(wasm.base.allocator, name_index, gop.value_ptr.*); + try wasm.resolved_symbols.put(gpa, gop.value_ptr.*, {}); + try wasm.undefs.putNoClobber(gpa, name_index, gop.value_ptr.*); return sym_index; } @@ -1709,7 +1791,9 @@ pub fn getDeclVAddr( decl_index: InternPool.DeclIndex, reloc_info: link.File.RelocInfo, ) !u64 { - const mod = wasm.base.options.module.?; + const target = wasm.base.comp.root_mod.resolved_target.result; + const gpa = wasm.base.comp.gpa; + const mod = wasm.base.comp.module.?; const decl = mod.declPtr(decl_index); const target_atom_index = try wasm.getOrCreateAtomForDecl(decl_index); @@ -1718,24 +1802,24 @@ pub fn getDeclVAddr( assert(reloc_info.parent_atom_index != 0); const atom_index = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?; const atom = wasm.getAtomPtr(atom_index); - const is_wasm32 = wasm.base.options.target.cpu.arch == .wasm32; + const is_wasm32 = target.cpu.arch == .wasm32; if (decl.ty.zigTypeTag(mod) == .Fn) { assert(reloc_info.addend == 0); // addend not allowed for function relocations // We found a function pointer, so add it to our table, // as function pointers are not allowed to be stored inside the data section. // They are instead stored in a function table which are called by index. try wasm.addTableFunction(target_symbol_index); - try atom.relocs.append(wasm.base.allocator, .{ + try atom.relocs.append(gpa, .{ .index = target_symbol_index, - .offset = @as(u32, @intCast(reloc_info.offset)), + .offset = @intCast(reloc_info.offset), .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64, }); } else { - try atom.relocs.append(wasm.base.allocator, .{ + try atom.relocs.append(gpa, .{ .index = target_symbol_index, - .offset = @as(u32, @intCast(reloc_info.offset)), + .offset = @intCast(reloc_info.offset), .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64, - .addend = @as(i32, @intCast(reloc_info.addend)), + .addend = @intCast(reloc_info.addend), }); } // we do not know the final address at this point, @@ -1751,9 +1835,10 @@ pub fn lowerAnonDecl( explicit_alignment: Alignment, src_loc: Module.SrcLoc, ) !codegen.Result { - const gop = try wasm.anon_decls.getOrPut(wasm.base.allocator, decl_val); + const gpa = wasm.base.comp.gpa; + const gop = try wasm.anon_decls.getOrPut(gpa, decl_val); if (!gop.found_existing) { - const mod = wasm.base.options.module.?; + const mod = wasm.base.comp.module.?; const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val)); const tv: TypedValue = .{ .ty = ty, .val = Value.fromInterned(decl_val) }; var name_buf: [32]u8 = undefined; @@ -1779,13 +1864,15 @@ pub fn lowerAnonDecl( } pub fn getAnonDeclVAddr(wasm: *Wasm, decl_val: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 { + const gpa = wasm.base.comp.gpa; + const target = wasm.base.comp.root_mod.resolved_target.result; const atom_index = wasm.anon_decls.get(decl_val).?; const target_symbol_index = wasm.getAtom(atom_index).getSymbolIndex().?; const parent_atom_index = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?; const parent_atom = wasm.getAtomPtr(parent_atom_index); - const is_wasm32 = wasm.base.options.target.cpu.arch == .wasm32; - const mod = wasm.base.options.module.?; + const is_wasm32 = target.cpu.arch == .wasm32; + const mod = wasm.base.comp.module.?; const ty = Type.fromInterned(mod.intern_pool.typeOf(decl_val)); if (ty.zigTypeTag(mod) == .Fn) { assert(reloc_info.addend == 0); // addend not allowed for function relocations @@ -1793,17 +1880,17 @@ pub fn getAnonDeclVAddr(wasm: *Wasm, decl_val: InternPool.Index, reloc_info: lin // as function pointers are not allowed to be stored inside the data section. // They are instead stored in a function table which are called by index. try wasm.addTableFunction(target_symbol_index); - try parent_atom.relocs.append(wasm.base.allocator, .{ + try parent_atom.relocs.append(gpa, .{ .index = target_symbol_index, - .offset = @as(u32, @intCast(reloc_info.offset)), + .offset = @intCast(reloc_info.offset), .relocation_type = if (is_wasm32) .R_WASM_TABLE_INDEX_I32 else .R_WASM_TABLE_INDEX_I64, }); } else { - try parent_atom.relocs.append(wasm.base.allocator, .{ + try parent_atom.relocs.append(gpa, .{ .index = target_symbol_index, - .offset = @as(u32, @intCast(reloc_info.offset)), + .offset = @intCast(reloc_info.offset), .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_I32 else .R_WASM_MEMORY_ADDR_I64, - .addend = @as(i32, @intCast(reloc_info.addend)), + .addend = @intCast(reloc_info.addend), }); } @@ -1814,7 +1901,12 @@ pub fn getAnonDeclVAddr(wasm: *Wasm, decl_val: InternPool.Index, reloc_info: lin return target_symbol_index; } -pub fn deleteDeclExport(wasm: *Wasm, decl_index: InternPool.DeclIndex) void { +pub fn deleteDeclExport( + wasm: *Wasm, + decl_index: InternPool.DeclIndex, + name: InternPool.NullTerminatedString, +) void { + _ = name; if (wasm.llvm_object) |_| return; const atom_index = wasm.decls.get(decl_index) orelse return; const sym_index = wasm.getAtom(atom_index).sym_index; @@ -1840,8 +1932,6 @@ pub fn updateExports( } if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(mod, exported, exports); - if (wasm.base.options.emit == null) return; - const decl_index = switch (exported) { .decl_index => |i| i, .value => |val| { @@ -1880,7 +1970,7 @@ pub fn updateExports( }; const exported_atom_index = try wasm.getOrCreateAtomForDecl(exported_decl_index); const exported_atom = wasm.getAtom(exported_atom_index); - const export_name = try wasm.string_table.put(wasm.base.allocator, mod.intern_pool.stringToSlice(exp.opts.name)); + const export_name = try wasm.string_table.put(gpa, mod.intern_pool.stringToSlice(exp.opts.name)); const sym_loc = exported_atom.symbolLoc(); const symbol = sym_loc.getSymbol(wasm); symbol.setGlobal(true); @@ -1915,7 +2005,7 @@ pub fn updateExports( if (!existing_sym.isUndefined()) blk: { if (symbol.isWeak()) { - try wasm.discarded.put(wasm.base.allocator, existing_loc, sym_loc); + try wasm.discarded.put(gpa, existing_loc, sym_loc); continue; // to-be-exported symbol is weak, so we keep the existing symbol } @@ -1939,18 +2029,18 @@ pub fn updateExports( } // in this case the existing symbol must be replaced either because it's weak or undefined. - try wasm.discarded.put(wasm.base.allocator, existing_loc, sym_loc); + try wasm.discarded.put(gpa, existing_loc, sym_loc); _ = wasm.imports.remove(existing_loc); _ = wasm.undefs.swapRemove(existing_sym.name); } // Ensure the symbol will be exported using the given name if (!mod.intern_pool.stringEqlSlice(exp.opts.name, sym_loc.getName(wasm))) { - try wasm.export_names.put(wasm.base.allocator, sym_loc, export_name); + try wasm.export_names.put(gpa, sym_loc, export_name); } try wasm.globals.put( - wasm.base.allocator, + gpa, export_name, sym_loc, ); @@ -1959,18 +2049,19 @@ pub fn updateExports( pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void { if (wasm.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index); - const mod = wasm.base.options.module.?; + const gpa = wasm.base.comp.gpa; + const mod = wasm.base.comp.module.?; const decl = mod.declPtr(decl_index); const atom_index = wasm.decls.get(decl_index).?; const atom = wasm.getAtomPtr(atom_index); - wasm.symbols_free_list.append(wasm.base.allocator, atom.sym_index) catch {}; + wasm.symbols_free_list.append(gpa, atom.sym_index) catch {}; _ = wasm.decls.remove(decl_index); wasm.symbols.items[atom.sym_index].tag = .dead; for (atom.locals.items) |local_atom_index| { const local_atom = wasm.getAtom(local_atom_index); const local_symbol = &wasm.symbols.items[local_atom.sym_index]; local_symbol.tag = .dead; // also for any local symbol - wasm.symbols_free_list.append(wasm.base.allocator, local_atom.sym_index) catch {}; + wasm.symbols_free_list.append(gpa, local_atom.sym_index) catch {}; assert(wasm.resolved_symbols.swapRemove(local_atom.symbolLoc())); assert(wasm.symbol_atom.remove(local_atom.symbolLoc())); } @@ -1999,8 +2090,9 @@ pub fn freeDecl(wasm: *Wasm, decl_index: InternPool.DeclIndex) void { /// Appends a new entry to the indirect function table pub fn addTableFunction(wasm: *Wasm, symbol_index: u32) !void { - const index = @as(u32, @intCast(wasm.function_table.count())); - try wasm.function_table.put(wasm.base.allocator, .{ .file = null, .index = symbol_index }, index); + const gpa = wasm.base.comp.gpa; + const index: u32 = @intCast(wasm.function_table.count()); + try wasm.function_table.put(gpa, .{ .file = null, .index = symbol_index }, index); } /// Assigns indexes to all indirect functions. @@ -2019,7 +2111,7 @@ fn mapFunctionTable(wasm: *Wasm) void { } } - if (wasm.base.options.import_table or wasm.base.options.output_mode == .Obj) { + if (wasm.import_table or wasm.base.comp.config.output_mode == .Obj) { const sym_loc = wasm.findGlobalSymbol("__indirect_function_table").?; const import = wasm.imports.getPtr(sym_loc).?; import.kind.table.limits.min = index - 1; // we start at index 1. @@ -2048,6 +2140,7 @@ pub fn addOrUpdateImport( /// is asserted instead. type_index: ?u32, ) !void { + const gpa = wasm.base.comp.gpa; assert(symbol_index != 0); // For the import name, we use the decl's name, rather than the fully qualified name // Also mangle the name when the lib name is set and not equal to "C" so imports with the same @@ -2055,11 +2148,11 @@ pub fn addOrUpdateImport( const mangle_name = lib_name != null and !std.mem.eql(u8, lib_name.?, "c"); const full_name = if (mangle_name) full_name: { - break :full_name try std.fmt.allocPrint(wasm.base.allocator, "{s}|{s}", .{ name, lib_name.? }); + break :full_name try std.fmt.allocPrint(gpa, "{s}|{s}", .{ name, lib_name.? }); } else name; - defer if (mangle_name) wasm.base.allocator.free(full_name); + defer if (mangle_name) gpa.free(full_name); - const decl_name_index = try wasm.string_table.put(wasm.base.allocator, full_name); + const decl_name_index = try wasm.string_table.put(gpa, full_name); const symbol: *Symbol = &wasm.symbols.items[symbol_index]; symbol.setUndefined(true); symbol.setGlobal(true); @@ -2068,12 +2161,12 @@ pub fn addOrUpdateImport( // we specified a specific name for the symbol that does not match the import name symbol.setFlag(.WASM_SYM_EXPLICIT_NAME); } - const global_gop = try wasm.globals.getOrPut(wasm.base.allocator, decl_name_index); + const global_gop = try wasm.globals.getOrPut(gpa, decl_name_index); if (!global_gop.found_existing) { const loc: SymbolLoc = .{ .file = null, .index = symbol_index }; global_gop.value_ptr.* = loc; - try wasm.resolved_symbols.put(wasm.base.allocator, loc, {}); - try wasm.undefs.putNoClobber(wasm.base.allocator, decl_name_index, loc); + try wasm.resolved_symbols.put(gpa, loc, {}); + try wasm.undefs.putNoClobber(gpa, decl_name_index, loc); } else if (global_gop.value_ptr.*.index != symbol_index) { // We are not updating a symbol, but found an existing global // symbol with the same name. This means we always favor the @@ -2081,21 +2174,21 @@ pub fn addOrUpdateImport( // We can also skip storing the import as we will not output // this symbol. return wasm.discarded.put( - wasm.base.allocator, + gpa, .{ .file = null, .index = symbol_index }, global_gop.value_ptr.*, ); } if (type_index) |ty_index| { - const gop = try wasm.imports.getOrPut(wasm.base.allocator, .{ .index = symbol_index, .file = null }); + const gop = try wasm.imports.getOrPut(gpa, .{ .index = symbol_index, .file = null }); const module_name = if (lib_name) |l_name| blk: { break :blk l_name; } else wasm.host_name; if (!gop.found_existing) { gop.value_ptr.* = .{ - .module_name = try wasm.string_table.put(wasm.base.allocator, module_name), - .name = try wasm.string_table.put(wasm.base.allocator, name), + .module_name = try wasm.string_table.put(gpa, module_name), + .name = try wasm.string_table.put(gpa, name), .kind = .{ .function = ty_index }, }; } @@ -2132,10 +2225,13 @@ const Kind = union(enum) { /// Parses an Atom and inserts its metadata into the corresponding sections. fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void { + const comp = wasm.base.comp; + const gpa = comp.gpa; + const shared_memory = comp.config.shared_memory; + const import_memory = comp.config.import_memory; const atom = wasm.getAtomPtr(atom_index); const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm); - const do_garbage_collect = wasm.base.options.gc_sections orelse - (wasm.base.options.output_mode != .Obj); + const do_garbage_collect = wasm.base.gc_sections; if (symbol.isDead() and do_garbage_collect) { // Prevent unreferenced symbols from being parsed. @@ -2147,7 +2243,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void { const index: u32 = @intCast(wasm.functions.count() + wasm.imported_functions_count); const type_index = wasm.atom_types.get(atom_index).?; try wasm.functions.putNoClobber( - wasm.base.allocator, + gpa, .{ .file = null, .index = index }, .{ .func = .{ .type_index = type_index }, .sym_index = atom.sym_index }, ); @@ -2156,7 +2252,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void { if (wasm.code_section_index == null) { wasm.code_section_index = @intCast(wasm.segments.items.len); - try wasm.segments.append(wasm.base.allocator, .{ + try wasm.segments.append(gpa, .{ .alignment = atom.alignment, .size = atom.size, .offset = 0, @@ -2167,11 +2263,11 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void { break :result wasm.code_section_index.?; }, .data => result: { - const segment_name = try std.mem.concat(wasm.base.allocator, u8, &.{ + const segment_name = try std.mem.concat(gpa, u8, &.{ kind.segmentName(), wasm.string_table.get(symbol.name), }); - errdefer wasm.base.allocator.free(segment_name); + errdefer gpa.free(segment_name); const segment_info: types.Segment = .{ .name = segment_name, .alignment = atom.alignment, @@ -2183,27 +2279,27 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void { // we set the entire region of it to zeroes. // We do not have to do this when exporting the memory (the default) because the runtime // will do it for us, and we do not emit the bss segment at all. - if ((wasm.base.options.output_mode == .Obj or wasm.base.options.import_memory) and kind.data == .uninitialized) { + if ((wasm.base.comp.config.output_mode == .Obj or import_memory) and kind.data == .uninitialized) { @memset(atom.code.items, 0); } - const should_merge = wasm.base.options.output_mode != .Obj; - const gop = try wasm.data_segments.getOrPut(wasm.base.allocator, segment_info.outputName(should_merge)); + const should_merge = wasm.base.comp.config.output_mode != .Obj; + const gop = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(should_merge)); if (gop.found_existing) { const index = gop.value_ptr.*; wasm.segments.items[index].size += atom.size; symbol.index = @intCast(wasm.segment_info.getIndex(index).?); // segment info already exists, so free its memory - wasm.base.allocator.free(segment_name); + gpa.free(segment_name); break :result index; } else { const index: u32 = @intCast(wasm.segments.items.len); var flags: u32 = 0; - if (wasm.base.options.shared_memory) { + if (shared_memory) { flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE); } - try wasm.segments.append(wasm.base.allocator, .{ + try wasm.segments.append(gpa, .{ .alignment = atom.alignment, .size = 0, .offset = 0, @@ -2212,7 +2308,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void { gop.value_ptr.* = index; const info_index: u32 = @intCast(wasm.segment_info.count()); - try wasm.segment_info.put(wasm.base.allocator, index, segment_info); + try wasm.segment_info.put(gpa, index, segment_info); symbol.index = info_index; break :result index; } @@ -2228,6 +2324,7 @@ fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void { /// From a given index, append the given `Atom` at the back of the linked list. /// Simply inserts it into the map of atoms when it doesn't exist yet. pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void { + const gpa = wasm.base.comp.gpa; const atom = wasm.getAtomPtr(atom_index); if (wasm.atoms.getPtr(index)) |last_index_ptr| { const last = wasm.getAtomPtr(last_index_ptr.*); @@ -2235,7 +2332,7 @@ pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void atom.prev = last_index_ptr.*; last_index_ptr.* = atom_index; } else { - try wasm.atoms.putNoClobber(wasm.base.allocator, index, atom_index); + try wasm.atoms.putNoClobber(gpa, index, atom_index); } } @@ -2344,7 +2441,7 @@ fn allocateVirtualAddresses(wasm: *Wasm) void { }; const atom = wasm.getAtom(atom_index); - const merge_segment = wasm.base.options.output_mode != .Obj; + const merge_segment = wasm.base.comp.config.output_mode != .Obj; const segment_info = if (atom.file) |object_index| blk: { break :blk wasm.objects.items[object_index].segment_info; } else wasm.segment_info.values(); @@ -2363,12 +2460,13 @@ fn allocateVirtualAddresses(wasm: *Wasm) void { } fn sortDataSegments(wasm: *Wasm) !void { + const gpa = wasm.base.comp.gpa; var new_mapping: std.StringArrayHashMapUnmanaged(u32) = .{}; - try new_mapping.ensureUnusedCapacity(wasm.base.allocator, wasm.data_segments.count()); - errdefer new_mapping.deinit(wasm.base.allocator); + try new_mapping.ensureUnusedCapacity(gpa, wasm.data_segments.count()); + errdefer new_mapping.deinit(gpa); - const keys = try wasm.base.allocator.dupe([]const u8, wasm.data_segments.keys()); - defer wasm.base.allocator.free(keys); + const keys = try gpa.dupe([]const u8, wasm.data_segments.keys()); + defer gpa.free(keys); const SortContext = struct { fn sort(_: void, lhs: []const u8, rhs: []const u8) bool { @@ -2388,7 +2486,7 @@ fn sortDataSegments(wasm: *Wasm) !void { const segment_index = wasm.data_segments.get(key).?; new_mapping.putAssumeCapacity(key, segment_index); } - wasm.data_segments.deinit(wasm.base.allocator); + wasm.data_segments.deinit(gpa); wasm.data_segments = new_mapping; } @@ -2401,8 +2499,9 @@ fn sortDataSegments(wasm: *Wasm) !void { /// original functions and their types. We need to know the type to verify it doesn't /// contain any parameters. fn setupInitFunctions(wasm: *Wasm) !void { + const gpa = wasm.base.comp.gpa; for (wasm.objects.items, 0..) |object, file_index| { - try wasm.init_funcs.ensureUnusedCapacity(wasm.base.allocator, object.init_funcs.len); + try wasm.init_funcs.ensureUnusedCapacity(gpa, object.init_funcs.len); for (object.init_funcs) |init_func| { const symbol = object.symtable[init_func.symbol_index]; const ty: std.wasm.Type = if (symbol.isUndefined()) ty: { @@ -2439,9 +2538,10 @@ fn setupInitFunctions(wasm: *Wasm) !void { /// Generates an atom containing the global error set' size. /// This will only be generated if the symbol exists. fn setupErrorsLen(wasm: *Wasm) !void { + const gpa = wasm.base.comp.gpa; const loc = wasm.findGlobalSymbol("__zig_errors_len") orelse return; - const errors_len = wasm.base.options.module.?.global_error_set.count(); + const errors_len = wasm.base.comp.module.?.global_error_set.count(); // overwrite existing atom if it already exists (maybe the error set has increased) // if not, allcoate a new atom. const atom_index = if (wasm.symbol_atom.get(loc)) |index| blk: { @@ -2456,19 +2556,19 @@ fn setupErrorsLen(wasm: *Wasm) !void { prev_atom.next = atom.next; atom.prev = null; } - atom.deinit(wasm.base.allocator); + atom.deinit(gpa); break :blk index; } else new_atom: { const atom_index: Atom.Index = @intCast(wasm.managed_atoms.items.len); - try wasm.symbol_atom.put(wasm.base.allocator, loc, atom_index); - try wasm.managed_atoms.append(wasm.base.allocator, undefined); + try wasm.symbol_atom.put(gpa, loc, atom_index); + try wasm.managed_atoms.append(gpa, undefined); break :new_atom atom_index; }; const atom = wasm.getAtomPtr(atom_index); atom.* = Atom.empty; atom.sym_index = loc.index; atom.size = 2; - try atom.code.writer(wasm.base.allocator).writeInt(u16, @intCast(errors_len), .little); + try atom.code.writer(gpa).writeInt(u16, @intCast(errors_len), .little); try wasm.parseAtom(atom_index, .{ .data = .read_only }); } @@ -2480,16 +2580,17 @@ fn setupErrorsLen(wasm: *Wasm) !void { /// references to the function stored in the symbol have been finalized so we end /// up calling the resolved function. fn initializeCallCtorsFunction(wasm: *Wasm) !void { + const gpa = wasm.base.comp.gpa; // No code to emit, so also no ctors to call if (wasm.code_section_index == null) { // Make sure to remove it from the resolved symbols so we do not emit // it within any section. TODO: Remove this once we implement garbage collection. const loc = wasm.findGlobalSymbol("__wasm_call_ctors").?; - std.debug.assert(wasm.resolved_symbols.swapRemove(loc)); + assert(wasm.resolved_symbols.swapRemove(loc)); return; } - var function_body = std.ArrayList(u8).init(wasm.base.allocator); + var function_body = std.ArrayList(u8).init(gpa); defer function_body.deinit(); const writer = function_body.writer(); @@ -2531,6 +2632,7 @@ fn createSyntheticFunction( func_ty: std.wasm.Type, function_body: *std.ArrayList(u8), ) !void { + const gpa = wasm.base.comp.gpa; const loc = wasm.findGlobalSymbol(symbol_name) orelse try wasm.createSyntheticSymbol(symbol_name, .function); const symbol = loc.getSymbol(wasm); @@ -2541,7 +2643,7 @@ fn createSyntheticFunction( // create function with above type const func_index = wasm.imported_functions_count + @as(u32, @intCast(wasm.functions.count())); try wasm.functions.putNoClobber( - wasm.base.allocator, + gpa, .{ .file = null, .index = func_index }, .{ .func = .{ .type_index = ty_index }, .sym_index = loc.index }, ); @@ -2549,7 +2651,7 @@ fn createSyntheticFunction( // create the atom that will be output into the final binary const atom_index = @as(Atom.Index, @intCast(wasm.managed_atoms.items.len)); - const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); + const atom = try wasm.managed_atoms.addOne(gpa); atom.* = .{ .size = @as(u32, @intCast(function_body.items.len)), .offset = 0, @@ -2562,7 +2664,7 @@ fn createSyntheticFunction( .original_offset = 0, }; try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index); - try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index); + try wasm.symbol_atom.putNoClobber(gpa, loc, atom_index); // `allocateAtoms` has already been called, set the atom's offset manually. // This is fine to do manually as we insert the atom at the very end. @@ -2582,10 +2684,11 @@ pub fn createFunction( function_body: *std.ArrayList(u8), relocations: *std.ArrayList(Relocation), ) !u32 { + const gpa = wasm.base.comp.gpa; const loc = try wasm.createSyntheticSymbol(symbol_name, .function); - const atom_index = @as(Atom.Index, @intCast(wasm.managed_atoms.items.len)); - const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); + const atom_index: Atom.Index = @intCast(wasm.managed_atoms.items.len); + const atom = try wasm.managed_atoms.addOne(gpa); atom.* = .{ .size = @intCast(function_body.items.len), .offset = 0, @@ -2607,9 +2710,9 @@ pub fn createFunction( break :idx index; }; try wasm.appendAtomAtIndex(section_index, atom_index); - try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index); - try wasm.atom_types.put(wasm.base.allocator, atom_index, try wasm.putOrGetFuncType(func_ty)); - try wasm.synthetic_functions.append(wasm.base.allocator, atom_index); + try wasm.symbol_atom.putNoClobber(gpa, loc, atom_index); + try wasm.atom_types.put(gpa, atom_index, try wasm.putOrGetFuncType(func_ty)); + try wasm.synthetic_functions.append(gpa, atom_index); return loc.index; } @@ -2622,9 +2725,13 @@ fn setupStartSection(wasm: *Wasm) !void { } fn initializeTLSFunction(wasm: *Wasm) !void { - if (!wasm.base.options.shared_memory) return; + const comp = wasm.base.comp; + const gpa = comp.gpa; + const shared_memory = comp.config.shared_memory; + + if (!shared_memory) return; - var function_body = std.ArrayList(u8).init(wasm.base.allocator); + var function_body = std.ArrayList(u8).init(gpa); defer function_body.deinit(); const writer = function_body.writer(); @@ -2684,6 +2791,7 @@ fn initializeTLSFunction(wasm: *Wasm) !void { } fn setupImports(wasm: *Wasm) !void { + const gpa = wasm.base.comp.gpa; log.debug("Merging imports", .{}); var discarded_it = wasm.discarded.keyIterator(); while (discarded_it.next()) |discarded| { @@ -2718,12 +2826,12 @@ fn setupImports(wasm: *Wasm) !void { // We copy the import to a new import to ensure the names contain references // to the internal string table, rather than of the object file. const new_imp: types.Import = .{ - .module_name = try wasm.string_table.put(wasm.base.allocator, object.string_table.get(import.module_name)), - .name = try wasm.string_table.put(wasm.base.allocator, object.string_table.get(import.name)), + .module_name = try wasm.string_table.put(gpa, object.string_table.get(import.module_name)), + .name = try wasm.string_table.put(gpa, object.string_table.get(import.name)), .kind = import.kind, }; // TODO: De-duplicate imports when they contain the same names and type - try wasm.imports.putNoClobber(wasm.base.allocator, symbol_loc, new_imp); + try wasm.imports.putNoClobber(gpa, symbol_loc, new_imp); } // Assign all indexes of the imports to their representing symbols @@ -2764,7 +2872,9 @@ fn setupImports(wasm: *Wasm) !void { /// Takes the global, function and table section from each linked object file /// and merges it into a single section for each. fn mergeSections(wasm: *Wasm) !void { - var removed_duplicates = std.ArrayList(SymbolLoc).init(wasm.base.allocator); + const gpa = wasm.base.comp.gpa; + + var removed_duplicates = std.ArrayList(SymbolLoc).init(gpa); defer removed_duplicates.deinit(); for (wasm.resolved_symbols.keys()) |sym_loc| { @@ -2791,7 +2901,7 @@ fn mergeSections(wasm: *Wasm) !void { switch (symbol.tag) { .function => { const gop = try wasm.functions.getOrPut( - wasm.base.allocator, + gpa, .{ .file = sym_loc.file, .index = symbol.index }, ); if (gop.found_existing) { @@ -2800,7 +2910,7 @@ fn mergeSections(wasm: *Wasm) !void { // we only emit a single function, instead of duplicates. symbol.unmark(); try wasm.discarded.putNoClobber( - wasm.base.allocator, + gpa, sym_loc, .{ .file = gop.key_ptr.*.file, .index = gop.value_ptr.*.sym_index }, ); @@ -2813,12 +2923,12 @@ fn mergeSections(wasm: *Wasm) !void { .global => { const original_global = object.globals[index]; symbol.index = @as(u32, @intCast(wasm.wasm_globals.items.len)) + wasm.imported_globals_count; - try wasm.wasm_globals.append(wasm.base.allocator, original_global); + try wasm.wasm_globals.append(gpa, original_global); }, .table => { const original_table = object.tables[index]; symbol.index = @as(u32, @intCast(wasm.tables.items.len)) + wasm.imported_tables_count; - try wasm.tables.append(wasm.base.allocator, original_table); + try wasm.tables.append(gpa, original_table); }, else => unreachable, } @@ -2838,10 +2948,11 @@ fn mergeSections(wasm: *Wasm) !void { /// 'types' section, while assigning the type index to the representing /// section (import, export, function). fn mergeTypes(wasm: *Wasm) !void { + const gpa = wasm.base.comp.gpa; // A map to track which functions have already had their // type inserted. If we do this for the same function multiple times, // it will be overwritten with the incorrect type. - var dirty = std.AutoHashMap(u32, void).init(wasm.base.allocator); + var dirty = std.AutoHashMap(u32, void).init(gpa); try dirty.ensureUnusedCapacity(@as(u32, @intCast(wasm.functions.count()))); defer dirty.deinit(); @@ -2873,10 +2984,12 @@ fn mergeTypes(wasm: *Wasm) !void { } fn setupExports(wasm: *Wasm) !void { - if (wasm.base.options.output_mode == .Obj) return; + const comp = wasm.base.comp; + const gpa = comp.gpa; + if (comp.config.output_mode == .Obj) return; log.debug("Building exports from symbols", .{}); - const force_exp_names = wasm.base.options.export_symbol_names; + const force_exp_names = wasm.export_symbol_names; if (force_exp_names.len > 0) { var failed_exports = false; @@ -2898,16 +3011,16 @@ fn setupExports(wasm: *Wasm) !void { for (wasm.resolved_symbols.keys()) |sym_loc| { const symbol = sym_loc.getSymbol(wasm); - if (!symbol.isExported(wasm.base.options.rdynamic)) continue; + if (!symbol.isExported(comp.config.rdynamic)) continue; const sym_name = sym_loc.getName(wasm); const export_name = if (wasm.export_names.get(sym_loc)) |name| name else blk: { if (sym_loc.file == null) break :blk symbol.name; - break :blk try wasm.string_table.put(wasm.base.allocator, sym_name); + break :blk try wasm.string_table.put(gpa, sym_name); }; const exp: types.Export = if (symbol.tag == .data) exp: { const global_index = @as(u32, @intCast(wasm.imported_globals_count + wasm.wasm_globals.items.len)); - try wasm.wasm_globals.append(wasm.base.allocator, .{ + try wasm.wasm_globals.append(gpa, .{ .global_type = .{ .valtype = .i32, .mutable = false }, .init = .{ .i32_const = @as(i32, @intCast(symbol.virtual_address)) }, }); @@ -2926,15 +3039,16 @@ fn setupExports(wasm: *Wasm) !void { wasm.string_table.get(exp.name), exp.index, }); - try wasm.exports.append(wasm.base.allocator, exp); + try wasm.exports.append(gpa, exp); } log.debug("Completed building exports. Total count: ({d})", .{wasm.exports.items.len}); } fn setupStart(wasm: *Wasm) !void { + const comp = wasm.base.comp; // do not export entry point if user set none or no default was set. - const entry_name = wasm.base.options.entry orelse return; + const entry_name = wasm.entry_name orelse return; const symbol_loc = wasm.findGlobalSymbol(entry_name) orelse { log.err("Entry symbol '{s}' missing, use '-fno-entry' to suppress", .{entry_name}); @@ -2948,33 +3062,33 @@ fn setupStart(wasm: *Wasm) !void { } // Ensure the symbol is exported so host environment can access it - if (wasm.base.options.output_mode != .Obj) { + if (comp.config.output_mode != .Obj) { symbol.setFlag(.WASM_SYM_EXPORTED); } } /// Sets up the memory section of the wasm module, as well as the stack. fn setupMemory(wasm: *Wasm) !void { + const comp = wasm.base.comp; + const shared_memory = comp.config.shared_memory; log.debug("Setting up memory layout", .{}); const page_size = std.wasm.page_size; // 64kb - // Use the user-provided stack size or else we use 1MB by default - const stack_size = wasm.base.options.stack_size_override orelse page_size * 16; const stack_alignment: Alignment = .@"16"; // wasm's stack alignment as specified by tool-convention const heap_alignment: Alignment = .@"16"; // wasm's heap alignment as specified by tool-convention // Always place the stack at the start by default // unless the user specified the global-base flag var place_stack_first = true; - var memory_ptr: u64 = if (wasm.base.options.global_base) |base| blk: { + var memory_ptr: u64 = if (wasm.global_base) |base| blk: { place_stack_first = false; break :blk base; } else 0; - const is_obj = wasm.base.options.output_mode == .Obj; + const is_obj = comp.config.output_mode == .Obj; if (place_stack_first and !is_obj) { memory_ptr = stack_alignment.forward(memory_ptr); - memory_ptr += stack_size; + memory_ptr += wasm.base.stack_size; // We always put the stack pointer global at index 0 wasm.wasm_globals.items[0].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr)))); } @@ -2997,7 +3111,7 @@ fn setupMemory(wasm: *Wasm) !void { } if (wasm.findGlobalSymbol("__tls_base")) |loc| { const sym = loc.getSymbol(wasm); - wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = if (wasm.base.options.shared_memory) + wasm.wasm_globals.items[sym.index - wasm.imported_globals_count].init.i32_const = if (shared_memory) @as(i32, 0) else @as(i32, @intCast(memory_ptr)); @@ -3010,7 +3124,7 @@ fn setupMemory(wasm: *Wasm) !void { } // create the memory init flag which is used by the init memory function - if (wasm.base.options.shared_memory and wasm.hasPassiveInitializationSegments()) { + if (shared_memory and wasm.hasPassiveInitializationSegments()) { // align to pointer size memory_ptr = mem.alignForward(u64, memory_ptr, 4); const loc = try wasm.createSyntheticSymbol("__wasm_init_memory_flag", .data); @@ -3021,7 +3135,7 @@ fn setupMemory(wasm: *Wasm) !void { if (!place_stack_first and !is_obj) { memory_ptr = stack_alignment.forward(memory_ptr); - memory_ptr += stack_size; + memory_ptr += wasm.base.stack_size; wasm.wasm_globals.items[0].init.i32_const = @as(i32, @bitCast(@as(u32, @intCast(memory_ptr)))); } @@ -3036,7 +3150,7 @@ fn setupMemory(wasm: *Wasm) !void { // For now we only support wasm32 by setting the maximum allowed memory size 2^32-1 const max_memory_allowed: u64 = (1 << 32) - 1; - if (wasm.base.options.initial_memory) |initial_memory| { + if (wasm.initial_memory) |initial_memory| { if (!std.mem.isAlignedGeneric(u64, initial_memory, page_size)) { log.err("Initial memory must be {d}-byte aligned", .{page_size}); return error.MissAlignment; @@ -3062,7 +3176,7 @@ fn setupMemory(wasm: *Wasm) !void { symbol.virtual_address = @as(u32, @intCast(memory_ptr)); } - if (wasm.base.options.max_memory) |max_memory| { + if (wasm.max_memory) |max_memory| { if (!std.mem.isAlignedGeneric(u64, max_memory, page_size)) { log.err("Maximum memory must be {d}-byte aligned", .{page_size}); return error.MissAlignment; @@ -3077,7 +3191,7 @@ fn setupMemory(wasm: *Wasm) !void { } wasm.memories.limits.max = @as(u32, @intCast(max_memory / page_size)); wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_HAS_MAX); - if (wasm.base.options.shared_memory) { + if (shared_memory) { wasm.memories.limits.setFlag(.WASM_LIMITS_FLAG_IS_SHARED); } log.debug("Maximum memory pages: {?d}", .{wasm.memories.limits.max}); @@ -3088,29 +3202,32 @@ fn setupMemory(wasm: *Wasm) !void { /// index of the segment within the final data section. When the segment does not yet /// exist, a new one will be initialized and appended. The new index will be returned in that case. pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u32 { + const comp = wasm.base.comp; + const gpa = comp.gpa; const object: Object = wasm.objects.items[object_index]; const symbol = object.symtable[symbol_index]; - const index = @as(u32, @intCast(wasm.segments.items.len)); + const index: u32 = @intCast(wasm.segments.items.len); + const shared_memory = comp.config.shared_memory; switch (symbol.tag) { .data => { const segment_info = object.segment_info[symbol.index]; - const merge_segment = wasm.base.options.output_mode != .Obj; - const result = try wasm.data_segments.getOrPut(wasm.base.allocator, segment_info.outputName(merge_segment)); + const merge_segment = comp.config.output_mode != .Obj; + const result = try wasm.data_segments.getOrPut(gpa, segment_info.outputName(merge_segment)); if (!result.found_existing) { result.value_ptr.* = index; var flags: u32 = 0; - if (wasm.base.options.shared_memory) { + if (shared_memory) { flags |= @intFromEnum(Segment.Flag.WASM_DATA_SEGMENT_IS_PASSIVE); } - try wasm.segments.append(wasm.base.allocator, .{ + try wasm.segments.append(gpa, .{ .alignment = .@"1", .size = 0, .offset = 0, .flags = flags, }); - try wasm.segment_info.putNoClobber(wasm.base.allocator, index, .{ - .name = try wasm.base.allocator.dupe(u8, segment_info.name), + try wasm.segment_info.putNoClobber(gpa, index, .{ + .name = try gpa.dupe(u8, segment_info.name), .alignment = segment_info.alignment, .flags = segment_info.flags, }); @@ -3183,7 +3300,8 @@ pub fn getMatchingSegment(wasm: *Wasm, object_index: u16, symbol_index: u32) !u3 /// Appends a new segment with default field values fn appendDummySegment(wasm: *Wasm) !void { - try wasm.segments.append(wasm.base.allocator, .{ + const gpa = wasm.base.comp.gpa; + try wasm.segments.append(gpa, .{ .alignment = .@"1", .size = 0, .offset = 0, @@ -3203,14 +3321,15 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { // and then return said symbol's index. The final table will be populated // during `flush` when we know all possible error names. + const gpa = wasm.base.comp.gpa; const atom_index = try wasm.createAtom(); const atom = wasm.getAtomPtr(atom_index); const slice_ty = Type.slice_const_u8_sentinel_0; - const mod = wasm.base.options.module.?; + const mod = wasm.base.comp.module.?; atom.alignment = slice_ty.abiAlignment(mod); const sym_index = atom.sym_index; - const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_name_table"); + const sym_name = try wasm.string_table.put(gpa, "__zig_err_name_table"); const symbol = &wasm.symbols.items[sym_index]; symbol.* = .{ .name = sym_name, @@ -3222,7 +3341,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); symbol.mark(); - try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {}); + try wasm.resolved_symbols.put(gpa, atom.symbolLoc(), {}); log.debug("Error name table was created with symbol index: ({d})", .{sym_index}); wasm.error_table_symbol = sym_index; @@ -3234,6 +3353,7 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { /// This creates a table that consists of pointers and length to each error name. /// The table is what is being pointed to within the runtime bodies that are generated. fn populateErrorNameTable(wasm: *Wasm) !void { + const gpa = wasm.base.comp.gpa; const symbol_index = wasm.error_table_symbol orelse return; const atom_index = wasm.symbol_atom.get(.{ .file = null, .index = symbol_index }).?; @@ -3243,7 +3363,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void { const names_atom_index = try wasm.createAtom(); const names_atom = wasm.getAtomPtr(names_atom_index); names_atom.alignment = .@"1"; - const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_names"); + const sym_name = try wasm.string_table.put(gpa, "__zig_err_names"); const names_symbol = &wasm.symbols.items[names_atom.sym_index]; names_symbol.* = .{ .name = sym_name, @@ -3259,7 +3379,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void { // Addend for each relocation to the table var addend: u32 = 0; - const mod = wasm.base.options.module.?; + const mod = wasm.base.comp.module.?; for (mod.global_error_set.keys()) |error_name_nts| { const atom = wasm.getAtomPtr(atom_index); @@ -3269,10 +3389,10 @@ fn populateErrorNameTable(wasm: *Wasm) !void { const slice_ty = Type.slice_const_u8_sentinel_0; const offset = @as(u32, @intCast(atom.code.items.len)); // first we create the data for the slice of the name - try atom.code.appendNTimes(wasm.base.allocator, 0, 4); // ptr to name, will be relocated - try atom.code.writer(wasm.base.allocator).writeInt(u32, len - 1, .little); + try atom.code.appendNTimes(gpa, 0, 4); // ptr to name, will be relocated + try atom.code.writer(gpa).writeInt(u32, len - 1, .little); // create relocation to the error name - try atom.relocs.append(wasm.base.allocator, .{ + try atom.relocs.append(gpa, .{ .index = names_atom.sym_index, .relocation_type = .R_WASM_MEMORY_ADDR_I32, .offset = offset, @@ -3282,7 +3402,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void { addend += len; // as we updated the error name table, we now store the actual name within the names atom - try names_atom.code.ensureUnusedCapacity(wasm.base.allocator, len); + try names_atom.code.ensureUnusedCapacity(gpa, len); names_atom.code.appendSliceAssumeCapacity(error_name); names_atom.code.appendAssumeCapacity(0); @@ -3291,8 +3411,8 @@ fn populateErrorNameTable(wasm: *Wasm) !void { names_atom.size = addend; const name_loc = names_atom.symbolLoc(); - try wasm.resolved_symbols.put(wasm.base.allocator, name_loc, {}); - try wasm.symbol_atom.put(wasm.base.allocator, name_loc, names_atom_index); + try wasm.resolved_symbols.put(gpa, name_loc, {}); + try wasm.symbol_atom.put(gpa, name_loc, names_atom_index); // link the atoms with the rest of the binary so they can be allocated // and relocations will be performed. @@ -3304,7 +3424,8 @@ fn populateErrorNameTable(wasm: *Wasm) !void { /// This initializes the index, appends a new segment, /// and finally, creates a managed `Atom`. pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !Atom.Index { - const new_index = @as(u32, @intCast(wasm.segments.items.len)); + const gpa = wasm.base.comp.gpa; + const new_index: u32 = @intCast(wasm.segments.items.len); index.* = new_index; try wasm.appendDummySegment(); @@ -3312,7 +3433,7 @@ pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) ! const atom = wasm.getAtomPtr(atom_index); wasm.symbols.items[atom.sym_index] = .{ .tag = .section, - .name = try wasm.string_table.put(wasm.base.allocator, name), + .name = try wasm.string_table.put(gpa, name), .index = 0, .flags = @intFromEnum(Symbol.Flag.WASM_SYM_BINDING_LOCAL), }; @@ -3322,8 +3443,10 @@ pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) ! } fn resetState(wasm: *Wasm) void { + const gpa = wasm.base.comp.gpa; + for (wasm.segment_info.values()) |segment_info| { - wasm.base.allocator.free(segment_info.name); + gpa.free(segment_info.name); } var atom_it = wasm.decls.valueIterator(); @@ -3357,49 +3480,44 @@ fn resetState(wasm: *Wasm) void { wasm.debug_pubtypes_index = null; } -pub fn flush(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { - if (wasm.base.options.emit == null) { - if (wasm.llvm_object) |llvm_object| { - return try llvm_object.flushModule(comp, prog_node); - } - return; - } +pub fn flush(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { + const comp = wasm.base.comp; + const use_lld = build_options.have_llvm and comp.config.use_lld; + const use_llvm = comp.config.use_llvm; - if (build_options.have_llvm and wasm.base.options.use_lld) { - return wasm.linkWithLLD(comp, prog_node); - } else if (wasm.base.options.use_llvm and !wasm.base.options.use_lld) { - return wasm.linkWithZld(comp, prog_node); + if (use_lld) { + return wasm.linkWithLLD(arena, prog_node); + } else if (use_llvm) { + return wasm.linkWithZld(arena, prog_node); } else { - return wasm.flushModule(comp, prog_node); + return wasm.flushModule(arena, prog_node); } } /// Uses the in-house linker to link one or multiple object -and archive files into a WebAssembly binary. -fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { +fn linkWithZld(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { const tracy = trace(@src()); defer tracy.end(); - const gpa = wasm.base.allocator; - const options = wasm.base.options; + const comp = wasm.base.comp; + const shared_memory = comp.config.shared_memory; + const import_memory = comp.config.import_memory; - // Used for all temporary memory allocated during flushin - var arena_instance = std.heap.ArenaAllocator.init(gpa); - defer arena_instance.deinit(); - const arena = arena_instance.allocator(); - - const directory = options.emit.?.directory; // Just an alias to make it shorter to type. - const full_out_path = try directory.join(arena, &[_][]const u8{options.emit.?.sub_path}); + const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path}); + const opt_zcu = comp.module; + const use_llvm = comp.config.use_llvm; // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. - const module_obj_path: ?[]const u8 = if (options.module != null) blk: { - assert(options.use_llvm); // `linkWithZld` should never be called when the Wasm backend is used - try wasm.flushModule(comp, prog_node); + const module_obj_path: ?[]const u8 = if (opt_zcu != null) blk: { + assert(use_llvm); // `linkWithZld` should never be called when the Wasm backend is used + try wasm.flushModule(arena, prog_node); if (fs.path.dirname(full_out_path)) |dirname| { - break :blk try fs.path.join(arena, &.{ dirname, wasm.base.intermediary_basename.? }); + break :blk try fs.path.join(arena, &.{ dirname, wasm.base.zcu_object_sub_path.? }); } else { - break :blk wasm.base.intermediary_basename.?; + break :blk wasm.base.zcu_object_sub_path.?; } } else null; @@ -3416,12 +3534,14 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l const id_symlink_basename = "zld.id"; var man: Cache.Manifest = undefined; - defer if (!options.disable_lld_caching) man.deinit(); + defer if (!wasm.base.disable_lld_caching) man.deinit(); var digest: [Cache.hex_digest_len]u8 = undefined; + const objects = comp.objects; + // NOTE: The following section must be maintained to be equal // as the section defined in `linkWithLLD` - if (!options.disable_lld_caching) { + if (!wasm.base.disable_lld_caching) { man = comp.cache_parent.obtain(); // We are about to obtain this lock, so here we give other processes a chance first. @@ -3429,7 +3549,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l comptime assert(Compilation.link_hash_implementation_version == 10); - for (options.objects) |obj| { + for (objects) |obj| { _ = try man.addFile(obj.path, null); man.hash.add(obj.must_link); } @@ -3438,19 +3558,19 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l } try man.addOptionalFile(module_obj_path); try man.addOptionalFile(compiler_rt_path); - man.hash.addOptionalBytes(options.entry); - man.hash.addOptional(options.stack_size_override); - man.hash.add(wasm.base.options.build_id); - man.hash.add(options.import_memory); - man.hash.add(options.import_table); - man.hash.add(options.export_table); - man.hash.addOptional(options.initial_memory); - man.hash.addOptional(options.max_memory); - man.hash.add(options.shared_memory); - man.hash.addOptional(options.global_base); - man.hash.add(options.export_symbol_names.len); + man.hash.addOptionalBytes(wasm.entry_name); + man.hash.add(wasm.base.stack_size); + man.hash.add(wasm.base.build_id); + man.hash.add(import_memory); + man.hash.add(shared_memory); + man.hash.add(wasm.import_table); + man.hash.add(wasm.export_table); + man.hash.addOptional(wasm.initial_memory); + man.hash.addOptional(wasm.max_memory); + man.hash.addOptional(wasm.global_base); + man.hash.add(wasm.export_symbol_names.len); // strip does not need to go into the linker hash because it is part of the hash namespace - for (options.export_symbol_names) |symbol_name| { + for (wasm.export_symbol_names) |symbol_name| { man.hash.addBytes(symbol_name); } @@ -3485,30 +3605,36 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l // Positional arguments to the linker such as object files and static archives. var positionals = std.ArrayList([]const u8).init(arena); - try positionals.ensureUnusedCapacity(options.objects.len); + try positionals.ensureUnusedCapacity(objects.len); + + const target = comp.root_mod.resolved_target.result; + const output_mode = comp.config.output_mode; + const link_mode = comp.config.link_mode; + const link_libc = comp.config.link_libc; + const link_libcpp = comp.config.link_libcpp; + const wasi_exec_model = comp.config.wasi_exec_model; // When the target os is WASI, we allow linking with WASI-LIBC - if (options.target.os.tag == .wasi) { - const is_exe_or_dyn_lib = wasm.base.options.output_mode == .Exe or - (wasm.base.options.output_mode == .Lib and wasm.base.options.link_mode == .Dynamic); + if (target.os.tag == .wasi) { + const is_exe_or_dyn_lib = output_mode == .Exe or + (output_mode == .Lib and link_mode == .Dynamic); if (is_exe_or_dyn_lib) { - const wasi_emulated_libs = wasm.base.options.wasi_emulated_libs; - for (wasi_emulated_libs) |crt_file| { + for (comp.wasi_emulated_libs) |crt_file| { try positionals.append(try comp.get_libc_crt_file( arena, wasi_libc.emulatedLibCRFileLibName(crt_file), )); } - if (wasm.base.options.link_libc) { + if (link_libc) { try positionals.append(try comp.get_libc_crt_file( arena, - wasi_libc.execModelCrtFileFullName(wasm.base.options.wasi_exec_model), + wasi_libc.execModelCrtFileFullName(wasi_exec_model), )); try positionals.append(try comp.get_libc_crt_file(arena, "libc.a")); } - if (wasm.base.options.link_libcpp) { + if (link_libcpp) { try positionals.append(comp.libcxx_static_lib.?.full_object_path); try positionals.append(comp.libcxxabi_static_lib.?.full_object_path); } @@ -3519,7 +3645,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l try positionals.append(path); } - for (options.objects) |object| { + for (objects) |object| { try positionals.append(object.path); } @@ -3562,7 +3688,7 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l try wasm.setupExports(); try wasm.writeToFile(enabled_features, emit_features_count, arena); - if (!wasm.base.options.disable_lld_caching) { + if (!wasm.base.disable_lld_caching) { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { @@ -3578,12 +3704,15 @@ fn linkWithZld(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) l } } -pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { +pub fn flushModule(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) link.File.FlushError!void { const tracy = trace(@src()); defer tracy.end(); + const comp = wasm.base.comp; + if (wasm.llvm_object) |llvm_object| { - return try llvm_object.flushModule(comp, prog_node); + try wasm.base.emitLlvmObject(arena, llvm_object, prog_node); + return; } var sub_prog_node = prog_node.start("Wasm Flush", 0); @@ -3593,16 +3722,13 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod // ensure the error names table is populated when an error name is referenced try wasm.populateErrorNameTable(); - // Used for all temporary memory allocated during flushin - var arena_instance = std.heap.ArenaAllocator.init(wasm.base.allocator); - defer arena_instance.deinit(); - const arena = arena_instance.allocator(); + const objects = comp.objects; // Positional arguments to the linker such as object files and static archives. var positionals = std.ArrayList([]const u8).init(arena); - try positionals.ensureUnusedCapacity(wasm.base.options.objects.len); + try positionals.ensureUnusedCapacity(objects.len); - for (wasm.base.options.objects) |object| { + for (objects) |object| { positionals.appendAssumeCapacity(object.path); } @@ -3634,7 +3760,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod try wasm.markReferences(); try wasm.setupErrorsLen(); try wasm.setupImports(); - if (wasm.base.options.module) |mod| { + if (comp.module) |mod| { var decl_it = wasm.decls.iterator(); while (decl_it.next()) |entry| { const decl = mod.declPtr(entry.key_ptr.*); @@ -3649,8 +3775,12 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } else if (Value.fromInterned(variable.init).isUndefDeep(mod)) { // for safe build modes, we store the atom in the data segment, // whereas for unsafe build modes we store it in bss. - const is_initialized = wasm.base.options.optimize_mode == .Debug or - wasm.base.options.optimize_mode == .ReleaseSafe; + const decl_namespace = mod.namespacePtr(decl.src_namespace); + const optimize_mode = decl_namespace.file_scope.mod.optimize_mode; + const is_initialized = switch (optimize_mode) { + .Debug, .ReleaseSafe => true, + .ReleaseFast, .ReleaseSmall => false, + }; try wasm.parseAtom(atom_index, .{ .data = if (is_initialized) .initialized else .uninitialized }); } else { // when the decl is all zeroes, we store the atom in the bss segment, @@ -3685,7 +3815,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod } if (wasm.dwarf) |*dwarf| { - try dwarf.flushModule(wasm.base.options.module.?); + try dwarf.flushModule(comp.module.?); } } @@ -3711,6 +3841,14 @@ fn writeToFile( feature_count: u32, arena: Allocator, ) !void { + const comp = wasm.base.comp; + const gpa = comp.gpa; + const use_llvm = comp.config.use_llvm; + const use_lld = build_options.have_llvm and comp.config.use_lld; + const shared_memory = comp.config.shared_memory; + const import_memory = comp.config.import_memory; + const export_memory = comp.config.export_memory; + // Size of each section header const header_size = 5 + 1; // The amount of sections that will be written @@ -3719,9 +3857,9 @@ fn writeToFile( var code_section_index: ?u32 = null; // Index of the data section. Used to tell relocation table where the section lives. var data_section_index: ?u32 = null; - const is_obj = wasm.base.options.output_mode == .Obj or (!wasm.base.options.use_llvm and wasm.base.options.use_lld); + const is_obj = comp.config.output_mode == .Obj or (!use_llvm and use_lld); - var binary_bytes = std.ArrayList(u8).init(wasm.base.allocator); + var binary_bytes = std.ArrayList(u8).init(gpa); defer binary_bytes.deinit(); const binary_writer = binary_bytes.writer(); @@ -3759,8 +3897,6 @@ fn writeToFile( } // Import section - const import_memory = wasm.base.options.import_memory or is_obj; - const export_memory = wasm.base.options.export_memory; if (wasm.imports.count() != 0 or import_memory) { const header_offset = try reserveVecSectionHeader(&binary_bytes); @@ -3774,8 +3910,8 @@ fn writeToFile( if (import_memory) { const mem_name = if (is_obj) "__linear_memory" else "memory"; const mem_imp: types.Import = .{ - .module_name = try wasm.string_table.put(wasm.base.allocator, wasm.host_name), - .name = try wasm.string_table.put(wasm.base.allocator, mem_name), + .module_name = try wasm.string_table.put(gpa, wasm.host_name), + .name = try wasm.string_table.put(gpa, mem_name), .kind = .{ .memory = wasm.memories.limits }, }; try wasm.emitImport(binary_writer, mem_imp); @@ -3937,7 +4073,7 @@ fn writeToFile( // When the shared-memory option is enabled, we *must* emit the 'data count' section. const data_segments_count = wasm.data_segments.count() - @intFromBool(wasm.data_segments.contains(".bss") and !import_memory); - if (data_segments_count != 0 and wasm.base.options.shared_memory) { + if (data_segments_count != 0 and shared_memory) { const header_offset = try reserveVecSectionHeader(&binary_bytes); try writeVecSectionHeader( binary_bytes.items, @@ -3955,7 +4091,7 @@ fn writeToFile( var atom_index = wasm.atoms.get(code_index).?; // The code section must be sorted in line with the function order. - var sorted_atoms = try std.ArrayList(*const Atom).initCapacity(wasm.base.allocator, wasm.functions.count()); + var sorted_atoms = try std.ArrayList(*const Atom).initCapacity(gpa, wasm.functions.count()); defer sorted_atoms.deinit(); while (true) { @@ -3966,7 +4102,7 @@ fn writeToFile( sorted_atoms.appendAssumeCapacity(atom); // found more code atoms than functions atom_index = atom.prev orelse break; } - std.debug.assert(wasm.functions.count() == sorted_atoms.items.len); + assert(wasm.functions.count() == sorted_atoms.items.len); const atom_sort_fn = struct { fn sort(ctx: *const Wasm, lhs: *const Atom, rhs: *const Atom) bool { @@ -4079,14 +4215,14 @@ fn writeToFile( if (data_section_index) |data_index| { try wasm.emitDataRelocations(&binary_bytes, data_index, symbol_table); } - } else if (!wasm.base.options.strip) { + } else if (comp.config.debug_format != .strip) { try wasm.emitNameSection(&binary_bytes, arena); } - if (!wasm.base.options.strip) { + if (comp.config.debug_format != .strip) { // The build id must be computed on the main sections only, // so we have to do it now, before the debug sections. - switch (wasm.base.options.build_id) { + switch (wasm.base.build_id) { .none => {}, .fast => { var id: [16]u8 = undefined; @@ -4112,7 +4248,7 @@ fn writeToFile( } // if (wasm.dwarf) |*dwarf| { - // const mod = wasm.base.options.module.?; + // const mod = comp.module.?; // try dwarf.writeDbgAbbrev(); // // for debug info and ranges, the address is always 0, // // as locations are always offsets relative to 'code' section. @@ -4121,7 +4257,7 @@ fn writeToFile( // try dwarf.writeDbgLineHeader(); // } - var debug_bytes = std.ArrayList(u8).init(wasm.base.allocator); + var debug_bytes = std.ArrayList(u8).init(gpa); defer debug_bytes.deinit(); const DebugSection = struct { @@ -4299,6 +4435,8 @@ fn emitFeaturesSection(binary_bytes: *std.ArrayList(u8), enabled_features: []con } fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem.Allocator) !void { + const comp = wasm.base.comp; + const import_memory = comp.config.import_memory; const Name = struct { index: u32, name: []const u8, @@ -4337,7 +4475,7 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem for (wasm.data_segments.keys()) |key| { // bss section is not emitted when this condition holds true, so we also // do not output a name for it. - if (!wasm.base.options.import_memory and std.mem.eql(u8, key, ".bss")) continue; + if (!import_memory and std.mem.eql(u8, key, ".bss")) continue; segments.appendAssumeCapacity(.{ .index = data_segment_index, .name = key }); data_segment_index += 1; } @@ -4362,8 +4500,10 @@ fn emitNameSection(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), arena: std.mem } fn emitNameSubsection(wasm: *Wasm, section_id: std.wasm.NameSubsection, names: anytype, writer: anytype) !void { + const gpa = wasm.base.comp.gpa; + // We must emit subsection size, so first write to a temporary list - var section_list = std.ArrayList(u8).init(wasm.base.allocator); + var section_list = std.ArrayList(u8).init(gpa); defer section_list.deinit(); const sub_writer = section_list.writer(); @@ -4441,26 +4581,30 @@ fn emitImport(wasm: *Wasm, writer: anytype, import: types.Import) !void { } } -fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) !void { +fn linkWithLLD(wasm: *Wasm, arena: Allocator, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); - var arena_allocator = std.heap.ArenaAllocator.init(wasm.base.allocator); - defer arena_allocator.deinit(); - const arena = arena_allocator.allocator(); + const comp = wasm.base.comp; + const shared_memory = comp.config.shared_memory; + const export_memory = comp.config.export_memory; + const import_memory = comp.config.import_memory; + const target = comp.root_mod.resolved_target.result; - const directory = wasm.base.options.emit.?.directory; // Just an alias to make it shorter to type. - const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.options.emit.?.sub_path}); + const gpa = comp.gpa; + + const directory = wasm.base.emit.directory; // Just an alias to make it shorter to type. + const full_out_path = try directory.join(arena, &[_][]const u8{wasm.base.emit.sub_path}); // If there is no Zig code to compile, then we should skip flushing the output file because it // will not be part of the linker line anyway. - const module_obj_path: ?[]const u8 = if (wasm.base.options.module != null) blk: { - try wasm.flushModule(comp, prog_node); + const module_obj_path: ?[]const u8 = if (comp.module != null) blk: { + try wasm.flushModule(arena, prog_node); if (fs.path.dirname(full_out_path)) |dirname| { - break :blk try fs.path.join(arena, &.{ dirname, wasm.base.intermediary_basename.? }); + break :blk try fs.path.join(arena, &.{ dirname, wasm.base.zcu_object_sub_path.? }); } else { - break :blk wasm.base.intermediary_basename.?; + break :blk wasm.base.zcu_object_sub_path.?; } } else null; @@ -4469,23 +4613,21 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! sub_prog_node.context.refresh(); defer sub_prog_node.end(); - const is_obj = wasm.base.options.output_mode == .Obj; + const is_obj = comp.config.output_mode == .Obj; const compiler_rt_path: ?[]const u8 = blk: { if (comp.compiler_rt_lib) |lib| break :blk lib.full_object_path; if (comp.compiler_rt_obj) |obj| break :blk obj.full_object_path; break :blk null; }; - const target = wasm.base.options.target; - const id_symlink_basename = "lld.id"; var man: Cache.Manifest = undefined; - defer if (!wasm.base.options.disable_lld_caching) man.deinit(); + defer if (!wasm.base.disable_lld_caching) man.deinit(); var digest: [Cache.hex_digest_len]u8 = undefined; - if (!wasm.base.options.disable_lld_caching) { + if (!wasm.base.disable_lld_caching) { man = comp.cache_parent.obtain(); // We are about to obtain this lock, so here we give other processes a chance first. @@ -4493,7 +4635,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! comptime assert(Compilation.link_hash_implementation_version == 10); - for (wasm.base.options.objects) |obj| { + for (comp.objects) |obj| { _ = try man.addFile(obj.path, null); man.hash.add(obj.must_link); } @@ -4502,20 +4644,20 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } try man.addOptionalFile(module_obj_path); try man.addOptionalFile(compiler_rt_path); - man.hash.addOptionalBytes(wasm.base.options.entry); - man.hash.addOptional(wasm.base.options.stack_size_override); - man.hash.add(wasm.base.options.build_id); - man.hash.add(wasm.base.options.import_memory); - man.hash.add(wasm.base.options.export_memory); - man.hash.add(wasm.base.options.import_table); - man.hash.add(wasm.base.options.export_table); - man.hash.addOptional(wasm.base.options.initial_memory); - man.hash.addOptional(wasm.base.options.max_memory); - man.hash.add(wasm.base.options.shared_memory); - man.hash.addOptional(wasm.base.options.global_base); - man.hash.add(wasm.base.options.export_symbol_names.len); + man.hash.addOptionalBytes(wasm.entry_name); + man.hash.add(wasm.base.stack_size); + man.hash.add(wasm.base.build_id); + man.hash.add(import_memory); + man.hash.add(export_memory); + man.hash.add(wasm.import_table); + man.hash.add(wasm.export_table); + man.hash.addOptional(wasm.initial_memory); + man.hash.addOptional(wasm.max_memory); + man.hash.add(shared_memory); + man.hash.addOptional(wasm.global_base); + man.hash.add(wasm.export_symbol_names.len); // strip does not need to go into the linker hash because it is part of the hash namespace - for (wasm.base.options.export_symbol_names) |symbol_name| { + for (wasm.export_symbol_names) |symbol_name| { man.hash.addBytes(symbol_name); } @@ -4553,8 +4695,8 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! // here. TODO: think carefully about how we can avoid this redundant operation when doing // build-obj. See also the corresponding TODO in linkAsArchive. const the_object_path = blk: { - if (wasm.base.options.objects.len != 0) - break :blk wasm.base.options.objects[0].path; + if (comp.objects.len != 0) + break :blk comp.objects[0].path; if (comp.c_object_table.count() != 0) break :blk comp.c_object_table.keys()[0].status.success.object_path; @@ -4573,7 +4715,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } } else { // Create an LLD command line and invoke it. - var argv = std.ArrayList([]const u8).init(wasm.base.allocator); + var argv = std.ArrayList([]const u8).init(gpa); defer argv.deinit(); // We will invoke ourselves as a child process to gain access to LLD. // This is necessary because LLD does not behave properly as a library - @@ -4582,59 +4724,57 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command }); try argv.append("--error-limit=0"); - if (wasm.base.options.lto) { - switch (wasm.base.options.optimize_mode) { + if (comp.config.lto) { + switch (comp.root_mod.optimize_mode) { .Debug => {}, .ReleaseSmall => try argv.append("-O2"), .ReleaseFast, .ReleaseSafe => try argv.append("-O3"), } } - if (wasm.base.options.import_memory) { + if (import_memory) { try argv.append("--import-memory"); } - if (wasm.base.options.export_memory) { + if (export_memory) { try argv.append("--export-memory"); } - if (wasm.base.options.import_table) { - assert(!wasm.base.options.export_table); + if (wasm.import_table) { + assert(!wasm.export_table); try argv.append("--import-table"); } - if (wasm.base.options.export_table) { - assert(!wasm.base.options.import_table); + if (wasm.export_table) { + assert(!wasm.import_table); try argv.append("--export-table"); } - if (wasm.base.options.gc_sections) |gc| { - // For wasm-ld we only need to specify '--no-gc-sections' when the user explicitly - // specified it as garbage collection is enabled by default. - if (!gc) { - try argv.append("--no-gc-sections"); - } + // For wasm-ld we only need to specify '--no-gc-sections' when the user explicitly + // specified it as garbage collection is enabled by default. + if (!wasm.base.gc_sections) { + try argv.append("--no-gc-sections"); } - if (wasm.base.options.strip) { + if (comp.config.debug_format == .strip) { try argv.append("-s"); } - if (wasm.base.options.initial_memory) |initial_memory| { + if (wasm.initial_memory) |initial_memory| { const arg = try std.fmt.allocPrint(arena, "--initial-memory={d}", .{initial_memory}); try argv.append(arg); } - if (wasm.base.options.max_memory) |max_memory| { + if (wasm.max_memory) |max_memory| { const arg = try std.fmt.allocPrint(arena, "--max-memory={d}", .{max_memory}); try argv.append(arg); } - if (wasm.base.options.shared_memory) { + if (shared_memory) { try argv.append("--shared-memory"); } - if (wasm.base.options.global_base) |global_base| { + if (wasm.global_base) |global_base| { const arg = try std.fmt.allocPrint(arena, "--global-base={d}", .{global_base}); try argv.append(arg); } else { @@ -4646,42 +4786,39 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } // Users are allowed to specify which symbols they want to export to the wasm host. - for (wasm.base.options.export_symbol_names) |symbol_name| { + for (wasm.export_symbol_names) |symbol_name| { const arg = try std.fmt.allocPrint(arena, "--export={s}", .{symbol_name}); try argv.append(arg); } - if (wasm.base.options.rdynamic) { + if (comp.config.rdynamic) { try argv.append("--export-dynamic"); } - if (wasm.base.options.entry) |entry| { - try argv.append("--entry"); - try argv.append(entry); + if (wasm.entry_name) |entry_name| { + try argv.appendSlice(&.{ "--entry", entry_name }); } else { try argv.append("--no-entry"); } - // Increase the default stack size to a more reasonable value of 1MB instead of - // the default of 1 Wasm page being 64KB, unless overridden by the user. - try argv.append("-z"); - const stack_size = wasm.base.options.stack_size_override orelse std.wasm.page_size * 16; - const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}); - try argv.append(arg); + try argv.appendSlice(&.{ + "-z", + try std.fmt.allocPrint(arena, "stack-size={d}", .{wasm.base.stack_size}), + }); - if (wasm.base.options.import_symbols) { + if (wasm.import_symbols) { try argv.append("--allow-undefined"); } - if (wasm.base.options.output_mode == .Lib and wasm.base.options.link_mode == .Dynamic) { + if (comp.config.output_mode == .Lib and comp.config.link_mode == .Dynamic) { try argv.append("--shared"); } - if (wasm.base.options.pie) { + if (comp.config.pie) { try argv.append("--pie"); } // XXX - TODO: add when wasm-ld supports --build-id. - // if (wasm.base.options.build_id) { + // if (wasm.base.build_id) { // try argv.append("--build-id=tree"); // } @@ -4692,26 +4829,25 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } if (target.os.tag == .wasi) { - const is_exe_or_dyn_lib = wasm.base.options.output_mode == .Exe or - (wasm.base.options.output_mode == .Lib and wasm.base.options.link_mode == .Dynamic); + const is_exe_or_dyn_lib = comp.config.output_mode == .Exe or + (comp.config.output_mode == .Lib and comp.config.link_mode == .Dynamic); if (is_exe_or_dyn_lib) { - const wasi_emulated_libs = wasm.base.options.wasi_emulated_libs; - for (wasi_emulated_libs) |crt_file| { + for (comp.wasi_emulated_libs) |crt_file| { try argv.append(try comp.get_libc_crt_file( arena, wasi_libc.emulatedLibCRFileLibName(crt_file), )); } - if (wasm.base.options.link_libc) { + if (comp.config.link_libc) { try argv.append(try comp.get_libc_crt_file( arena, - wasi_libc.execModelCrtFileFullName(wasm.base.options.wasi_exec_model), + wasi_libc.execModelCrtFileFullName(comp.config.wasi_exec_model), )); try argv.append(try comp.get_libc_crt_file(arena, "libc.a")); } - if (wasm.base.options.link_libcpp) { + if (comp.config.link_libcpp) { try argv.append(comp.libcxx_static_lib.?.full_object_path); try argv.append(comp.libcxxabi_static_lib.?.full_object_path); } @@ -4720,7 +4856,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! // Positional arguments to the linker such as object files. var whole_archive = false; - for (wasm.base.options.objects) |obj| { + for (comp.objects) |obj| { if (obj.must_link and !whole_archive) { try argv.append("-whole-archive"); whole_archive = true; @@ -4742,9 +4878,9 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! try argv.append(p); } - if (wasm.base.options.output_mode != .Obj and - !wasm.base.options.skip_linker_dependencies and - !wasm.base.options.link_libc) + if (comp.config.output_mode != .Obj and + !comp.skip_linker_dependencies and + !comp.config.link_libc) { try argv.append(comp.libc_static_lib.?.full_object_path); } @@ -4753,7 +4889,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! try argv.append(p); } - if (wasm.base.options.verbose_link) { + if (comp.verbose_link) { // Skip over our own name so that the LLD linker name is the first argv item. Compilation.dump_argv(argv.items[1..]); } @@ -4828,7 +4964,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! // it, and then can react to that in the same way as trying to run an ELF file // from a foreign CPU architecture. if (fs.has_executable_bit and target.os.tag == .wasi and - wasm.base.options.output_mode == .Exe) + comp.config.output_mode == .Exe) { // TODO: what's our strategy for reporting linker errors from this function? // report a nice error here with the file path if it fails instead of @@ -4838,7 +4974,7 @@ fn linkWithLLD(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Node) ! } } - if (!wasm.base.options.disable_lld_caching) { + if (!wasm.base.disable_lld_caching) { // Update the file with the digest. If it fails we can continue; it only // means that the next invocation will have an unnecessary cache miss. Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { @@ -5089,10 +5225,13 @@ fn emitDataRelocations( } fn hasPassiveInitializationSegments(wasm: *const Wasm) bool { + const comp = wasm.base.comp; + const import_memory = comp.config.import_memory; + var it = wasm.data_segments.iterator(); while (it.next()) |entry| { const segment: Segment = wasm.segments.items[entry.value_ptr.*]; - if (segment.needsPassiveInitialization(wasm.base.options.import_memory, entry.key_ptr.*)) { + if (segment.needsPassiveInitialization(import_memory, entry.key_ptr.*)) { return true; } } @@ -5113,14 +5252,15 @@ pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { if (wasm.getTypeIndex(func_type)) |index| { return index; } + const gpa = wasm.base.comp.gpa; // functype does not exist. - const index = @as(u32, @intCast(wasm.func_types.items.len)); - const params = try wasm.base.allocator.dupe(std.wasm.Valtype, func_type.params); - errdefer wasm.base.allocator.free(params); - const returns = try wasm.base.allocator.dupe(std.wasm.Valtype, func_type.returns); - errdefer wasm.base.allocator.free(returns); - try wasm.func_types.append(wasm.base.allocator, .{ + const index: u32 = @intCast(wasm.func_types.items.len); + const params = try gpa.dupe(std.wasm.Valtype, func_type.params); + errdefer gpa.free(params); + const returns = try gpa.dupe(std.wasm.Valtype, func_type.returns); + errdefer gpa.free(returns); + try wasm.func_types.append(gpa, .{ .params = params, .returns = returns, }); @@ -5131,9 +5271,10 @@ pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 { /// Asserts declaration has an associated `Atom`. /// Returns the index into the list of types. pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: std.wasm.Type) !u32 { + const gpa = wasm.base.comp.gpa; const atom_index = wasm.decls.get(decl_index).?; const index = try wasm.putOrGetFuncType(func_type); - try wasm.atom_types.put(wasm.base.allocator, atom_index, index); + try wasm.atom_types.put(gpa, atom_index, index); return index; } @@ -5142,19 +5283,20 @@ pub fn storeDeclType(wasm: *Wasm, decl_index: InternPool.DeclIndex, func_type: s fn markReferences(wasm: *Wasm) !void { const tracy = trace(@src()); defer tracy.end(); - const do_garbage_collect = wasm.base.options.gc_sections orelse - (wasm.base.options.output_mode != .Obj); + + const do_garbage_collect = wasm.base.gc_sections; + const comp = wasm.base.comp; for (wasm.resolved_symbols.keys()) |sym_loc| { const sym = sym_loc.getSymbol(wasm); - if (sym.isExported(wasm.base.options.rdynamic) or sym.isNoStrip() or !do_garbage_collect) { + if (sym.isExported(comp.config.rdynamic) or sym.isNoStrip() or !do_garbage_collect) { try wasm.mark(sym_loc); continue; } // Debug sections may require to be parsed and marked when it contains // relocations to alive symbols. - if (sym.tag == .section and !wasm.base.options.strip) { + if (sym.tag == .section and comp.config.debug_format != .strip) { const file = sym_loc.file orelse continue; // Incremental debug info is done independently const object = &wasm.objects.items[file]; const atom_index = try Object.parseSymbolIntoAtom(object, file, sym_loc.index, wasm); @@ -5200,3 +5342,10 @@ fn mark(wasm: *Wasm, loc: SymbolLoc) !void { try wasm.mark(target_loc.finalLoc(wasm)); } } + +fn defaultEntrySymbolName(wasi_exec_model: std.builtin.WasiExecModel) []const u8 { + return switch (wasi_exec_model) { + .reactor => "_initialize", + .command => "_start", + }; +} diff --git a/src/link/Wasm/Object.zig b/src/link/Wasm/Object.zig index 610c534c8875..f0c21b8c89c8 100644 --- a/src/link/Wasm/Object.zig +++ b/src/link/Wasm/Object.zig @@ -883,6 +883,8 @@ fn assertEnd(reader: anytype) !void { /// Parses an object file into atoms, for code and data sections pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32, wasm: *Wasm) !Atom.Index { + const comp = wasm.base.comp; + const gpa = comp.gpa; const symbol = &object.symtable[symbol_index]; const relocatable_data: RelocatableData = switch (symbol.tag) { .function => object.relocatable_data.get(.code).?[symbol.index - object.importedCountByKind(.function)], @@ -900,7 +902,7 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32 }; const final_index = try wasm.getMatchingSegment(object_index, symbol_index); const atom_index = @as(Atom.Index, @intCast(wasm.managed_atoms.items.len)); - const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); + const atom = try wasm.managed_atoms.addOne(gpa); atom.* = Atom.empty; try wasm.appendAtomAtIndex(final_index, atom_index); @@ -910,7 +912,7 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32 atom.alignment = relocatable_data.getAlignment(object); atom.code = std.ArrayListUnmanaged(u8).fromOwnedSlice(relocatable_data.data[0..relocatable_data.size]); atom.original_offset = relocatable_data.offset; - try wasm.symbol_atom.putNoClobber(wasm.base.allocator, atom.symbolLoc(), atom_index); + try wasm.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom_index); const segment: *Wasm.Segment = &wasm.segments.items[final_index]; if (relocatable_data.type == .data) { //code section and custom sections are 1-byte aligned segment.alignment = segment.alignment.max(atom.alignment); @@ -927,7 +929,7 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32 .R_WASM_TABLE_INDEX_SLEB, .R_WASM_TABLE_INDEX_SLEB64, => { - try wasm.function_table.put(wasm.base.allocator, .{ + try wasm.function_table.put(gpa, .{ .file = object_index, .index = reloc.index, }, 0); @@ -938,7 +940,7 @@ pub fn parseSymbolIntoAtom(object: *Object, object_index: u16, symbol_index: u32 const sym = object.symtable[reloc.index]; if (sym.tag != .global) { try wasm.got_symbols.append( - wasm.base.allocator, + gpa, .{ .file = object_index, .index = reloc.index }, ); } diff --git a/src/main.zig b/src/main.zig index 3f22d8575643..4442234bd299 100644 --- a/src/main.zig +++ b/src/main.zig @@ -21,7 +21,6 @@ const introspect = @import("introspect.zig"); const EnvVar = introspect.EnvVar; const LibCInstallation = @import("libc_installation.zig").LibCInstallation; const wasi_libc = @import("wasi_libc.zig"); -const BuildId = std.Build.CompileStep.BuildId; const Cache = std.Build.Cache; const target_util = @import("target.zig"); const crash_report = @import("crash_report.zig"); @@ -88,13 +87,15 @@ const normal_usage = \\ fetch Copy a package into global cache and print its hash \\ init Initialize a Zig package in the current directory \\ - \\ ast-check Look for simple compile errors in any set of files \\ build-exe Create executable from source or object files \\ build-lib Create library from source or object files \\ build-obj Create object from source or object files - \\ fmt Reformat Zig source into canonical form + \\ test Perform unit testing \\ run Create executable and run immediately - \\ test Create and run a test build + \\ + \\ ast-check Look for simple compile errors in any set of files + \\ fmt Reformat Zig source into canonical form + \\ reduce Minimize a bug report \\ translate-c Convert C code to Zig code \\ \\ ar Use Zig as a drop-in archiver @@ -270,8 +271,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")) { @@ -322,13 +321,14 @@ 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 info = try detectNativeTargetInfo(.{}); + const host = resolveTargetQueryOrFatal(.{}); const stdout = io.getStdOut().writer(); - return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target); + return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, host); } else if (mem.eql(u8, cmd, "version")) { try std.io.getStdOut().writeAll(build_options.version ++ "\n"); - // Check libc++ linkage to make sure Zig was built correctly, but only for "env" and "version" - // to avoid affecting the startup time for build-critical commands (check takes about ~10 μs) + // Check libc++ linkage to make sure Zig was built correctly, but only + // for "env" and "version" to avoid affecting the startup time for + // build-critical commands (check takes about ~10 μs) return verifyLibcxxCorrectlyLinked(); } else if (mem.eql(u8, cmd, "env")) { verifyLibcxxCorrectlyLinked(); @@ -404,37 +404,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 +477,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 +496,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 +514,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 +550,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 +566,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 +761,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 +787,11 @@ 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,54 +819,29 @@ 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; var version_script: ?[]const u8 = null; var disable_c_depfile = false; - var linker_sort_section: ?link.SortSection = null; + var linker_sort_section: ?link.File.Elf.SortSection = null; var linker_gc_sections: ?bool = null; - var linker_compress_debug_sections: ?link.CompressDebugSections = null; + var linker_compress_debug_sections: ?link.File.Elf.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; var linker_print_map: bool = false; - var linker_opt_bisect_limit: i32 = -1; + var llvm_opt_bisect_limit: c_int = -1; var linker_z_nocopyreloc = false; var linker_z_nodelete = false; var linker_z_notext = false; @@ -867,25 +854,17 @@ fn buildOutputType( var linker_tsaware = false; var linker_nxcompat = false; var linker_dynamicbase = true; - var linker_optimization: ?u8 = null; + var linker_optimization: ?[]const 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 entry: Compilation.CreateOptions.Entry = .default; 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 stack_size: ?u64 = null; + var image_base: ?u64 = null; var link_eh_frame_hdr = false; var link_emit_relocs = false; var each_lib_rpath: ?bool = null; - var build_id: ?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 build_id: ?std.zig.BuildId = null; var runtime_args_start: ?usize = null; var test_filter: ?[]const u8 = null; var test_name_prefix: ?[]const u8 = null; @@ -893,16 +872,14 @@ 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 major_subsystem_version: ?u16 = null; + var minor_subsystem_version: ?u16 = null; var enable_link_snapshots: bool = false; var debug_incremental: bool = false; var install_name: ?[]const u8 = null; - var hash_style: link.HashStyle = .both; + var hash_style: link.File.Elf.HashStyle = .both; var entitlements: ?[]const u8 = null; var pagezero_size: ?u64 = null; var lib_search_strategy: SystemLib.SearchStrategy = .paths_first; @@ -910,63 +887,103 @@ 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 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 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 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 cc_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); - - // The dependency string for the root package - var root_deps_str: ?[]const u8 = null; + // 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`. + .any_c_source_files = undefined, + }, + // Populated in the call to `createModule` for the root module. + .resolved_options = undefined, + + .system_libs = .{}, + .resolved_system_libs = .{}, + .wasi_emulated_libs = .{}, + + .c_source_files = .{}, + .rc_source_files = .{}, + + .llvm_m_args = .{}, + .sysroot = null, + .lib_dirs = .{}, // populated by createModule() + .lib_dir_args = .{}, // populated from CLI arg parsing + .libc_installation = null, + .want_native_include_dirs = false, + .frameworks = .{}, + .framework_dirs = .{}, + .rpath_list = .{}, + .libc_paths_file = try EnvVar.ZIG_LIBC.get(arena), + .link_objects = .{}, + .native_system_include_paths = &.{}, + }; // 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 +994,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 +1016,77 @@ 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; + if (mod_opts.strip == false) + create_module.opts.any_non_stripped = true; + if (mod_opts.error_tracing == true) + create_module.opts.any_error_tracing = true; + + const root_src = try introspect.resolvePath(arena, root_src_orig); + gop.value_ptr.* = .{ + .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, - }); - } 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(); + }, + .cc_argv = try cc_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; } 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 +1099,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,12 +1112,12 @@ 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; + mod_opts.structured_cfg = true; } else if (mem.startsWith(u8, arg, "-fno-structured-cfg")) { - want_structured_cfg = false; + mod_opts.structured_cfg = false; } else if (mem.eql(u8, arg, "--color")) { const next_arg = args_iter.next() orelse { fatal("expected [auto|on|off] after --color", .{}); @@ -1086,46 +1128,40 @@ 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..]; + 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| { - fatal("unable to parse stack size '{s}': {s}", .{ next_arg, @errorName(err) }); - }; + stack_size = parseStackSize(args_iter.nextOrFatal()); } else if (mem.eql(u8, arg, "--image-base")) { - const next_arg = args_iter.nextOrFatal(); - image_base_override = std.fmt.parseUnsigned(u64, next_arg, 0) catch |err| { - fatal("unable to parse image base override '{s}': {s}", .{ next_arg, @errorName(err) }); - }; + image_base = parseImageBase(args_iter.nextOrFatal()); } else if (mem.eql(u8, arg, "--name")) { provided_name = args_iter.nextOrFatal(); 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 create_module.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 create_module.lib_dir_args.append(arena, args_iter.nextOrFatal()); } else if (mem.eql(u8, arg, "-F")) { - try framework_dirs.append(args_iter.nextOrFatal()); + try create_module.framework_dirs.append(arena, args_iter.nextOrFatal()); } else if (mem.eql(u8, arg, "-framework")) { - try frameworks.put(gpa, args_iter.nextOrFatal(), .{}); + try create_module.frameworks.put(arena, args_iter.nextOrFatal(), .{}); } else if (mem.eql(u8, arg, "-weak_framework")) { - try frameworks.put(gpa, args_iter.nextOrFatal(), .{ .weak = true }); + try create_module.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 create_module.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=")) { const param = arg["--compress-debug-sections=".len..]; - linker_compress_debug_sections = std.meta.stringToEnum(link.CompressDebugSections, param) orelse { + linker_compress_debug_sections = std.meta.stringToEnum(link.File.Elf.CompressDebugSections, param) orelse { fatal("expected --compress-debug-sections=[none|zlib|zstd], found '{s}'", .{param}); }; } else if (mem.eql(u8, arg, "--compress-debug-sections")) { - linker_compress_debug_sections = link.CompressDebugSections.zlib; + linker_compress_debug_sections = link.File.Elf.CompressDebugSections.zlib; } else if (mem.eql(u8, arg, "-pagezero_size")) { const next_arg = args_iter.nextOrFatal(); pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| { @@ -1168,7 +1204,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 +1215,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 cc_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, &cc_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, &cc_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, &cc_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, &cc_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, &cc_argv, .iframework, arg, path, false); + try create_module.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, &cc_argv, .iframeworkwithsysroot, arg, path, false); + try create_module.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,23 +1257,23 @@ 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(); + create_module.sysroot = next_arg; + try cc_argv.appendSlice(arena, &.{ "-isysroot", next_arg }); } else if (mem.eql(u8, arg, "--libc")) { - libc_paths_file = args_iter.nextOrFatal(); + create_module.libc_paths_file = args_iter.nextOrFatal(); } else if (mem.eql(u8, arg, "--test-filter")) { test_filter = args_iter.nextOrFatal(); } else if (mem.eql(u8, arg, "--test-name-prefix")) { @@ -1258,7 +1293,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 +1333,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 +1341,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,11 +1410,11 @@ 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; + create_module.opts.rdynamic = true; } else if (mem.eql(u8, arg, "-fsoname")) { soname = .yes_default_value; } else if (mem.startsWith(u8, arg, "-fsoname=")) { @@ -1432,36 +1467,36 @@ 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")) { - dll_export_fns = true; + create_module.opts.dll_export_fns = true; } else if (mem.eql(u8, arg, "-fno-dll-export-fns")) { - dll_export_fns = false; + create_module.opts.dll_export_fns = false; } else if (mem.eql(u8, arg, "--show-builtin")) { 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"; + create_module.opts.debug_format = .{ .dwarf = .@"32" }; } else if (mem.eql(u8, arg, "-gdwarf64")) { - dwarf_format = .@"64"; + create_module.opts.debug_format = .{ .dwarf = .@"64" }; } else if (mem.eql(u8, arg, "-fformatted-panics")) { formatted_panics = true; } else if (mem.eql(u8, arg, "-fno-formatted-panics")) { 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")) { @@ -1475,7 +1510,9 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "-fno-builtin")) { no_builtin = true; } else if (mem.startsWith(u8, arg, "-fopt-bisect-limit=")) { - linker_opt_bisect_limit = std.math.lossyCast(i32, parseIntSuffix(arg, "-fopt-bisect-limit=".len)); + const next_arg = arg["-fopt-bisect-limit=".len..]; + llvm_opt_bisect_limit = std.fmt.parseInt(c_int, next_arg, 0) catch |err| + fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); } else if (mem.eql(u8, arg, "--eh-frame-hdr")) { link_eh_frame_hdr = true; } else if (mem.eql(u8, arg, "--dynamicbase")) { @@ -1518,13 +1555,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 (entry) { + .default, .disabled => entry = .enabled, + .enabled, .named => {}, + } } else if (mem.eql(u8, arg, "-fno-entry")) { - linker_force_entry = false; + 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 +1576,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")) { @@ -1551,7 +1591,7 @@ fn buildOutputType( build_id = .fast; } else if (mem.startsWith(u8, arg, "--build-id=")) { const style = arg["--build-id=".len..]; - build_id = BuildId.parse(style) catch |err| { + build_id = std.zig.BuildId.parse(style) catch |err| { fatal("unable to parse --build-id style '{s}': {s}", .{ style, @errorName(err), }); @@ -1585,37 +1625,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 create_module.lib_dir_args.append(arena, arg[2..]); } else if (mem.startsWith(u8, arg, "-F")) { - try framework_dirs.append(arg[2..]); + try create_module.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 cc_argv.append(arena, arg); } else if (mem.startsWith(u8, arg, "-I")) { - try cssan.addIncludePath(.I, arg, arg[2..], true); + try cssan.addIncludePath(arena, &cc_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 +1666,31 @@ 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)) { + .shared_library => { + try create_module.link_objects.append(arena, .{ .path = arg }); + create_module.opts.any_dyn_libs = true; + }, + .object, .static_library => { + try create_module.link_objects.append(arena, .{ .path = arg }); + }, + .res => { + try create_module.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 +1698,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,19 +1718,15 @@ 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; - want_native_include_dirs = true; + create_module.opts.ensure_libc_on_non_freestanding = true; + create_module.opts.ensure_libcpp_on_non_freestanding = arg_mode == .cpp; + create_module.want_native_include_dirs = true; // Clang's driver enables this switch unconditionally. // Disabling the emission of .eh_frame_hdr can unexpectedly break // some functionality that depend on it, such as C++ exceptions and @@ -1733,24 +1779,37 @@ fn buildOutputType( } }, .other => { - try clang_argv.appendSlice(it.other_args); + try cc_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, - }), + .shared_library => { + try create_module.link_objects.append(arena, .{ + .path = it.only_arg, + .must_link = must_link, + }); + create_module.opts.any_dyn_libs = true; + }, + .unknown, .object, .static_library => { + try create_module.link_objects.append(arena, .{ + .path = it.only_arg, + .must_link = must_link, + }); + }, + .res => { + try create_module.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 +1819,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 +1840,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 create_module.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 +1859,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,26 +1877,26 @@ 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, + .rdynamic => create_module.opts.rdynamic = true, .wl => { var split_it = mem.splitScalar(u8, it.only_arg, ','); while (split_it.next()) |linker_arg| { @@ -1847,7 +1910,7 @@ fn buildOutputType( const key = linker_arg[0..equals_pos]; const value = linker_arg[equals_pos + 1 ..]; if (mem.eql(u8, key, "--build-id")) { - build_id = BuildId.parse(value) catch |err| { + build_id = std.zig.BuildId.parse(value) catch |err| { fatal("unable to parse --build-id style '{s}': {s}", .{ value, @errorName(err), }); @@ -1870,7 +1933,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 +1975,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 cc_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 cc_argv.append(arena, "-gline-tables-only"); } else { - try clang_argv.appendSlice(it.other_args); + try cc_argv.appendSlice(arena, it.other_args); } }, .gdwarf32 => { - strip = false; - dwarf_format = .@"32"; + mod_opts.strip = false; + create_module.opts.debug_format = .{ .dwarf = .@"32" }; }, .gdwarf64 => { - strip = false; - dwarf_format = .@"64"; + mod_opts.strip = false; + create_module.opts.debug_format = .{ .dwarf = .@"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 cc_argv.appendSlice(arena, it.other_args); } }, .linker_script => linker_script = it.only_arg, @@ -1964,65 +2027,63 @@ 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 cc_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 cc_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 create_module.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 cc_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 cc_argv.appendSlice(arena, it.other_args); }, - .framework_dir => try framework_dirs.append(it.only_arg), - .framework => try frameworks.put(gpa, it.only_arg, .{}), - .nostdlibinc => want_native_include_dirs = false, - .strip => strip = true, + .framework_dir => try create_module.framework_dirs.append(arena, it.only_arg), + .framework => try create_module.frameworks.put(arena, it.only_arg, .{}), + .nostdlibinc => create_module.want_native_include_dirs = false, + .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; + create_module.sysroot = it.only_arg; }, .entry => { - entry = it.only_arg; + 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 create_module.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) { linker_compress_debug_sections = .zlib; } else { - linker_compress_debug_sections = std.meta.stringToEnum(link.CompressDebugSections, it.only_arg) orelse { + linker_compress_debug_sections = std.meta.stringToEnum(link.File.Elf.CompressDebugSections, it.only_arg) orelse { fatal("expected [none|zlib|zstd] after --compress-debug-sections, found '{s}'", .{it.only_arg}); }; } @@ -2077,30 +2138,25 @@ fn buildOutputType( } provided_name = name[prefix..end]; } else if (mem.eql(u8, arg, "-rpath")) { - try rpath_list.append(linker_args_it.nextOrFatal()); + try create_module.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")) { - rdynamic = true; + create_module.opts.rdynamic = true; } else if (mem.eql(u8, arg, "-version-script") or mem.eql(u8, arg, "--version-script")) { version_script = linker_args_it.nextOrFatal(); } else if (mem.eql(u8, arg, "-O")) { - const opt = linker_args_it.nextOrFatal(); - linker_optimization = std.fmt.parseUnsigned(u8, opt, 10) catch |err| { - fatal("unable to parse optimization level '{s}': {s}", .{ opt, @errorName(err) }); - }; + linker_optimization = linker_args_it.nextOrFatal(); } else if (mem.startsWith(u8, arg, "-O")) { - linker_optimization = std.fmt.parseUnsigned(u8, arg["-O".len..], 10) catch |err| { - fatal("unable to parse optimization level '{s}': {s}", .{ arg, @errorName(err) }); - }; + linker_optimization = arg["-O".len..]; } else if (mem.eql(u8, arg, "-pagezero_size")) { const next_arg = linker_args_it.nextOrFatal(); pagezero_size = std.fmt.parseUnsigned(u64, eatIntPrefix(next_arg, 16), 16) catch |err| { @@ -2131,7 +2187,7 @@ fn buildOutputType( linker_print_map = true; } else if (mem.eql(u8, arg, "--sort-section")) { const arg1 = linker_args_it.nextOrFatal(); - linker_sort_section = std.meta.stringToEnum(link.SortSection, arg1) orelse { + linker_sort_section = std.meta.stringToEnum(link.File.Elf.SortSection, arg1) orelse { fatal("expected [name|alignment] after --sort-section, found '{s}'", .{arg1}); }; } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or @@ -2145,9 +2201,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 +2211,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; + 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,17 +2223,17 @@ 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 { + linker_compress_debug_sections = std.meta.stringToEnum(link.File.Elf.CompressDebugSections, arg1) orelse { fatal("expected [none|zlib|zstd] after --compress-debug-sections, found '{s}'", .{arg1}); }; } else if (mem.startsWith(u8, arg, "-z")) { @@ -2208,10 +2264,7 @@ fn buildOutputType( } else if (mem.eql(u8, z_arg, "norelro")) { linker_z_relro = false; } else if (mem.startsWith(u8, z_arg, "stack-size=")) { - const next_arg = z_arg["stack-size=".len..]; - stack_size_override = std.fmt.parseUnsigned(u64, next_arg, 0) catch |err| { - fatal("unable to parse stack size '{s}': {s}", .{ next_arg, @errorName(err) }); - }; + stack_size = parseStackSize(z_arg["stack-size=".len..]); } else if (mem.startsWith(u8, z_arg, "common-page-size=")) { linker_z_common_page_size = parseIntSuffix(z_arg, "common-page-size=".len); } else if (mem.startsWith(u8, z_arg, "max-page-size=")) { @@ -2232,19 +2285,13 @@ fn buildOutputType( }; have_version = true; } else if (mem.eql(u8, arg, "-e") or mem.eql(u8, arg, "--entry")) { - entry = linker_args_it.nextOrFatal(); + 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| { - fatal("unable to parse stack size override '{s}': {s}", .{ stack_size, @errorName(err) }); - }; + stack_size = parseStackSize(linker_args_it.nextOrFatal()); } else if (mem.eql(u8, arg, "--image-base")) { - const image_base = linker_args_it.nextOrFatal(); - image_base_override = std.fmt.parseUnsigned(u64, image_base, 0) catch |err| { - fatal("unable to parse image base override '{s}': {s}", .{ image_base, @errorName(err) }); - }; + image_base = parseImageBase(linker_args_it.nextOrFatal()); } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) { linker_script = linker_args_it.nextOrFatal(); } else if (mem.eql(u8, arg, "--eh-frame-hdr")) { @@ -2262,7 +2309,7 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "--high-entropy-va")) { // This option does not do anything. } else if (mem.eql(u8, arg, "--export-all-symbols")) { - rdynamic = true; + create_module.opts.rdynamic = true; } else if (mem.eql(u8, arg, "--color-diagnostics") or mem.eql(u8, arg, "--color-diagnostics=always")) { @@ -2276,7 +2323,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")) { @@ -2290,44 +2337,40 @@ fn buildOutputType( _ = linker_args_it.nextOrFatal(); } else if (mem.eql(u8, arg, "--major-subsystem-version")) { const major = linker_args_it.nextOrFatal(); - major_subsystem_version = std.fmt.parseUnsigned( - u32, - major, - 10, - ) catch |err| { - fatal("unable to parse major subsystem version '{s}': {s}", .{ major, @errorName(err) }); + major_subsystem_version = std.fmt.parseUnsigned(u16, major, 10) catch |err| { + fatal("unable to parse major subsystem version '{s}': {s}", .{ + major, @errorName(err), + }); }; } else if (mem.eql(u8, arg, "--minor-subsystem-version")) { const minor = linker_args_it.nextOrFatal(); - minor_subsystem_version = std.fmt.parseUnsigned( - u32, - minor, - 10, - ) catch |err| { - fatal("unable to parse minor subsystem version '{s}': {s}", .{ minor, @errorName(err) }); + minor_subsystem_version = std.fmt.parseUnsigned(u16, minor, 10) catch |err| { + fatal("unable to parse minor subsystem version '{s}': {s}", .{ + minor, @errorName(err), + }); }; } else if (mem.eql(u8, arg, "-framework")) { - try frameworks.put(gpa, linker_args_it.nextOrFatal(), .{}); + try create_module.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 create_module.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 create_module.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 +2404,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 create_module.link_objects.append(arena, .{ .path = linker_args_it.nextOrFatal(), .must_link = true, }); @@ -2369,7 +2412,7 @@ fn buildOutputType( mem.eql(u8, arg, "--hash-style")) { const next_arg = linker_args_it.nextOrFatal(); - hash_style = std.meta.stringToEnum(link.HashStyle, next_arg) orelse { + hash_style = std.meta.stringToEnum(link.File.Elf.HashStyle, next_arg) orelse { fatal("expected [sysv|gnu|both] after --hash-style, found '{s}'", .{ next_arg, }); @@ -2402,22 +2445,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 +2477,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 +2494,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,8 +2508,8 @@ fn buildOutputType( } }, } - if (c_source_files.items.len == 0 and - link_objects.items.len == 0 and + if (create_module.c_source_files.items.len == 0 and + create_module.link_objects.items.len == 0 and root_src_file == null) { // For example `zig cc` and no args should print the "no input files" message. @@ -2476,258 +2519,115 @@ 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", .{}); + if (show_builtin and root_src_file == null) { + // Without this, there will be no main module created and no zig + // compilation unit, and therefore also no builtin.zig contents + // created. + root_src_file = "builtin.zig"; } - 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.zig.CrossTarget.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.zig.CrossTarget.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); + implicit_root_mod: { + const unresolved_src_path = b: { + if (root_src_file) |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, + src_path, + }); + } + create_module.opts.have_zcu = true; + break :b src_path; } - var mcpu_buffer = std.ArrayList(u8).init(gpa); - defer mcpu_buffer.deinit(); - - try mcpu_buffer.appendSlice(target_mcpu orelse "baseline"); + if (create_module.modules.count() != 0) + break :implicit_root_mod; - 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; - } - } + if (create_module.c_source_files.items.len >= 1) + break :b create_module.c_source_files.items[0].src_path; - 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; - } - } + if (create_module.link_objects.items.len >= 1) + break :b create_module.link_objects.items[0].path; - const cross_target = try parseCrossTargetOrReportFatalError(arena, target_parse_options); - const target_info = try detectNativeTargetInfo(cross_target); + if (emit_bin == .yes) + break :b emit_bin.yes; - if (target_info.target.os.tag != .freestanding) { - if (ensure_libc_on_non_freestanding) - link_libc = true; - if (ensure_libcpp_on_non_freestanding) - link_libcpp = true; - } + if (create_module.rc_source_files.items.len >= 1) + break :b create_module.rc_source_files.items[0].src_path; - if (linker_force_entry) |force| { - if (!force) { - entry = null; - } else if (entry == null and output_mode == .Exe) { - entry = switch (target_info.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_info.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 (arg_mode == .run) + fatal("`zig run` expects at least one positional argument", .{}); - if (target_info.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)", .{}); - } - } + fatal("expected a positional argument, -femit-bin=[path], --show-builtin, or --name [name]", .{}); - if (target_info.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", .{}); - } + break :implicit_root_mod; + }; - if (!target_info.target.cpu.features.isEnabled(@intFromEnum(std.Target.wasm.Feature.atomics)) or - !target_info.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; - } + // See duplicate logic: ModCreationGlobalFlags + 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 (mod_opts.strip == false) + create_module.opts.any_non_stripped = true; + if (mod_opts.error_tracing == true) + create_module.opts.any_error_tracing = true; - // 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'", .{}); - } - } + const src_path = try introspect.resolvePath(arena, unresolved_src_path); + const name = if (arg_mode == .zig_test) + "test" + else + fs.path.stem(fs.path.basename(src_path)); - if (use_lld) |opt| { - if (opt and cross_target.isDarwin()) { - fatal("LLD requested with Mach-O object format. Only the self-hosted linker is supported for this target.", .{}); - } + try create_module.modules.put(arena, name, .{ + .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 cc_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 (want_lto) |opt| { - if (opt and cross_target.isDarwin()) { - fatal("LTO is not yet supported with the Mach-O object format. More details: https://github.com/ziglang/zig/issues/8680", .{}); - } + if (!create_module.opts.have_zcu and arg_mode == .zig_test) { + fatal("`zig test` expects a zig source file argument", .{}); } - 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,290 +2657,119 @@ 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 resolved_system_libs: std.MultiArrayList(struct { - name: []const u8, - lib: Compilation.SystemLib, - }) = .{}; - - var libc_installation: ?LibCInstallation = null; - if (libc_paths_file) |paths_file| { - libc_installation = LibCInstallation.parse(arena, paths_file, cross_target) catch |err| { - fatal("unable to parse libc paths file at path {s}: {s}", .{ paths_file, @errorName(err) }); - }; - } - - for (system_libs.keys(), system_libs.values()) |lib_name, info| { - if (target_util.is_libc_lib_name(target_info.target, lib_name)) { - link_libc = true; - continue; - } - if (target_util.is_libcpp_lib_name(target_info.target, lib_name)) { - link_libcpp = true; - continue; - } - switch (target_util.classifyCompilerRtLibName(target_info.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; - }, - } - - if (target_info.target.isMinGW()) { - const exists = mingw.libExists(arena, target_info.target, zig_lib_directory, lib_name) catch |err| { - fatal("failed to check zig installation for DLL import libs: {s}", .{ - @errorName(err), - }); + var global_cache_directory: Compilation.Directory = l: { + if (override_global_cache_dir) |p| { + break :l .{ + .handle = try fs.cwd().makeOpenPath(p, .{}), + .path = p, }; - if (exists) { - try 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_info.target.os.tag == .wasi) { - if (wasi_libc.getEmulatedLibCRTFile(lib_name)) |crt_file| { - try wasi_emulated_libs.append(crt_file); - continue; - } + if (builtin.os.tag == .wasi) { + break :l getWasiPreopen("/cache"); } - - try external_system_libs.append(arena, .{ - .name = lib_name, - .info = info, - }); - } - // After this point, external_system_libs is used instead of system_libs. - - // Trigger native system library path detection if necessary. - if (sysroot == null and cross_target.isNativeOs() and cross_target.isNativeAbi() and - (external_system_libs.len != 0 or want_native_include_dirs)) - { - const paths = std.zig.system.NativePaths.detect(arena, target_info) catch |err| { - fatal("unable to detect native system paths: {s}", .{@errorName(err)}); + const p = try introspect.resolveGlobalCacheDir(arena); + break :l .{ + .handle = try fs.cwd().makeOpenPath(p, .{}), + .path = p, }; - for (paths.warnings.items) |warning| { - warn("{s}", .{warning}); - } - - try clang_argv.ensureUnusedCapacity(paths.include_dirs.items.len * 2); - for (paths.include_dirs.items) |include_dir| { - clang_argv.appendAssumeCapacity("-isystem"); - clang_argv.appendAssumeCapacity(include_dir); - } + }; + defer global_cache_directory.handle.close(); - try framework_dirs.appendSlice(paths.framework_dirs.items); - try lib_dirs.appendSlice(paths.lib_dirs.items); - try rpath_list.appendSlice(paths.rpaths.items); + if (linker_optimization) |o| { + warn("ignoring deprecated linker optimization setting '{s}'", .{o}); } - if (builtin.target.os.tag == .windows and - target_info.target.abi == .msvc and - external_system_libs.len != 0) - { - if (libc_installation == null) { - libc_installation = try LibCInstallation.findNative(.{ - .allocator = arena, - .verbose = true, - .target = cross_target.toTarget(), - }); + 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.any_c_source_files = create_module.c_source_files.items.len != 0; - try lib_dirs.appendSlice(&.{ libc_installation.?.msvc_lib_dir.?, libc_installation.?.kernel32_lib_dir.? }); - } + 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}); } - // If any libs in this list are statically provided, we omit them from the - // resolved list and populate the link_objects array instead. - { - var test_path = std.ArrayList(u8).init(gpa); - defer test_path.deinit(); - - var checked_paths = std.ArrayList(u8).init(gpa); - defer checked_paths.deinit(); + // 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); + }; - var failed_libs = std.ArrayList(struct { - name: []const u8, - strategy: SystemLib.SearchStrategy, - checked_paths: []const u8, - preferred_mode: std.builtin.LinkMode, - }).init(arena); + 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 null; + }; - syslib: for (external_system_libs.items(.name), 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)); + 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 "", + }, + .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(), + }); - checked_paths.clearRetainingCapacity(); + break :root_mod test_mod; + } else main_mod; - switch (info.search_strategy) { - .mode_first, .no_fallback => { - // check for preferred mode - for (lib_dirs.items) |lib_dir_path| { - if (try accessLibPath( - &test_path, - &checked_paths, - lib_dir_path, - lib_name, - target_info.target, - info.preferred_mode, - )) { - 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, .{ - .name = lib_name, - .lib = .{ - .needed = info.needed, - .weak = info.weak, - .path = path, - }, - }), - } - continue :syslib; - } - } - // check for fallback mode - if (info.search_strategy == .no_fallback) { - try failed_libs.append(.{ - .name = lib_name, - .strategy = info.search_strategy, - .checked_paths = try arena.dupe(u8, checked_paths.items), - .preferred_mode = info.preferred_mode, - }); - continue :syslib; - } - for (lib_dirs.items) |lib_dir_path| { - if (try accessLibPath( - &test_path, - &checked_paths, - lib_dir_path, - lib_name, - target_info.target, - info.fallbackMode(), - )) { - 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, .{ - .name = lib_name, - .lib = .{ - .needed = info.needed, - .weak = info.weak, - .path = path, - }, - }), - } - continue :syslib; - } - } - try failed_libs.append(.{ - .name = lib_name, - .strategy = info.search_strategy, - .checked_paths = try arena.dupe(u8, checked_paths.items), - .preferred_mode = info.preferred_mode, - }); - continue :syslib; - }, - .paths_first => { - for (lib_dirs.items) |lib_dir_path| { - // check for preferred mode - if (try accessLibPath( - &test_path, - &checked_paths, - lib_dir_path, - lib_name, - target_info.target, - info.preferred_mode, - )) { - 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, .{ - .name = lib_name, - .lib = .{ - .needed = info.needed, - .weak = info.weak, - .path = path, - }, - }), - } - continue :syslib; - } + const target = main_mod.resolved_target.result; - // check for fallback mode - if (try accessLibPath( - &test_path, - &checked_paths, - lib_dir_path, - lib_name, - target_info.target, - info.fallbackMode(), - )) { - 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, .{ - .name = lib_name, - .lib = .{ - .needed = info.needed, - .weak = info.weak, - .path = path, - }, - }), - } - continue :syslib; - } - } - try failed_libs.append(.{ - .name = lib_name, - .strategy = info.search_strategy, - .checked_paths = try arena.dupe(u8, checked_paths.items), - .preferred_mode = info.preferred_mode, - }); - continue :syslib; - }, - } - @compileError("unreachable"); + if (target.ofmt != .coff) { + if (manifest_file != null) { + fatal("manifest file is not allowed unless the target object format is coff (Windows/UEFI)", .{}); } - - if (failed_libs.items.len > 0) { - for (failed_libs.items) |f| { - const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths; - std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{ - @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths, - }); - } - process.exit(1); + 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)", .{}); } } - // After this point, resolved_system_libs is used instead of external_system_libs. // We now repeat part of the process for frameworks. var resolved_frameworks = std.ArrayList(Compilation.Framework).init(arena); - if (frameworks.keys().len > 0) { + if (create_module.frameworks.keys().len > 0) { var test_path = std.ArrayList(u8).init(gpa); defer test_path.deinit(); @@ -3052,10 +2781,10 @@ fn buildOutputType( checked_paths: []const u8, }).init(arena); - framework: for (frameworks.keys(), frameworks.values()) |framework_name, info| { + framework: for (create_module.frameworks.keys(), create_module.frameworks.values()) |framework_name, info| { checked_paths.clearRetainingCapacity(); - for (framework_dirs.items) |framework_dir_path| { + for (create_module.framework_dirs.items) |framework_dir_path| { if (try accessFrameworkPath( &test_path, &checked_paths, @@ -3090,15 +2819,15 @@ fn buildOutputType( } // After this point, resolved_frameworks is used instead of frameworks. - const object_format = target_info.target.ofmt; - - if (output_mode == .Obj and (object_format == .coff or object_format == .macho)) { - const total_obj_count = c_source_files.items.len + + if (create_module.resolved_options.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 + - link_objects.items.len; + create_module.rc_source_files.items.len + + create_module.link_objects.items.len; if (total_obj_count > 1) { - fatal("{s} does not support linking multiple objects into one", .{@tagName(object_format)}); + fatal("{s} does not support linking multiple objects into one", .{@tagName(target.ofmt)}); } } @@ -3108,10 +2837,12 @@ fn buildOutputType( const output_to_cache = listen != .none; const optional_version = if (have_version) version else null; + const root_name = if (provided_name) |n| n else main_mod.fully_qualified_name; + const resolved_soname: ?[]const u8 = switch (soname) { .yes => |explicit| explicit, .no => null, - .yes_default_value => switch (object_format) { + .yes_default_value => switch (target.ofmt) { .elf => if (have_version) try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ root_name, version.major }) else @@ -3120,7 +2851,7 @@ fn buildOutputType( }, }; - const a_out_basename = switch (object_format) { + const a_out_basename = switch (target.ofmt) { .coff => "a.exe", else => "a.out", }; @@ -3142,9 +2873,9 @@ fn buildOutputType( }, .basename = try std.zig.binNameAlloc(arena, .{ .root_name = root_name, - .target = target_info.target, - .output_mode = output_mode, - .link_mode = link_mode, + .target = target, + .output_mode = create_module.resolved_options.output_mode, + .link_mode = create_module.resolved_options.link_mode, .version = optional_version, }), }, @@ -3262,15 +2993,15 @@ 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.resolved_options.output_mode) { .Obj => false, - .Lib => (link_mode orelse .Static) == .Dynamic, + .Lib => create_module.resolved_options.link_mode == .Dynamic, .Exe => true, }; // Note that cmake when targeting Windows will try to execute // zig cc to make an executable and output an implib too. const implib_eligible = is_exe_or_dyn_lib and - emit_bin_loc != null and target_info.target.os.tag == .windows; + emit_bin_loc != null and target.os.tag == .windows; if (!implib_eligible) { if (!emit_implib_arg_provided) { emit_implib = .no; @@ -3296,76 +3027,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(); @@ -3381,37 +3046,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 @@ -3420,7 +3085,7 @@ fn buildOutputType( // "-" is stdin. Dump it to a real file. const sep = fs.path.sep_str; const sub_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-stdin{s}", .{ - std.crypto.random.int(u64), ext.canonicalName(target_info.target), + std.crypto.random.int(u64), ext.canonicalName(target), }); try local_cache_directory.handle.makePath("tmp"); // Note that in one of the happy paths, execve() is used to switch @@ -3448,20 +3113,35 @@ fn buildOutputType( else => false, }; + const disable_lld_caching = !output_to_cache; + + const cache_mode: Compilation.CacheMode = b: { + if (disable_lld_caching) break :b .incremental; + if (!create_module.resolved_options.have_zcu) break :b .whole; + + // 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, this clause can be removed so that + // incremental cache mode is used for LLVM backend too. + if (create_module.resolved_options.use_llvm) break :b .whole; + + break :b .incremental; + }; + gimmeMoreOfThoseSweetSweetFileDescriptors(); - const comp = Compilation.create(gpa, .{ + const comp = Compilation.create(gpa, arena, .{ .zig_lib_directory = zig_lib_directory, .local_cache_directory = local_cache_directory, .global_cache_directory = global_cache_directory, + .thread_pool = &thread_pool, + .self_exe_path = self_exe_path, + .config = create_module.resolved_options, .root_name = root_name, - .target = target_info.target, - .is_native_os = cross_target.isNativeOs(), - .is_native_abi = cross_target.isNativeAbi(), - .dynamic_linker = target_info.dynamic_linker.get(), - .sysroot = sysroot, - .output_mode = output_mode, + .sysroot = create_module.sysroot, .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, @@ -3469,45 +3149,21 @@ 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, + .lib_dirs = create_module.lib_dirs.items, + .rpath_list = create_module.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, + .link_objects = create_module.link_objects.items, + .framework_dirs = create_module.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, .version_script = version_script, .disable_c_depfile = disable_c_depfile, @@ -3516,18 +3172,15 @@ 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, - .linker_opt_bisect_limit = linker_opt_bisect_limit, + .llvm_opt_bisect_limit = llvm_opt_bisect_limit, .linker_global_base = linker_global_base, .linker_export_symbol_names = linker_export_symbol_names.items, .linker_z_nocopyreloc = linker_z_nocopyreloc, @@ -3542,7 +3195,6 @@ fn buildOutputType( .linker_tsaware = linker_tsaware, .linker_nxcompat = linker_nxcompat, .linker_dynamicbase = linker_dynamicbase, - .linker_optimization = linker_optimization, .linker_compress_debug_sections = linker_compress_debug_sections, .linker_module_definition_file = linker_module_definition_file, .major_subsystem_version = major_subsystem_version, @@ -3551,20 +3203,16 @@ fn buildOutputType( .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, + .stack_size = stack_size, + .image_base = image_base, .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, - .libc_installation = if (libc_installation) |*lci| lci else null, + .libc_installation = if (create_module.libc_installation) |*lci| lci else null, .verbose_cc = verbose_cc, .verbose_link = verbose_link, .verbose_air = verbose_air, @@ -3574,21 +3222,16 @@ 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, + .disable_lld_caching = disable_lld_caching, + .cache_mode = cache_mode, .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, @@ -3598,13 +3241,15 @@ 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, + .native_system_include_paths = create_module.native_system_include_paths, + // Any leftover C compilation args (such as -I) apply globally rather + // than to any particular module. This feature can greatly reduce CLI + // noise when --search-prefix and --mod are combined. + .global_cc_argv = try cc_argv.toOwnedSlice(arena), }) catch |err| switch (err) { error.LibCUnavailable => { - const target = target_info.target; const triple_name = try target.zigTriple(arena); std.log.err("unable to find or provide libc for target '{s}'", .{triple_name}); @@ -3632,7 +3277,9 @@ fn buildOutputType( defer if (!comp_destroyed) comp.destroy(); if (show_builtin) { - return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena)); + const builtin_mod = comp.root_mod.getBuiltinDependency(); + const source = builtin_mod.builtin_file.?.source; + return std.io.getStdOut().writeAll(source); } switch (listen) { .none => {}, @@ -3681,7 +3328,7 @@ fn buildOutputType( return cmdTranslateC(comp, arena, null); } - updateModule(comp) catch |err| switch (err) { + updateModule(comp, color) catch |err| switch (err) { error.SemanticAnalyzeFail => { assert(listen == .none); saveState(comp, debug_incremental); @@ -3693,10 +3340,10 @@ fn buildOutputType( try comp.makeBinFileExecutable(); saveState(comp, debug_incremental); - if (test_exec_args.items.len == 0 and object_format == .c) default_exec_args: { + if (test_exec_args.items.len == 0 and target.ofmt == .c) default_exec_args: { // Default to using `zig run` to execute the produced .c code from `zig test`. const c_code_loc = emit_bin_loc orelse break :default_exec_args; - const c_code_directory = c_code_loc.directory orelse comp.bin_file.options.emit.?.directory; + const c_code_directory = c_code_loc.directory orelse comp.bin_file.?.emit.directory; const c_code_path = try fs.path.join(arena, &[_][]const u8{ c_code_directory.path orelse ".", c_code_loc.basename, }); @@ -3706,23 +3353,24 @@ 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_info.target.os.tag == .windows) { + } else if (target.os.tag == .windows) { try test_exec_args.appendSlice(&.{ "--subsystem", "console", "-lkernel32", "-lntdll", }); } - 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,11 +3390,11 @@ fn buildOutputType( test_exec_args.items, self_exe_path.?, arg_mode, - &target_info, + &target, &comp_destroyed, all_args, runtime_args_start, - link_libc, + create_module.resolved_options.link_libc, ); } @@ -3754,6 +3402,522 @@ 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), + 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), + sysroot: ?[]const u8, + lib_dirs: std.ArrayListUnmanaged([]const u8), + lib_dir_args: std.ArrayListUnmanaged([]const u8), + libc_installation: ?LibCInstallation, + want_native_include_dirs: bool, + frameworks: std.StringArrayHashMapUnmanaged(Framework), + native_system_include_paths: []const []const u8, + framework_dirs: std.ArrayListUnmanaged([]const u8), + rpath_list: std.ArrayListUnmanaged([]const u8), + libc_paths_file: ?[]const u8, + link_objects: std.ArrayListUnmanaged(Compilation.LinkObject), +}; + +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; + create_module.opts.root_strip = cli_mod.inherited.strip; + create_module.opts.root_error_tracing = cli_mod.inherited.error_tracing; + const target = resolved_target.result; + + // First, remove libc, libc++, and compiler_rt libraries from the system libraries list. + // 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, + }) = .{}; + 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 external_system_libs.append(arena, .{ + .name = lib_name, + .info = info, + }); + } + // After this point, external_system_libs is used instead of system_libs. + if (external_system_libs.len != 0) + create_module.want_native_include_dirs = true; + + // Resolve the library path arguments with respect to sysroot. + if (create_module.sysroot) |root| { + try create_module.lib_dirs.ensureUnusedCapacity(arena, create_module.lib_dir_args.items.len * 2); + for (create_module.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 }); + create_module.lib_dirs.appendAssumeCapacity(full_path); + } + create_module.lib_dirs.appendAssumeCapacity(dir); + } + } else { + create_module.lib_dirs = create_module.lib_dir_args; + } + create_module.lib_dir_args = undefined; // From here we use lib_dirs instead. + + if (resolved_target.is_native_os and target.isDarwin()) { + // If we want to link against frameworks, we need system headers. + if (create_module.frameworks.count() > 0) + create_module.want_native_include_dirs = true; + } + + // Trigger native system library path detection if necessary. + if (create_module.sysroot == null and + resolved_target.is_native_os and resolved_target.is_native_abi and + create_module.want_native_include_dirs) + { + var paths = std.zig.system.NativePaths.detect(arena, target) catch |err| { + fatal("unable to detect native system paths: {s}", .{@errorName(err)}); + }; + for (paths.warnings.items) |warning| { + warn("{s}", .{warning}); + } + + create_module.native_system_include_paths = try paths.include_dirs.toOwnedSlice(arena); + + try create_module.framework_dirs.appendSlice(arena, paths.framework_dirs.items); + try create_module.lib_dirs.appendSlice(arena, paths.lib_dirs.items); + try create_module.rpath_list.appendSlice(arena, paths.rpaths.items); + } + + if (create_module.libc_paths_file) |paths_file| { + create_module.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) + { + if (create_module.libc_installation == null) { + create_module.libc_installation = LibCInstallation.findNative(.{ + .allocator = arena, + .verbose = true, + .target = target, + }) catch |err| { + fatal("unable to find native libc installation: {s}", .{@errorName(err)}); + }; + + try create_module.lib_dirs.appendSlice(arena, &.{ + create_module.libc_installation.?.msvc_lib_dir.?, + create_module.libc_installation.?.kernel32_lib_dir.?, + }); + } + } + + // If any libs in this list are statically provided, we omit them from the + // resolved list and populate the link_objects array instead. + { + var test_path = std.ArrayList(u8).init(gpa); + defer test_path.deinit(); + + var checked_paths = std.ArrayList(u8).init(gpa); + defer checked_paths.deinit(); + + var failed_libs = std.ArrayList(struct { + name: []const u8, + strategy: SystemLib.SearchStrategy, + checked_paths: []const u8, + preferred_mode: std.builtin.LinkMode, + }).init(arena); + + syslib: for (external_system_libs.items(.name), 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)); + + checked_paths.clearRetainingCapacity(); + + switch (info.search_strategy) { + .mode_first, .no_fallback => { + // check for preferred mode + for (create_module.lib_dirs.items) |lib_dir_path| { + if (try accessLibPath( + &test_path, + &checked_paths, + lib_dir_path, + lib_name, + target, + info.preferred_mode, + )) { + const path = try arena.dupe(u8, test_path.items); + switch (info.preferred_mode) { + .Static => try create_module.link_objects.append(arena, .{ .path = path }), + .Dynamic => try create_module.resolved_system_libs.append(arena, .{ + .name = lib_name, + .lib = .{ + .needed = info.needed, + .weak = info.weak, + .path = path, + }, + }), + } + continue :syslib; + } + } + // check for fallback mode + if (info.search_strategy == .no_fallback) { + try failed_libs.append(.{ + .name = lib_name, + .strategy = info.search_strategy, + .checked_paths = try arena.dupe(u8, checked_paths.items), + .preferred_mode = info.preferred_mode, + }); + continue :syslib; + } + for (create_module.lib_dirs.items) |lib_dir_path| { + if (try accessLibPath( + &test_path, + &checked_paths, + lib_dir_path, + lib_name, + target, + info.fallbackMode(), + )) { + const path = try arena.dupe(u8, test_path.items); + switch (info.fallbackMode()) { + .Static => try create_module.link_objects.append(arena, .{ .path = path }), + .Dynamic => try create_module.resolved_system_libs.append(arena, .{ + .name = lib_name, + .lib = .{ + .needed = info.needed, + .weak = info.weak, + .path = path, + }, + }), + } + continue :syslib; + } + } + try failed_libs.append(.{ + .name = lib_name, + .strategy = info.search_strategy, + .checked_paths = try arena.dupe(u8, checked_paths.items), + .preferred_mode = info.preferred_mode, + }); + continue :syslib; + }, + .paths_first => { + for (create_module.lib_dirs.items) |lib_dir_path| { + // check for preferred mode + if (try accessLibPath( + &test_path, + &checked_paths, + lib_dir_path, + lib_name, + target, + info.preferred_mode, + )) { + const path = try arena.dupe(u8, test_path.items); + switch (info.preferred_mode) { + .Static => try create_module.link_objects.append(arena, .{ .path = path }), + .Dynamic => try create_module.resolved_system_libs.append(arena, .{ + .name = lib_name, + .lib = .{ + .needed = info.needed, + .weak = info.weak, + .path = path, + }, + }), + } + continue :syslib; + } + + // check for fallback mode + if (try accessLibPath( + &test_path, + &checked_paths, + lib_dir_path, + lib_name, + target, + info.fallbackMode(), + )) { + const path = try arena.dupe(u8, test_path.items); + switch (info.fallbackMode()) { + .Static => try create_module.link_objects.append(arena, .{ .path = path }), + .Dynamic => try create_module.resolved_system_libs.append(arena, .{ + .name = lib_name, + .lib = .{ + .needed = info.needed, + .weak = info.weak, + .path = path, + }, + }), + } + continue :syslib; + } + } + try failed_libs.append(.{ + .name = lib_name, + .strategy = info.search_strategy, + .checked_paths = try arena.dupe(u8, checked_paths.items), + .preferred_mode = info.preferred_mode, + }); + continue :syslib; + }, + } + @compileError("unreachable"); + } + + if (failed_libs.items.len > 0) { + for (failed_libs.items) |f| { + const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths; + std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{ + @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths, + }); + } + process.exit(1); + } + } + // After this point, create_module.resolved_system_libs is used instead of + // create_module.external_system_libs. + + if (create_module.resolved_system_libs.len != 0) + create_module.opts.any_dyn_libs = true; + + create_module.resolved_options = Compilation.Config.resolve(create_module.opts) catch |err| switch (err) { + error.WasiExecModelRequiresWasi => fatal("only WASI OS targets support execution model", .{}), + error.SharedMemoryIsWasmOnly => fatal("only WebAssembly CPU targets support shared memory", .{}), + error.ObjectFilesCannotShareMemory => fatal("object files cannot share memory", .{}), + error.SharedMemoryRequiresAtomicsAndBulkMemory => fatal("shared memory requires atomics and bulk_memory CPU features", .{}), + error.ThreadsRequireSharedMemory => fatal("threads require shared memory", .{}), + error.EmittingLlvmModuleRequiresLlvmBackend => fatal("emitting an LLVM module requires using the LLVM backend", .{}), + error.LlvmLacksTargetSupport => fatal("LLVM lacks support for the specified target", .{}), + error.ZigLacksTargetSupport => fatal("compiler backend unavailable for the specified target", .{}), + error.EmittingBinaryRequiresLlvmLibrary => fatal("producing machine code via LLVM requires using the LLVM library", .{}), + error.LldIncompatibleObjectFormat => fatal("using LLD to link {s} files is unsupported", .{@tagName(target.ofmt)}), + error.LtoRequiresLld => fatal("LTO requires using LLD", .{}), + error.SanitizeThreadRequiresLibCpp => fatal("thread sanitization is (for now) implemented in C++, so it requires linking libc++", .{}), + error.LibCppRequiresLibUnwind => fatal("libc++ requires linking libunwind", .{}), + error.OsRequiresLibC => fatal("the target OS requires using libc as the stable syscall interface", .{}), + error.LibCppRequiresLibC => fatal("libc++ requires linking libc", .{}), + error.LibUnwindRequiresLibC => fatal("libunwind requires linking libc", .{}), + error.TargetCannotDynamicLink => fatal("dynamic linking unavailable on the specified target", .{}), + error.LibCRequiresDynamicLinking => fatal("libc of the specified target requires dynamic linking", .{}), + error.SharedLibrariesRequireDynamicLinking => fatal("using shared libraries requires dynamic linking", .{}), + error.ExportMemoryAndDynamicIncompatible => fatal("exporting memory is incompatible with dynamic linking", .{}), + error.DynamicLibraryPrecludesPie => fatal("dynamic libraries cannot be position independent executables", .{}), + error.TargetRequiresPie => fatal("the specified target requires position independent executables", .{}), + error.SanitizeThreadRequiresPie => fatal("thread sanitization requires position independent executables", .{}), + error.BackendLacksErrorTracing => fatal("the selected backend has not yet implemented error return tracing", .{}), + error.LlvmLibraryUnavailable => fatal("zig was compiled without LLVM libraries", .{}), + error.LldUnavailable => fatal("zig was compiled without LLD libraries", .{}), + error.ClangUnavailable => fatal("zig was compiled without Clang libraries", .{}), + error.DllExportFnsRequiresWindows => fatal("only Windows OS targets support DLLs", .{}), + }; + } + + 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.value) 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| { @@ -3805,7 +3969,7 @@ fn serve( const hdr = try server.receiveMessage(); switch (hdr.tag) { - .exit => return, + .exit => return cleanExit(), .update => { assert(main_progress_node.recently_updated_child == null); tracy.frameMark(); @@ -3827,7 +3991,7 @@ fn serve( continue; } - if (comp.bin_file.options.output_mode == .Exe) { + if (comp.config.output_mode == .Exe) { try comp.makeBinFileWritable(); } @@ -3862,7 +4026,7 @@ fn serve( // test_exec_args, // self_exe_path.?, // arg_mode, - // target_info, + // target, // true, // &comp_destroyed, // all_args, @@ -3877,7 +4041,7 @@ fn serve( try comp.hotCodeSwap(main_progress_node, pid); try serveUpdateResults(&server, comp); } else { - if (comp.bin_file.options.output_mode == .Exe) { + if (comp.config.output_mode == .Exe) { try comp.makeBinFileWritable(); } try comp.update(main_progress_node); @@ -3967,63 +4131,76 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void { try s.serveErrorBundle(error_bundle); return; } - // This logic is a bit counter-intuitive because the protocol implies that - // each emitted artifact could possibly be in a different location, when in - // reality, there is only one artifact output directory, and the build - // system depends on that fact. So, until the protocol is changed to - // reflect this, this logic only needs to ensure that emit_bin_path is - // emitted for at least one thing, if there are any artifacts. - if (comp.bin_file.options.emit) |emit| { + + // This logic is counter-intuitive because the protocol accounts for each + // emitted artifact possibly being in a different location, which correctly + // matches the behavior of the compiler, however, the build system + // currently always passes flags that makes all build artifacts output to + // the same local cache directory, and relies on them all being in the same + // directory. + // + // So, until the build system and protocol are changed to reflect this, + // this logic must ensure that emit_bin_path is emitted for at least one + // thing, if there are any artifacts. + + switch (comp.cache_use) { + .incremental => if (comp.bin_file) |lf| { + const full_path = try lf.emit.directory.join(gpa, &.{lf.emit.sub_path}); + defer gpa.free(full_path); + try s.serveEmitBinPath(full_path, .{ + .flags = .{ .cache_hit = comp.last_update_was_cache_hit }, + }); + return; + }, + .whole => |whole| if (whole.bin_sub_path) |sub_path| { + const full_path = try comp.local_cache_directory.join(gpa, &.{sub_path}); + defer gpa.free(full_path); + try s.serveEmitBinPath(full_path, .{ + .flags = .{ .cache_hit = comp.last_update_was_cache_hit }, + }); + return; + }, + } + + for ([_]?Compilation.Emit{ + comp.docs_emit, + comp.implib_emit, + }) |opt_emit| { + const emit = opt_emit orelse continue; const full_path = try emit.directory.join(gpa, &.{emit.sub_path}); defer gpa.free(full_path); try s.serveEmitBinPath(full_path, .{ .flags = .{ .cache_hit = comp.last_update_was_cache_hit }, }); - } else if (comp.bin_file.options.docs_emit) |emit| { - const full_path = try emit.directory.join(gpa, &.{emit.sub_path}); + return; + } + + for ([_]?Compilation.EmitLoc{ + comp.emit_asm, + comp.emit_llvm_ir, + comp.emit_llvm_bc, + }) |opt_emit_loc| { + const emit_loc = opt_emit_loc orelse continue; + const directory = emit_loc.directory orelse continue; + const full_path = try directory.join(gpa, &.{emit_loc.basename}); defer gpa.free(full_path); try s.serveEmitBinPath(full_path, .{ .flags = .{ .cache_hit = comp.last_update_was_cache_hit }, }); + return; } } -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 parseCrossTargetOrReportFatalError( +fn parseTargetQueryOrReportFatalError( allocator: Allocator, - opts: std.zig.CrossTarget.ParseOptions, -) !std.zig.CrossTarget { + opts: std.Target.Query.ParseOptions, +) std.Target.Query { var opts_with_diags = opts; - var diags: std.zig.CrossTarget.ParseOptions.Diagnostics = .{}; + var diags: std.Target.Query.ParseOptions.Diagnostics = .{}; if (opts_with_diags.diagnostics == null) { opts_with_diags.diagnostics = &diags; } - return std.zig.CrossTarget.parse(opts_with_diags) catch |err| switch (err) { + return std.Target.Query.parse(opts_with_diags) catch |err| switch (err) { error.UnknownCpuModel => { help: { var help_text = std.ArrayList(u8).init(allocator); @@ -4061,7 +4238,9 @@ fn parseCrossTargetOrReportFatalError( } 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), + }), }; } @@ -4072,17 +4251,17 @@ fn runOrTest( test_exec_args: []const ?[]const u8, self_exe_path: []const u8, arg_mode: ArgMode, - target_info: *const std.zig.system.NativeTargetInfo, + target: *const std.Target, comp_destroyed: *bool, all_args: []const []const u8, runtime_args_start: ?usize, link_libc: bool, ) !void { - const exe_emit = comp.bin_file.options.emit orelse return; + const lf = comp.bin_file orelse return; // A naive `directory.join` here will indeed get the correct path to the binary, // however, in the case of cwd, we actually want `./foo` so that the path can be executed. const exe_path = try fs.path.join(arena, &[_][]const u8{ - exe_emit.directory.path orelse ".", exe_emit.sub_path, + lf.emit.directory.path orelse ".", lf.emit.sub_path, }); var argv = std.ArrayList([]const u8).init(gpa); @@ -4106,7 +4285,7 @@ fn runOrTest( if (process.can_execv and arg_mode == .run) { // execv releases the locks; no need to destroy the Compilation here. const err = process.execve(gpa, argv.items, &env_map); - try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc); + try warnAboutForeignBinaries(arena, arg_mode, target, link_libc); const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following command failed to execve with '{s}':\n{s}", .{ @errorName(err), cmd }); } else if (process.can_spawn) { @@ -4122,7 +4301,7 @@ fn runOrTest( comp_destroyed.* = true; const term = child.spawnAndWait() catch |err| { - try warnAboutForeignBinaries(arena, arg_mode, target_info, link_libc); + try warnAboutForeignBinaries(arena, arg_mode, target, link_libc); const cmd = try std.mem.join(arena, " ", argv.items); fatal("the following command failed with '{s}':\n{s}", .{ @errorName(err), cmd }); }; @@ -4174,7 +4353,7 @@ fn runOrTestHotSwap( all_args: []const []const u8, runtime_args_start: ?usize, ) !std.ChildProcess.Id { - const exe_emit = comp.bin_file.options.emit.?; + const lf = comp.bin_file.?; const exe_path = switch (builtin.target.os.tag) { // On Windows it seems impossible to perform an atomic rename of a file that is currently @@ -4182,16 +4361,16 @@ fn runOrTestHotSwap( // tmp zig-cache and use it to spawn the child process. This way we are free to update // the binary with each requested hot update. .windows => blk: { - try exe_emit.directory.handle.copyFile(exe_emit.sub_path, comp.local_cache_directory.handle, exe_emit.sub_path, .{}); + try lf.emit.directory.handle.copyFile(lf.emit.sub_path, comp.local_cache_directory.handle, lf.emit.sub_path, .{}); break :blk try fs.path.join(gpa, &[_][]const u8{ - comp.local_cache_directory.path orelse ".", exe_emit.sub_path, + comp.local_cache_directory.path orelse ".", lf.emit.sub_path, }); }, // A naive `directory.join` here will indeed get the correct path to the binary, // however, in the case of cwd, we actually want `./foo` so that the path can be executed. else => try fs.path.join(gpa, &[_][]const u8{ - exe_emit.directory.path orelse ".", exe_emit.sub_path, + lf.emit.directory.path orelse ".", lf.emit.sub_path, }), }; defer gpa.free(exe_path); @@ -4263,13 +4442,13 @@ fn runOrTestHotSwap( } } -fn updateModule(comp: *Compilation) !void { +fn updateModule(comp: *Compilation, color: Color) !void { { // If the terminal is dumb, we dont want to show the user all the output. var progress: std.Progress = .{ .dont_print_on_dumb = true }; const main_progress_node = progress.start("", 0); defer main_progress_node.end(); - switch (comp.color) { + switch (color) { .off => { progress.terminal = null; }, @@ -4287,24 +4466,25 @@ fn updateModule(comp: *Compilation) !void { defer errors.deinit(comp.gpa); if (errors.errorMessageCount() > 0) { - errors.renderToStdErr(renderOptions(comp.color)); + errors.renderToStdErr(renderOptions(color)); return error.SemanticAnalyzeFail; } } fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilation.CImportResult) !void { if (build_options.only_core_functionality) @panic("@translate-c is not available in a zig2.c build"); + const color: Color = .auto; assert(comp.c_source_files.len == 1); const c_source_file = comp.c_source_files[0]; - const translated_zig_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{comp.bin_file.options.root_name}); + const translated_zig_basename = try std.fmt.allocPrint(arena, "{s}.zig", .{comp.root_name}); - var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); + var man: Cache.Manifest = comp.obtainCObjectCacheManifest(comp.root_mod); man.want_shared_lock = false; defer man.deinit(); man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects - man.hash.add(comp.c_frontend); + man.hash.add(comp.config.c_frontend); Compilation.cache_helpers.hashCSource(&man, c_source_file) catch |err| { fatal("unable to process '{s}': {s}", .{ c_source_file.src_path, @errorName(err) }); }; @@ -4313,14 +4493,14 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati const digest = if (try man.hit()) man.final() else digest: { if (fancy_output) |p| p.cache_hit = false; var argv = std.ArrayList([]const u8).init(arena); - try argv.append(@tagName(comp.c_frontend)); // argv[0] is program name, actual args start at [1] + try argv.append(@tagName(comp.config.c_frontend)); // argv[0] is program name, actual args start at [1] var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{}); defer zig_cache_tmp_dir.close(); const ext = Compilation.classifyFileExt(c_source_file.src_path); const out_dep_path: ?[]const u8 = blk: { - if (comp.c_frontend == .aro or comp.disable_c_depfile or !ext.clangSupportsDepFile()) + if (comp.config.c_frontend == .aro or comp.disable_c_depfile or !ext.clangSupportsDepFile()) break :blk null; const c_src_basename = fs.path.basename(c_source_file.src_path); @@ -4330,14 +4510,15 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati }; // TODO - if (comp.c_frontend != .aro) try comp.addTranslateCCArgs(arena, &argv, ext, out_dep_path); + if (comp.config.c_frontend != .aro) + try comp.addTranslateCCArgs(arena, &argv, ext, out_dep_path, comp.root_mod); try argv.append(c_source_file.src_path); if (comp.verbose_cc) { Compilation.dump_argv(argv.items); } - var tree = switch (comp.c_frontend) { + var tree = switch (comp.config.c_frontend) { .aro => tree: { const aro = @import("aro"); const translate_c = @import("aro_translate_c.zig"); @@ -4385,7 +4566,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati p.errors = errors; return; } else { - errors.renderToStdErr(renderOptions(comp.color)); + errors.renderToStdErr(renderOptions(color)); process.exit(1); } }, @@ -4667,9 +4848,12 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes: while (true) { switch (cur_includes) { .any, .msvc => { - const cross_target = std.zig.CrossTarget.parse(.{ .arch_os_abi = "native-windows-msvc" }) catch unreachable; - const target = cross_target.toTarget(); - const is_native_abi = cross_target.isNativeAbi(); + const target_query: std.Target.Query = .{ + .os_tag = .windows, + .abi = .msvc, + }; + 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) { // fall back to mingw @@ -4692,9 +4876,12 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes: }; }, .gnu => { - const cross_target = std.zig.CrossTarget.parse(.{ .arch_os_abi = "native-windows-gnu" }) catch unreachable; - const target = cross_target.toTarget(); - const is_native_abi = cross_target.isNativeAbi(); + const target_query: std.Target.Query = .{ + .os_tag = .windows, + .abi = .gnu, + }; + 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 .{ .include_paths = detected_libc.libc_include_dir_list, @@ -4755,9 +4942,10 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void { } } - const cross_target = try parseCrossTargetOrReportFatalError(gpa, .{ + const target_query = parseTargetQueryOrReportFatalError(gpa, .{ .arch_os_abi = target_arch_os_abi, }); + const target = resolveTargetQueryOrFatal(target_query); if (print_includes) { var arena_state = std.heap.ArenaAllocator.init(gpa); @@ -4767,7 +4955,7 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void { const libc_installation: ?*LibCInstallation = libc: { if (input_file) |libc_file| { const libc = try arena.create(LibCInstallation); - libc.* = LibCInstallation.parse(arena, libc_file, cross_target) catch |err| { + libc.* = LibCInstallation.parse(arena, libc_file, target) catch |err| { fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) }); }; break :libc libc; @@ -4782,8 +4970,7 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void { }; defer zig_lib_directory.handle.close(); - const target = cross_target.toTarget(); - const is_native_abi = cross_target.isNativeAbi(); + const is_native_abi = target_query.isNativeAbi(); const libc_dirs = Compilation.detectLibCIncludeDirs( arena, @@ -4813,20 +5000,18 @@ pub fn cmdLibC(gpa: Allocator, args: []const []const u8) !void { } if (input_file) |libc_file| { - var libc = LibCInstallation.parse(gpa, libc_file, cross_target) catch |err| { + var libc = LibCInstallation.parse(gpa, libc_file, target) catch |err| { fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) }); }; defer libc.deinit(gpa); } else { - if (!cross_target.isNative()) { + if (!target_query.isNative()) { fatal("unable to detect libc for non-native target", .{}); } - const target_info = try detectNativeTargetInfo(cross_target); - var libc = LibCInstallation.findNative(.{ .allocator = gpa, .verbose = true, - .target = target_info.target, + .target = target, }) catch |err| { fatal("unable to detect native libc: {s}", .{@errorName(err)}); }; @@ -4918,6 +5103,7 @@ pub const usage_build = \\ --global-cache-dir [path] Override path to global Zig cache directory \\ --zig-lib-dir [arg] Override path to Zig lib directory \\ --build-runner [file] Override path to build runner + \\ --prominent-compile-errors Buffer compile errors and display at end \\ --seed [integer] For shuffling dependency traversal order (default: random) \\ --fetch Exit after fetching dependency tree \\ -h, --help Print this help and exit @@ -5024,7 +5210,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")) { @@ -5114,12 +5300,16 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi gimmeMoreOfThoseSweetSweetFileDescriptors(); - const cross_target: std.zig.CrossTarget = .{}; - const target_info = try detectNativeTargetInfo(cross_target); + const target_query: std.Target.Query = .{}; + const resolved_target: Package.Module.ResolvedTarget = .{ + .result = resolveTargetQueryOrFatal(target_query), + .is_native_os = true, + .is_native_abi = true, + }; const exe_basename = try std.zig.binNameAlloc(arena, .{ .root_name = "build", - .target = target_info.target, + .target = resolved_target.result, .output_mode = .Exe, }); const emit_bin: Compilation.EmitLoc = .{ @@ -5130,29 +5320,62 @@ 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| - .{ - .root = .{ - .root_dir = Cache.Directory.cwd(), - .sub_path = fs.path.dirname(build_runner_path) orelse "", - }, - .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", - }; + 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 = resolved_target, + .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 = .{ + .resolved_target = resolved_target, + }, + .global = config, + .parent = null, + .builtin_mod = null, + }); - var build_mod: Package.Module = .{ - .root = .{ .root_dir = build_root.directory }, - .root_src_path = build_root.build_zig_basename, + const builtin_mod = root_mod.getBuiltinDependency(); + + 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 +5419,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 +5448,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 +5468,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,22 +5510,18 @@ 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, .{ + const comp = Compilation.create(gpa, arena, .{ .zig_lib_directory = zig_lib_directory, .local_cache_directory = local_cache_directory, .global_cache_directory = global_cache_directory, .root_name = "build", - .target = target_info.target, - .is_native_os = cross_target.isNativeOs(), - .is_native_abi = cross_target.isNativeAbi(), - .dynamic_linker = target_info.dynamic_linker.get(), - .output_mode = .Exe, - .main_mod = &main_mod, + .config = config, + .root_mod = root_mod, + .main_mod = build_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, @@ -5311,17 +5541,16 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi }; defer comp.destroy(); - updateModule(comp) catch |err| switch (err) { + updateModule(comp, color) catch |err| switch (err) { error.SemanticAnalyzeFail => process.exit(2), else => |e| return e, }; - try comp.makeBinFileExecutable(); - const emit = comp.bin_file.options.emit.?; - child_argv.items[argv_index_exe] = try emit.directory.join( - arena, - &[_][]const u8{emit.sub_path}, - ); + // Since incremental compilation isn't done yet, we use cache_mode = whole + // above, and thus the output file is already closed. + //try comp.makeBinFileExecutable(); + child_argv.items[argv_index_exe] = + try local_cache_directory.join(arena, &.{comp.cache_use.whole.bin_sub_path.?}); break :argv child_argv.items; }; @@ -5515,7 +5744,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", @@ -5725,7 +5954,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", @@ -5805,15 +6034,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); @@ -6270,10 +6497,6 @@ test "fds" { gimmeMoreOfThoseSweetSweetFileDescriptors(); } -fn detectNativeTargetInfo(cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo { - return std.zig.system.NativeTargetInfo.detect(cross_target); -} - const usage_ast_check = \\Usage: zig ast-check [file] \\ @@ -6378,7 +6601,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", @@ -6551,7 +6774,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", @@ -6670,24 +6893,24 @@ fn parseIntSuffix(arg: []const u8, prefix_len: usize) u64 { fn warnAboutForeignBinaries( arena: Allocator, arg_mode: ArgMode, - target_info: *const std.zig.system.NativeTargetInfo, + target: *const std.Target, link_libc: bool, ) !void { - const host_cross_target: std.zig.CrossTarget = .{}; - const host_target_info = try detectNativeTargetInfo(host_cross_target); + const host_query: std.Target.Query = .{}; + const host_target = resolveTargetQueryOrFatal(host_query); - switch (host_target_info.getExternalExecutor(target_info, .{ .link_libc = link_libc })) { + switch (std.zig.system.getExternalExecutor(host_target, target, .{ .link_libc = link_libc })) { .native => return, .rosetta => { - const host_name = try host_target_info.target.zigTriple(arena); - const foreign_name = try target_info.target.zigTriple(arena); + const host_name = try host_target.zigTriple(arena); + const foreign_name = try target.zigTriple(arena); warn("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}). Consider installing Rosetta.", .{ host_name, foreign_name, }); }, .qemu => |qemu| { - const host_name = try host_target_info.target.zigTriple(arena); - const foreign_name = try target_info.target.zigTriple(arena); + const host_name = try host_target.zigTriple(arena); + const foreign_name = try target.zigTriple(arena); switch (arg_mode) { .zig_test => warn( "the host system ({s}) does not appear to be capable of executing binaries " ++ @@ -6703,8 +6926,8 @@ fn warnAboutForeignBinaries( } }, .wine => |wine| { - const host_name = try host_target_info.target.zigTriple(arena); - const foreign_name = try target_info.target.zigTriple(arena); + const host_name = try host_target.zigTriple(arena); + const foreign_name = try target.zigTriple(arena); switch (arg_mode) { .zig_test => warn( "the host system ({s}) does not appear to be capable of executing binaries " ++ @@ -6720,8 +6943,8 @@ fn warnAboutForeignBinaries( } }, .wasmtime => |wasmtime| { - const host_name = try host_target_info.target.zigTriple(arena); - const foreign_name = try target_info.target.zigTriple(arena); + const host_name = try host_target.zigTriple(arena); + const foreign_name = try target.zigTriple(arena); switch (arg_mode) { .zig_test => warn( "the host system ({s}) does not appear to be capable of executing binaries " ++ @@ -6737,8 +6960,8 @@ fn warnAboutForeignBinaries( } }, .darling => |darling| { - const host_name = try host_target_info.target.zigTriple(arena); - const foreign_name = try target_info.target.zigTriple(arena); + const host_name = try host_target.zigTriple(arena); + const foreign_name = try target.zigTriple(arena); switch (arg_mode) { .zig_test => warn( "the host system ({s}) does not appear to be capable of executing binaries " ++ @@ -6754,7 +6977,7 @@ fn warnAboutForeignBinaries( } }, .bad_dl => |foreign_dl| { - const host_dl = host_target_info.dynamic_linker.get() orelse "(none)"; + const host_dl = host_target.dynamic_linker.get() orelse "(none)"; const tip_suffix = switch (arg_mode) { .zig_test => ", '--test-no-exec', or '--test-cmd'", else => "", @@ -6764,8 +6987,8 @@ fn warnAboutForeignBinaries( }); }, .bad_os_or_cpu => { - const host_name = try host_target_info.target.zigTriple(arena); - const foreign_name = try target_info.target.zigTriple(arena); + const host_name = try host_target.zigTriple(arena); + const foreign_name = try target.zigTriple(arena); const tip_suffix = switch (arg_mode) { .zig_test => ". Consider using '--test-no-exec' or '--test-cmd'", else => "", @@ -6814,18 +7037,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 @@ -6872,8 +7099,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 }; @@ -7249,11 +7477,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 @@ -7262,7 +7501,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"; @@ -7288,25 +7530,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, @@ -7514,3 +7756,28 @@ 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)}); +} + +fn parseStackSize(s: []const u8) u64 { + return std.fmt.parseUnsigned(u64, s, 0) catch |err| + fatal("unable to parse stack size '{s}': {s}", .{ s, @errorName(err) }); +} + +fn parseImageBase(s: []const u8) u64 { + return std.fmt.parseUnsigned(u64, s, 0) catch |err| + fatal("unable to parse image base '{s}': {s}", .{ s, @errorName(err) }); +} diff --git a/src/mingw.zig b/src/mingw.zig index 5a36e0082401..d8f344778304 100644 --- a/src/mingw.zig +++ b/src/mingw.zig @@ -41,14 +41,16 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr //"-D_UNICODE", //"-DWPRFLAG=1", }); - return comp.build_crt_file("crt2", .Obj, .@"mingw-w64 crt2.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "crt", "crtexe.c", }), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("crt2", .Obj, .@"mingw-w64 crt2.o", prog_node, &files); }, .dllcrt2_o => { @@ -60,14 +62,16 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "-U__CRTDLL__", "-D__MSVCRT__", }); - return comp.build_crt_file("dllcrt2", .Obj, .@"mingw-w64 dllcrt2.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "crt", "crtdll.c", }), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("dllcrt2", .Obj, .@"mingw-w64 dllcrt2.o", prog_node, &files); }, .mingw32_lib => { @@ -97,6 +101,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", "mingw", "crt", dep, }), .extra_flags = args.items, + .owner = undefined, }; } return comp.build_crt_file("mingw32", .Lib, .@"mingw-w64 mingw32.lib", prog_node, &c_source_files); @@ -125,6 +130,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr (try c_source_files.addOne()).* = .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", dep }), .extra_flags = extra_flags, + .owner = undefined, }; } if (comp.getTarget().cpu.arch == .x86) { @@ -134,6 +140,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", "mingw", dep, }), .extra_flags = extra_flags, + .owner = undefined, }; } } else { @@ -143,6 +150,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", "mingw", dep, }), .extra_flags = extra_flags, + .owner = undefined, }; } } @@ -175,6 +183,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", "mingw", dep, }), .extra_flags = extra_flags, + .owner = undefined, }; } const target = comp.getTarget(); @@ -185,6 +194,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", "mingw", dep, }), .extra_flags = extra_flags, + .owner = undefined, }; } } else if (target.cpu.arch.isARM()) { @@ -194,6 +204,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", "mingw", dep, }), .extra_flags = extra_flags, + .owner = undefined, }; } } else if (target.cpu.arch.isAARCH64()) { @@ -203,6 +214,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", "mingw", dep, }), .extra_flags = extra_flags, + .owner = undefined, }; } } else { @@ -238,6 +250,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", "mingw", "libsrc", dep, }), .extra_flags = extra_flags, + .owner = undefined, }; } return comp.build_crt_file("uuid", .Lib, .@"mingw-w64 uuid.lib", prog_node, &c_source_files); @@ -274,7 +287,7 @@ fn add_cc_args( } pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { - if (build_options.only_c) @panic("building import libs not included in core functionality"); + if (build_options.only_c) @compileError("building import libs not included in core functionality"); var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); diff --git a/src/musl.zig b/src/musl.zig index bbb3c145bb1b..c244257a7017 100644 --- a/src/musl.zig +++ b/src/musl.zig @@ -3,6 +3,7 @@ const Allocator = std.mem.Allocator; const mem = std.mem; const path = std.fs.path; const assert = std.debug.assert; +const Module = @import("Package/Module.zig"); const Compilation = @import("Compilation.zig"); const build_options = @import("build_options"); @@ -33,12 +34,14 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr try args.appendSlice(&[_][]const u8{ "-Qunused-arguments", }); - return comp.build_crt_file("crti", .Obj, .@"musl crti.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try start_asm_path(comp, arena, "crti.s"), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("crti", .Obj, .@"musl crti.o", prog_node, &files); }, .crtn_o => { var args = std.ArrayList([]const u8).init(arena); @@ -46,12 +49,14 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr try args.appendSlice(&[_][]const u8{ "-Qunused-arguments", }); - return comp.build_crt_file("crtn", .Obj, .@"musl crtn.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try start_asm_path(comp, arena, "crtn.s"), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("crtn", .Obj, .@"musl crtn.o", prog_node, &files); }, .crt1_o => { var args = std.ArrayList([]const u8).init(arena); @@ -60,14 +65,16 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "-fno-stack-protector", "-DCRT", }); - return comp.build_crt_file("crt1", .Obj, .@"musl crt1.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "crt", "crt1.c", }), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("crt1", .Obj, .@"musl crt1.o", prog_node, &files); }, .rcrt1_o => { var args = std.ArrayList([]const u8).init(arena); @@ -77,14 +84,16 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "-fno-stack-protector", "-DCRT", }); - return comp.build_crt_file("rcrt1", .Obj, .@"musl rcrt1.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "crt", "rcrt1.c", }), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("rcrt1", .Obj, .@"musl rcrt1.o", prog_node, &files); }, .scrt1_o => { var args = std.ArrayList([]const u8).init(arena); @@ -94,14 +103,16 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "-fno-stack-protector", "-DCRT", }); - return comp.build_crt_file("Scrt1", .Obj, .@"musl Scrt1.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "crt", "Scrt1.c", }), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("Scrt1", .Obj, .@"musl Scrt1.o", prog_node, &files); }, .libc_a => { // When there is a src//foo.* then it should substitute for src/foo.* @@ -185,57 +196,87 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr c_source_file.* = .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", src_file }), .extra_flags = args.items, + .owner = undefined, }; } return comp.build_crt_file("c", .Lib, .@"musl libc.a", prog_node, c_source_files.items); }, .libc_so => { - const target = comp.getTarget(); + const optimize_mode = comp.compilerRtOptMode(); + const strip = comp.compilerRtStrip(); + const config = try Compilation.Config.resolve(.{ + .output_mode = .Lib, + .link_mode = .Dynamic, + .resolved_target = comp.root_mod.resolved_target, + .is_test = false, + .have_zcu = false, + .emit_bin = true, + .root_optimize_mode = optimize_mode, + .root_strip = strip, + .link_libc = false, + }); + + const target = comp.root_mod.resolved_target.result; const arch_define = try std.fmt.allocPrint(arena, "-DARCH_{s}", .{ @tagName(target.cpu.arch), }); - const clang_argv: []const []const u8 = if (target.ptrBitWidth() == 64) - &[_][]const u8{ "-DPTR64", arch_define } + const cc_argv: []const []const u8 = if (target.ptrBitWidth() == 64) + &.{ "-DPTR64", arch_define } else - &[_][]const u8{arch_define}; + &.{arch_define}; + + const root_mod = try Module.create(arena, .{ + .global_cache_directory = comp.global_cache_directory, + .paths = .{ + .root = .{ .root_dir = comp.zig_lib_directory }, + .root_src_path = "", + }, + .fully_qualified_name = "root", + .inherited = .{ + .resolved_target = comp.root_mod.resolved_target, + .strip = strip, + .stack_check = false, + .stack_protector = 0, + .sanitize_c = false, + .sanitize_thread = false, + .red_zone = comp.root_mod.red_zone, + .omit_frame_pointer = comp.root_mod.omit_frame_pointer, + .valgrind = false, + .optimize_mode = optimize_mode, + .structured_cfg = comp.root_mod.structured_cfg, + }, + .global = config, + .cc_argv = cc_argv, + .parent = null, + .builtin_mod = null, + }); - const sub_compilation = try Compilation.create(comp.gpa, .{ + const sub_compilation = try Compilation.create(comp.gpa, arena, .{ .local_cache_directory = comp.global_cache_directory, .global_cache_directory = comp.global_cache_directory, - .cache_mode = .whole, .zig_lib_directory = comp.zig_lib_directory, - .target = target, - .root_name = "c", - .main_mod = null, - .output_mode = .Lib, - .link_mode = .Dynamic, + .self_exe_path = comp.self_exe_path, + .cache_mode = .whole, + .config = config, + .root_mod = root_mod, .thread_pool = comp.thread_pool, - .libc_installation = comp.bin_file.options.libc_installation, - .emit_bin = Compilation.EmitLoc{ .directory = null, .basename = "libc.so" }, - .optimize_mode = comp.compilerRtOptMode(), - .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, + .root_name = "c", + .libc_installation = comp.libc_installation, + .emit_bin = .{ .directory = null, .basename = "libc.so" }, .emit_h = null, - .strip = comp.compilerRtStrip(), - .is_native_os = false, - .is_native_abi = false, - .self_exe_path = comp.self_exe_path, .verbose_cc = comp.verbose_cc, - .verbose_link = comp.bin_file.options.verbose_link, + .verbose_link = comp.verbose_link, .verbose_air = comp.verbose_air, .verbose_llvm_ir = comp.verbose_llvm_ir, .verbose_cimport = comp.verbose_cimport, .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, .clang_passthrough_mode = comp.clang_passthrough_mode, .c_source_files = &[_]Compilation.CSourceFile{ - .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "libc.S" }) }, + .{ + .src_path = try comp.zig_lib_directory.join(arena, &.{ "libc", "musl", "libc.S" }), + .owner = root_mod, + }, }, - .clang_argv = clang_argv, .skip_linker_dependencies = true, .soname = "libc.so", }); @@ -248,12 +289,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr const basename = try comp.gpa.dupe(u8, "libc.so"); errdefer comp.gpa.free(basename); - comp.crt_files.putAssumeCapacityNoClobber(basename, .{ - .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join(comp.gpa, &[_][]const u8{ - sub_compilation.bin_file.options.emit.?.sub_path, - }), - .lock = sub_compilation.bin_file.toOwnedLock(), - }); + comp.crt_files.putAssumeCapacityNoClobber(basename, try sub_compilation.toCrtFile()); }, } } diff --git a/src/print_env.zig b/src/print_env.zig index 89c6ffe754bc..46ca10d14932 100644 --- a/src/print_env.zig +++ b/src/print_env.zig @@ -17,8 +17,8 @@ pub fn cmdEnv(arena: Allocator, args: []const []const u8, stdout: std.fs.File.Wr const global_cache_dir = try introspect.resolveGlobalCacheDir(arena); - const info = try std.zig.system.NativeTargetInfo.detect(.{}); - const triple = try info.target.zigTriple(arena); + const host = try std.zig.system.resolveTargetQuery(.{}); + const triple = try host.zigTriple(arena); var bw = std.io.bufferedWriter(stdout); const w = bw.writer(); diff --git a/src/target.zig b/src/target.zig index cac9aa11b064..5ae5c420c2e3 100644 --- a/src/target.zig +++ b/src/target.zig @@ -2,6 +2,9 @@ const std = @import("std"); const Type = @import("type.zig").Type; const AddressSpace = std.builtin.AddressSpace; const Alignment = @import("InternPool.zig").Alignment; +const Feature = @import("Module.zig").Feature; + +pub const default_stack_protector_buffer_size = 4; pub const ArchOsAbi = struct { arch: std.Target.Cpu.Arch, @@ -204,11 +207,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) { @@ -314,6 +324,14 @@ pub fn hasLlvmSupport(target: std.Target, ofmt: std.Target.ObjectFormat) bool { }; } +/// The set of targets that Zig supports using LLD to link for. +pub fn hasLldSupport(ofmt: std.Target.ObjectFormat) bool { + return switch (ofmt) { + .elf, .coff, .wasm => true, + else => false, + }; +} + /// The set of targets that our own self-hosted backends have robust support for. /// Used to select between LLVM backend and self-hosted backend when compiling in /// debug mode. A given target should only return true here if it is passing greater @@ -343,6 +361,13 @@ pub fn supportsStackProtector(target: std.Target, backend: std.builtin.CompilerB }; } +pub fn clangSupportsStackProtector(target: std.Target) bool { + return switch (target.cpu.arch) { + .spirv32, .spirv64 => return false, + else => true, + }; +} + pub fn libcProvidesStackProtector(target: std.Target) bool { return !target.isMinGW() and target.os.tag != .wasi and !target.isSpirV(); } @@ -356,96 +381,6 @@ pub fn supportsReturnAddress(target: std.Target) bool { }; } -fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool { - if (ignore_case) { - return std.ascii.eqlIgnoreCase(a, b); - } else { - return std.mem.eql(u8, a, b); - } -} - -pub fn is_libc_lib_name(target: std.Target, name: []const u8) bool { - const ignore_case = target.os.tag == .macos or target.os.tag == .windows; - - if (eqlIgnoreCase(ignore_case, name, "c")) - return true; - - if (target.isMinGW()) { - if (eqlIgnoreCase(ignore_case, name, "m")) - return true; - if (eqlIgnoreCase(ignore_case, name, "uuid")) - return true; - if (eqlIgnoreCase(ignore_case, name, "mingw32")) - return true; - if (eqlIgnoreCase(ignore_case, name, "msvcrt-os")) - return true; - if (eqlIgnoreCase(ignore_case, name, "mingwex")) - return true; - - return false; - } - - if (target.abi.isGnu() or target.abi.isMusl()) { - if (eqlIgnoreCase(ignore_case, name, "m")) - return true; - if (eqlIgnoreCase(ignore_case, name, "rt")) - return true; - if (eqlIgnoreCase(ignore_case, name, "pthread")) - return true; - if (eqlIgnoreCase(ignore_case, name, "util")) - return true; - if (eqlIgnoreCase(ignore_case, name, "xnet")) - return true; - if (eqlIgnoreCase(ignore_case, name, "resolv")) - return true; - if (eqlIgnoreCase(ignore_case, name, "dl")) - return true; - } - - if (target.abi.isMusl()) { - if (eqlIgnoreCase(ignore_case, name, "crypt")) - return true; - } - - if (target.os.tag.isDarwin()) { - if (eqlIgnoreCase(ignore_case, name, "System")) - return true; - if (eqlIgnoreCase(ignore_case, name, "c")) - return true; - if (eqlIgnoreCase(ignore_case, name, "dbm")) - return true; - if (eqlIgnoreCase(ignore_case, name, "dl")) - return true; - if (eqlIgnoreCase(ignore_case, name, "info")) - return true; - if (eqlIgnoreCase(ignore_case, name, "m")) - return true; - if (eqlIgnoreCase(ignore_case, name, "poll")) - return true; - if (eqlIgnoreCase(ignore_case, name, "proc")) - return true; - if (eqlIgnoreCase(ignore_case, name, "pthread")) - return true; - if (eqlIgnoreCase(ignore_case, name, "rpcsvc")) - return true; - } - - if (target.os.isAtLeast(.macos, .{ .major = 10, .minor = 8, .patch = 0 }) orelse false) { - if (eqlIgnoreCase(ignore_case, name, "mx")) - return true; - } - - return false; -} - -pub fn is_libcpp_lib_name(target: std.Target, name: []const u8) bool { - const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows; - - return eqlIgnoreCase(ignore_case, name, "c++") or - eqlIgnoreCase(ignore_case, name, "stdc++") or - eqlIgnoreCase(ignore_case, name, "c++abi"); -} - pub const CompilerRtClassification = enum { none, only_compiler_rt, only_libunwind, both }; pub fn classifyCompilerRtLibName(target: std.Target, name: []const u8) CompilerRtClassification { @@ -465,12 +400,16 @@ 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), + .bpfel, .bpfeb => false, + else => true, + }; } pub fn defaultCompilerRtOptimizeMode(target: std.Target) std.builtin.OptimizeMode { @@ -709,3 +648,37 @@ 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 backendSupportsFeature( + cpu_arch: std.Target.Cpu.Arch, + ofmt: std.Target.ObjectFormat, + use_llvm: bool, + feature: Feature, +) bool { + return switch (feature) { + .panic_fn => ofmt == .c or use_llvm or cpu_arch == .x86_64, + .panic_unwrap_error => ofmt == .c or use_llvm, + .safety_check_formatted => ofmt == .c or use_llvm, + .error_return_trace => use_llvm, + .is_named_enum_value => use_llvm, + .error_set_has_value => use_llvm or cpu_arch.isWasm(), + .field_reordering => use_llvm, + .safety_checked_instructions => use_llvm, + }; +} diff --git a/src/wasi_libc.zig b/src/wasi_libc.zig index 2d5f0bc685de..3c0ff26314f3 100644 --- a/src/wasi_libc.zig +++ b/src/wasi_libc.zig @@ -74,27 +74,31 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr var args = std.ArrayList([]const u8).init(arena); try addCCArgs(comp, arena, &args, .{}); try addLibcBottomHalfIncludes(comp, arena, &args); - return comp.build_crt_file("crt1-reactor", .Obj, .@"wasi crt1-reactor.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", try sanitize(arena, crt1_reactor_src_file), }), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("crt1-reactor", .Obj, .@"wasi crt1-reactor.o", prog_node, &files); }, .crt1_command_o => { var args = std.ArrayList([]const u8).init(arena); try addCCArgs(comp, arena, &args, .{}); try addLibcBottomHalfIncludes(comp, arena, &args); - return comp.build_crt_file("crt1-command", .Obj, .@"wasi crt1-command.o", prog_node, &.{ + var files = [_]Compilation.CSourceFile{ .{ .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", try sanitize(arena, crt1_command_src_file), }), .extra_flags = args.items, + .owner = undefined, }, - }); + }; + return comp.build_crt_file("crt1-command", .Obj, .@"wasi crt1-command.o", prog_node, &files); }, .libc_a => { var libc_sources = std.ArrayList(Compilation.CSourceFile).init(arena); @@ -109,6 +113,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", try sanitize(arena, file_path), }), .extra_flags = args.items, + .owner = undefined, }); } } @@ -125,6 +130,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", try sanitize(arena, file_path), }), .extra_flags = args.items, + .owner = undefined, }); } } @@ -141,6 +147,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", try sanitize(arena, file_path), }), .extra_flags = args.items, + .owner = undefined, }); } } @@ -159,6 +166,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", try sanitize(arena, file_path), }), .extra_flags = args.items, + .owner = undefined, }); } try comp.build_crt_file("wasi-emulated-process-clocks", .Lib, .@"libwasi-emulated-process-clocks.a", prog_node, emu_clocks_sources.items); @@ -175,6 +183,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", try sanitize(arena, file_path), }), .extra_flags = args.items, + .owner = undefined, }); } try comp.build_crt_file("wasi-emulated-getpid", .Lib, .@"libwasi-emulated-getpid.a", prog_node, emu_getpid_sources.items); @@ -191,6 +200,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", try sanitize(arena, file_path), }), .extra_flags = args.items, + .owner = undefined, }); } try comp.build_crt_file("wasi-emulated-mman", .Lib, .@"libwasi-emulated-mman.a", prog_node, emu_mman_sources.items); @@ -208,6 +218,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", try sanitize(arena, file_path), }), .extra_flags = args.items, + .owner = undefined, }); } } @@ -224,6 +235,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr "libc", try sanitize(arena, file_path), }), .extra_flags = args.items, + .owner = undefined, }); } } diff --git a/stage1/zig1.wasm b/stage1/zig1.wasm index 2084fd765d92..4d692444efab 100644 Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index ac09066552f5..e99925f22886 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -876,27 +876,27 @@ test "two comptime calls with array default initialized to undefined" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO const S = struct { - const CrossTarget = struct { - dynamic_linker: DynamicLinker = DynamicLinker{}, + const A = struct { + c: B = B{}, - pub fn parse() void { - var result: CrossTarget = .{}; - result.getCpuArch(); + pub fn d() void { + var f: A = .{}; + f.e(); } - pub fn getCpuArch(self: CrossTarget) void { - _ = self; + pub fn e(g: A) void { + _ = g; } }; - const DynamicLinker = struct { + const B = struct { buffer: [255]u8 = undefined, }; }; comptime { - S.CrossTarget.parse(); - S.CrossTarget.parse(); + S.A.d(); + S.A.d(); } } diff --git a/test/cases.zig b/test/cases.zig index 2f390413321b..9bbdde4777a3 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -9,9 +9,9 @@ pub const BuildOptions = struct { llvm_has_xtensa: bool, }; -pub fn addCases(cases: *Cases, build_options: BuildOptions) !void { - try @import("compile_errors.zig").addCases(cases); - try @import("cbe.zig").addCases(cases); - try @import("llvm_targets.zig").addCases(cases, build_options); - try @import("nvptx.zig").addCases(cases); +pub fn addCases(cases: *Cases, build_options: BuildOptions, b: *std.Build) !void { + try @import("compile_errors.zig").addCases(cases, b); + try @import("cbe.zig").addCases(cases, b); + try @import("llvm_targets.zig").addCases(cases, build_options, b); + try @import("nvptx.zig").addCases(cases, b); } diff --git a/test/cbe.zig b/test/cbe.zig index bee713843846..26724a1df000 100644 --- a/test/cbe.zig +++ b/test/cbe.zig @@ -2,16 +2,16 @@ const std = @import("std"); const Cases = @import("src/Cases.zig"); const nl = if (@import("builtin").os.tag == .windows) "\r\n" else "\n"; -// These tests should work with all platforms, but we're using linux_x64 for -// now for consistency. Will be expanded eventually. -const linux_x64 = std.zig.CrossTarget{ - .cpu_arch = .x86_64, - .os_tag = .linux, -}; - -pub fn addCases(ctx: *Cases) !void { +pub fn addCases(ctx: *Cases, b: *std.Build) !void { + // These tests should work with all platforms, but we're using linux_x64 for + // now for consistency. Will be expanded eventually. + const linux_x64: std.Target.Query = .{ + .cpu_arch = .x86_64, + .os_tag = .linux, + }; + { - var case = ctx.exeFromCompiledC("hello world with updates", .{}); + var case = ctx.exeFromCompiledC("hello world with updates", .{}, b); // Regular old hello world case.addCompareOutput( @@ -59,7 +59,7 @@ pub fn addCases(ctx: *Cases) !void { } { - var case = ctx.exeFromCompiledC("var args", .{}); + var case = ctx.exeFromCompiledC("var args", .{}, b); case.addCompareOutput( \\extern fn printf(format: [*:0]const u8, ...) c_int; @@ -72,7 +72,7 @@ pub fn addCases(ctx: *Cases) !void { } { - var case = ctx.exeFromCompiledC("errorFromInt", .{}); + var case = ctx.exeFromCompiledC("errorFromInt", .{}, b); case.addCompareOutput( \\pub export fn main() c_int { @@ -108,7 +108,7 @@ pub fn addCases(ctx: *Cases) !void { } { - var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64); + var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64, b); // Exit with 0 case.addCompareOutput( @@ -202,7 +202,7 @@ pub fn addCases(ctx: *Cases) !void { } { - var case = ctx.exeFromCompiledC("alloc and retptr", .{}); + var case = ctx.exeFromCompiledC("alloc and retptr", .{}, b); case.addCompareOutput( \\fn add(a: i32, b: i32) i32 { @@ -220,7 +220,7 @@ pub fn addCases(ctx: *Cases) !void { } { - var case = ctx.exeFromCompiledC("inferred local const and var", .{}); + var case = ctx.exeFromCompiledC("inferred local const and var", .{}, b); case.addCompareOutput( \\fn add(a: i32, b: i32) i32 { @@ -236,7 +236,7 @@ pub fn addCases(ctx: *Cases) !void { , ""); } { - var case = ctx.exeFromCompiledC("control flow", .{}); + var case = ctx.exeFromCompiledC("control flow", .{}, b); // Simple while loop case.addCompareOutput( @@ -423,7 +423,7 @@ pub fn addCases(ctx: *Cases) !void { }); } //{ - // var case = ctx.exeFromCompiledC("optionals", .{}); + // var case = ctx.exeFromCompiledC("optionals", .{}, b); // // Simple while loop // case.addCompareOutput( @@ -451,7 +451,7 @@ pub fn addCases(ctx: *Cases) !void { //} { - var case = ctx.exeFromCompiledC("errors", .{}); + var case = ctx.exeFromCompiledC("errors", .{}, b); case.addCompareOutput( \\pub export fn main() c_int { \\ var e1 = error.Foo; @@ -495,7 +495,7 @@ pub fn addCases(ctx: *Cases) !void { } { - var case = ctx.exeFromCompiledC("structs", .{}); + var case = ctx.exeFromCompiledC("structs", .{}, b); case.addError( \\const Point = struct { x: i32, y: i32 }; \\pub export fn main() c_int { @@ -562,7 +562,7 @@ pub fn addCases(ctx: *Cases) !void { } { - var case = ctx.exeFromCompiledC("unions", .{}); + var case = ctx.exeFromCompiledC("unions", .{}, b); case.addError( \\const U = union { @@ -596,7 +596,7 @@ pub fn addCases(ctx: *Cases) !void { } { - var case = ctx.exeFromCompiledC("enums", .{}); + var case = ctx.exeFromCompiledC("enums", .{}, b); case.addError( \\const E1 = packed enum { a, b, c }; @@ -838,7 +838,7 @@ pub fn addCases(ctx: *Cases) !void { } { - var case = ctx.exeFromCompiledC("shift right and left", .{}); + var case = ctx.exeFromCompiledC("shift right and left", .{}, b); case.addCompareOutput( \\pub export fn main() c_int { \\ var i: u32 = 16; @@ -863,7 +863,7 @@ pub fn addCases(ctx: *Cases) !void { } { - var case = ctx.exeFromCompiledC("inferred error sets", .{}); + var case = ctx.exeFromCompiledC("inferred error sets", .{}, b); case.addCompareOutput( \\pub export fn main() c_int { @@ -884,7 +884,7 @@ pub fn addCases(ctx: *Cases) !void { { // TODO: add u64 tests, ran into issues with the literal generated for std.math.maxInt(u64) - var case = ctx.exeFromCompiledC("add and sub wrapping operations", .{}); + var case = ctx.exeFromCompiledC("add and sub wrapping operations", .{}, b); case.addCompareOutput( \\pub export fn main() c_int { \\ // Addition @@ -933,7 +933,7 @@ pub fn addCases(ctx: *Cases) !void { } { - var case = ctx.exeFromCompiledC("rem", linux_x64); + var case = ctx.exeFromCompiledC("rem", linux_x64, b); case.addCompareOutput( \\fn assert(ok: bool) void { \\ if (!ok) unreachable; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index a3bdf5b8f141..ec3726e94d06 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,9 +2,9 @@ const std = @import("std"); const builtin = @import("builtin"); const Cases = @import("src/Cases.zig"); -pub fn addCases(ctx: *Cases) !void { +pub fn addCases(ctx: *Cases, b: *std.Build) !void { { - const case = ctx.obj("multiline error messages", .{}); + const case = ctx.obj("multiline error messages", b.host); case.addError( \\comptime { @@ -39,7 +39,7 @@ pub fn addCases(ctx: *Cases) !void { } { - const case = ctx.obj("isolated carriage return in multiline string literal", .{}); + const case = ctx.obj("isolated carriage return in multiline string literal", b.host); case.addError("const foo = \\\\\test\r\r rogue carriage return\n;", &[_][]const u8{ ":1:19: error: expected ';' after declaration", @@ -48,7 +48,7 @@ pub fn addCases(ctx: *Cases) !void { } { - const case = ctx.obj("missing semicolon at EOF", .{}); + const case = ctx.obj("missing semicolon at EOF", b.host); case.addError( \\const foo = 1 , &[_][]const u8{ @@ -57,7 +57,7 @@ pub fn addCases(ctx: *Cases) !void { } { - const case = ctx.obj("argument causes error", .{}); + const case = ctx.obj("argument causes error", b.host); case.addError( \\pub export fn entry() void { @@ -80,7 +80,7 @@ pub fn addCases(ctx: *Cases) !void { } { - const case = ctx.obj("astgen failure in file struct", .{}); + const case = ctx.obj("astgen failure in file struct", b.host); case.addError( \\pub export fn entry() void { @@ -95,7 +95,7 @@ pub fn addCases(ctx: *Cases) !void { } { - const case = ctx.obj("invalid store to comptime field", .{}); + const case = ctx.obj("invalid store to comptime field", b.host); case.addError( \\const a = @import("a.zig"); @@ -119,7 +119,7 @@ pub fn addCases(ctx: *Cases) !void { } { - const case = ctx.obj("file in multiple modules", .{}); + const case = ctx.obj("file in multiple modules", b.host); case.addDepModule("foo", "foo.zig"); case.addError( @@ -138,7 +138,7 @@ pub fn addCases(ctx: *Cases) !void { } { - const case = ctx.obj("wrong same named struct", .{}); + const case = ctx.obj("wrong same named struct", b.host); case.addError( \\const a = @import("a.zig"); @@ -172,7 +172,7 @@ pub fn addCases(ctx: *Cases) !void { } { - const case = ctx.obj("non-printable invalid character", .{}); + const case = ctx.obj("non-printable invalid character", b.host); case.addError("\xff\xfe" ++ \\export fn foo() bool { @@ -185,7 +185,7 @@ pub fn addCases(ctx: *Cases) !void { } { - const case = ctx.obj("imported generic method call with invalid param", .{}); + const case = ctx.obj("imported generic method call with invalid param", b.host); case.addError( \\pub const import = @import("import.zig"); diff --git a/test/link/bss/build.zig b/test/link/bss/build.zig index 3c7964fae1ef..865af0a4889f 100644 --- a/test/link/bss/build.zig +++ b/test/link/bss/build.zig @@ -7,6 +7,7 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = "bss", .root_source_file = .{ .path = "main.zig" }, + .target = b.host, .optimize = .Debug, }); diff --git a/test/link/common_symbols/build.zig b/test/link/common_symbols/build.zig index bb44073e386f..9b5c9da66292 100644 --- a/test/link/common_symbols/build.zig +++ b/test/link/common_symbols/build.zig @@ -14,7 +14,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const lib_a = b.addStaticLibrary(.{ .name = "a", .optimize = optimize, - .target = .{}, + .target = b.host, }); lib_a.addCSourceFiles(.{ .files = &.{ "c.c", "a.c", "b.c" }, diff --git a/test/link/common_symbols_alignment/build.zig b/test/link/common_symbols_alignment/build.zig index 049a8f5a8ffb..63aff339a976 100644 --- a/test/link/common_symbols_alignment/build.zig +++ b/test/link/common_symbols_alignment/build.zig @@ -14,7 +14,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const lib_a = b.addStaticLibrary(.{ .name = "a", .optimize = optimize, - .target = .{}, + .target = b.host, }); lib_a.addCSourceFiles(.{ .files = &.{"a.c"}, diff --git a/test/link/elf.zig b/test/link/elf.zig index 4055ec9219eb..dafbf867ed2d 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -5,20 +5,20 @@ pub fn testAll(b: *Build) *Step { const elf_step = b.step("test-elf", "Run ELF tests"); - const default_target = CrossTarget{ + const default_target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, // TODO relax this once ELF linker is able to handle other archs .os_tag = .linux, - }; - const musl_target = CrossTarget{ + }); + const musl_target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl, - }; - const glibc_target = CrossTarget{ + }); + const glibc_target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .gnu, - }; + }); // Exercise linker in -r mode elf_step.dependOn(testEmitRelocatable(b, .{ .use_llvm = false, .target = musl_target })); @@ -132,14 +132,18 @@ pub fn testAll(b: *Build) *Step { fn testAbsSymbols(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "abs-symbols", opts); - const obj = addObject(b, "obj", opts); - addAsmSourceBytes(obj, + const obj = addObject(b, opts, .{ + .name = "obj", + .asm_source_bytes = \\.globl foo \\foo = 0x800008 - ); + \\ + , + }); - const exe = addExecutable(b, "test", opts); - addCSourceBytes(exe, + const exe = addExecutable(b, opts, .{ + .name = "test", + .c_source_bytes = \\#include \\#include \\#include @@ -159,7 +163,8 @@ fn testAbsSymbols(b: *Build, opts: Options) *Step { \\ foo = 5; \\ return 0; \\} - , &.{}); + , + }); exe.addObject(obj); exe.linkLibC(); @@ -173,31 +178,36 @@ fn testAbsSymbols(b: *Build, opts: Options) *Step { fn testAsNeeded(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "as-needed", opts); - const main_o = addObject(b, "main", opts); - addCSourceBytes(main_o, + const main_o = addObject(b, opts, .{ + .name = "main", + .c_source_bytes = \\#include \\int baz(); \\int main() { \\ printf("%d\n", baz()); \\ return 0; \\} - , &.{}); + \\ + , + }); main_o.linkLibC(); - const libfoo = addSharedLibrary(b, "foo", opts); + const libfoo = addSharedLibrary(b, opts, .{ .name = "foo" }); addCSourceBytes(libfoo, "int foo() { return 42; }", &.{}); - const libbar = addSharedLibrary(b, "bar", opts); + const libbar = addSharedLibrary(b, opts, .{ .name = "bar" }); addCSourceBytes(libbar, "int bar() { return 42; }", &.{}); - const libbaz = addSharedLibrary(b, "baz", opts); + const libbaz = addSharedLibrary(b, opts, .{ .name = "baz" }); addCSourceBytes(libbaz, \\int foo(); \\int baz() { return foo(); } , &.{}); { - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ + .name = "test", + }); exe.addObject(main_o); exe.linkSystemLibrary2("foo", .{ .needed = true }); exe.addLibraryPath(libfoo.getEmittedBinDirectory()); @@ -225,7 +235,9 @@ fn testAsNeeded(b: *Build, opts: Options) *Step { } { - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ + .name = "test", + }); exe.addObject(main_o); exe.linkSystemLibrary2("foo", .{ .needed = false }); exe.addLibraryPath(libfoo.getEmittedBinDirectory()); @@ -259,7 +271,7 @@ fn testAsNeeded(b: *Build, opts: Options) *Step { fn testCanonicalPlt(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "canonical-plt", opts); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(dso, \\void *foo() { \\ return foo; @@ -269,17 +281,21 @@ fn testCanonicalPlt(b: *Build, opts: Options) *Step { \\} , &.{}); - const b_o = addObject(b, "obj", opts); - addCSourceBytes(b_o, + const b_o = addObject(b, opts, .{ + .name = "obj", + .c_source_bytes = \\void *bar(); \\void *baz() { \\ return bar; \\} - , &.{}); - b_o.force_pic = true; + \\ + , + .pic = true, + }); - const main_o = addObject(b, "main", opts); - addCSourceBytes(main_o, + const main_o = addObject(b, opts, .{ + .name = "main", + .c_source_bytes = \\#include \\void *foo(); \\void *bar(); @@ -290,11 +306,15 @@ fn testCanonicalPlt(b: *Build, opts: Options) *Step { \\ assert(bar == baz()); \\ return 0; \\} - , &.{}); + \\ + , + .pic = false, + }); main_o.linkLibC(); - main_o.force_pic = false; - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ + .name = "main", + }); exe.addObject(main_o); exe.addObject(b_o); exe.linkLibrary(dso); @@ -311,7 +331,9 @@ fn testCanonicalPlt(b: *Build, opts: Options) *Step { fn testCommonSymbols(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "common-symbols", opts); - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ + .name = "test", + }); addCSourceBytes(exe, \\int foo; \\int bar; @@ -338,8 +360,9 @@ fn testCommonSymbols(b: *Build, opts: Options) *Step { fn testCommonSymbolsInArchive(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "common-symbols-in-archive", opts); - const a_o = addObject(b, "a", opts); - addCSourceBytes(a_o, + const a_o = addObject(b, opts, .{ + .name = "a", + .c_source_bytes = \\#include \\int foo; \\int bar; @@ -348,28 +371,43 @@ fn testCommonSymbolsInArchive(b: *Build, opts: Options) *Step { \\int main() { \\ printf("%d %d %d %d\n", foo, bar, baz, two ? two() : -1); \\} - , &.{"-fcommon"}); + \\ + , + .c_source_flags = &.{"-fcommon"}, + }); a_o.linkLibC(); - const b_o = addObject(b, "b", opts); - addCSourceBytes(b_o, "int foo = 5;", &.{"-fcommon"}); + const b_o = addObject(b, opts, .{ + .name = "b", + .c_source_bytes = "int foo = 5;", + .c_source_flags = &.{"-fcommon"}, + }); { - const c_o = addObject(b, "c", opts); - addCSourceBytes(c_o, + const c_o = addObject(b, opts, .{ + .name = "c", + .c_source_bytes = \\int bar; \\int two() { return 2; } - , &.{"-fcommon"}); - - const d_o = addObject(b, "d", opts); - addCSourceBytes(d_o, "int baz;", &.{"-fcommon"}); - - const lib = addStaticLibrary(b, "lib", opts); + \\ + , + .c_source_flags = &.{"-fcommon"}, + }); + + const d_o = addObject(b, opts, .{ + .name = "d", + .c_source_bytes = "int baz;", + .c_source_flags = &.{"-fcommon"}, + }); + + const lib = addStaticLibrary(b, opts, .{ .name = "lib" }); lib.addObject(b_o); lib.addObject(c_o); lib.addObject(d_o); - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ + .name = "test", + }); exe.addObject(a_o); exe.linkLibrary(lib); exe.linkLibC(); @@ -380,18 +418,23 @@ fn testCommonSymbolsInArchive(b: *Build, opts: Options) *Step { } { - const e_o = addObject(b, "e", opts); - addCSourceBytes(e_o, + const e_o = addObject(b, opts, .{ + .name = "e", + .c_source_bytes = \\int bar = 0; \\int baz = 7; \\int two() { return 2; } - , &.{"-fcommon"}); + , + .c_source_flags = &.{"-fcommon"}, + }); - const lib = addStaticLibrary(b, "lib", opts); + const lib = addStaticLibrary(b, opts, .{ .name = "lib" }); lib.addObject(b_o); lib.addObject(e_o); - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ + .name = "test", + }); exe.addObject(a_o); exe.linkLibrary(lib); exe.linkLibC(); @@ -407,21 +450,23 @@ fn testCommonSymbolsInArchive(b: *Build, opts: Options) *Step { fn testCopyrel(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "copyrel", opts); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(dso, \\int foo = 3; \\int bar = 5; , &.{}); - const exe = addExecutable(b, "main", opts); - addCSourceBytes(exe, + const exe = addExecutable(b, opts, .{ + .name = "main", + .c_source_bytes = \\#include \\extern int foo, bar; \\int main() { \\ printf("%d %d\n", foo, bar); \\ return 0; \\} - , &.{}); + , + }); exe.linkLibrary(dso); exe.linkLibC(); // https://github.com/ziglang/zig/issues/17619 @@ -437,7 +482,7 @@ fn testCopyrel(b: *Build, opts: Options) *Step { fn testCopyrelAlias(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "copyrel-alias", opts); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(dso, \\int bruh = 31; \\int foo = 42; @@ -445,7 +490,10 @@ fn testCopyrelAlias(b: *Build, opts: Options) *Step { \\extern int baz __attribute__((alias("foo"))); , &.{}); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ + .name = "main", + .pic = false, + }); addCSourceBytes(exe, \\#include \\extern int foo; @@ -461,7 +509,6 @@ fn testCopyrelAlias(b: *Build, opts: Options) *Step { , &.{}); exe.linkLibrary(dso); exe.linkLibC(); - exe.force_pic = false; exe.pie = false; const run = addRunArtifact(exe); @@ -474,28 +521,31 @@ fn testCopyrelAlias(b: *Build, opts: Options) *Step { fn testCopyrelAlignment(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "copyrel-alignment", opts); - const a_so = addSharedLibrary(b, "a", opts); + const a_so = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(a_so, "__attribute__((aligned(32))) int foo = 5;", &.{}); - const b_so = addSharedLibrary(b, "b", opts); + const b_so = addSharedLibrary(b, opts, .{ .name = "b" }); addCSourceBytes(b_so, "__attribute__((aligned(8))) int foo = 5;", &.{}); - const c_so = addSharedLibrary(b, "c", opts); + const c_so = addSharedLibrary(b, opts, .{ .name = "c" }); addCSourceBytes(c_so, "__attribute__((aligned(256))) int foo = 5;", &.{}); - const obj = addObject(b, "main", opts); - addCSourceBytes(obj, + const obj = addObject(b, opts, .{ + .name = "main", + .c_source_bytes = \\#include \\extern int foo; \\int main() { printf("%d\n", foo); } - , &.{}); + \\ + , + .pic = false, + }); obj.linkLibC(); - obj.force_pic = false; const exp_stdout = "5\n"; { - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); exe.addObject(obj); exe.linkLibrary(a_so); exe.linkLibC(); @@ -514,7 +564,7 @@ fn testCopyrelAlignment(b: *Build, opts: Options) *Step { } { - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); exe.addObject(obj); exe.linkLibrary(b_so); exe.linkLibC(); @@ -533,7 +583,7 @@ fn testCopyrelAlignment(b: *Build, opts: Options) *Step { } { - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); exe.addObject(obj); exe.linkLibrary(c_so); exe.linkLibC(); @@ -557,7 +607,7 @@ fn testCopyrelAlignment(b: *Build, opts: Options) *Step { fn testDsoPlt(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "dso-plt", opts); - const dso = addSharedLibrary(b, "dso", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "dso" }); addCSourceBytes(dso, \\#include \\void world() { @@ -573,7 +623,7 @@ fn testDsoPlt(b: *Build, opts: Options) *Step { , &.{}); dso.linkLibC(); - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ .name = "test" }); addCSourceBytes(exe, \\#include \\void world() { @@ -599,7 +649,7 @@ fn testDsoPlt(b: *Build, opts: Options) *Step { fn testDsoUndef(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "dso-undef", opts); - const dso = addSharedLibrary(b, "dso", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "dso" }); addCSourceBytes(dso, \\extern int foo; \\int bar = 5; @@ -607,13 +657,15 @@ fn testDsoUndef(b: *Build, opts: Options) *Step { , &.{}); dso.linkLibC(); - const obj = addObject(b, "obj", opts); - addCSourceBytes(obj, "int foo = 3;", &.{}); + const obj = addObject(b, opts, .{ + .name = "obj", + .c_source_bytes = "int foo = 3;", + }); - const lib = addStaticLibrary(b, "lib", opts); + const lib = addStaticLibrary(b, opts, .{ .name = "lib" }); lib.addObject(obj); - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ .name = "test" }); exe.linkLibrary(dso); exe.linkLibrary(lib); addCSourceBytes(exe, @@ -641,8 +693,9 @@ fn testDsoUndef(b: *Build, opts: Options) *Step { fn testEmitRelocatable(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "emit-relocatable", opts); - const obj1 = addObject(b, "obj1", opts); - addZigSourceBytes(obj1, + const obj1 = addObject(b, opts, .{ + .name = "obj1", + .zig_source_bytes = \\const std = @import("std"); \\extern var bar: i32; \\export fn foo() i32 { @@ -651,18 +704,20 @@ fn testEmitRelocatable(b: *Build, opts: Options) *Step { \\export fn printFoo() void { \\ std.debug.print("foo={d}\n", .{foo()}); \\} - ); - addCSourceBytes(obj1, + , + .c_source_bytes = \\#include \\int bar = 42; \\void printBar() { \\ fprintf(stderr, "bar=%d\n", bar); \\} - , &.{}); + , + }); obj1.linkLibC(); - const exe = addExecutable(b, "test", opts); - addZigSourceBytes(exe, + const exe = addExecutable(b, opts, .{ + .name = "test", + .zig_source_bytes = \\const std = @import("std"); \\extern fn printFoo() void; \\extern fn printBar() void; @@ -670,7 +725,8 @@ fn testEmitRelocatable(b: *Build, opts: Options) *Step { \\ printFoo(); \\ printBar(); \\} - ); + , + }); exe.addObject(obj1); exe.linkLibC(); @@ -688,20 +744,26 @@ fn testEmitRelocatable(b: *Build, opts: Options) *Step { fn testEmitStaticLib(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "emit-static-lib", opts); - const obj1 = addObject(b, "obj1", opts); - addCSourceBytes(obj1, + const obj1 = addObject(b, opts, .{ + .name = "obj1", + .c_source_bytes = \\int foo = 0; \\int bar = 2; \\int fooBar() { \\ return foo + bar; \\} - , &.{}); + , + }); - const obj2 = addObject(b, "obj2", opts); - addCSourceBytes(obj2, "int tentative;", &.{"-fcommon"}); + const obj2 = addObject(b, opts, .{ + .name = "obj2", + .c_source_bytes = "int tentative;", + .c_source_flags = &.{"-fcommon"}, + }); - const obj3 = addObject(b, "a_very_long_file_name_so_that_it_ends_up_in_strtab", opts); - addZigSourceBytes(obj3, + const obj3 = addObject(b, opts, .{ + .name = "a_very_long_file_name_so_that_it_ends_up_in_strtab", + .zig_source_bytes = \\fn weakFoo() callconv(.C) usize { \\ return 42; \\} @@ -710,9 +772,10 @@ fn testEmitStaticLib(b: *Build, opts: Options) *Step { \\ @export(weakFoo, .{ .name = "weakFoo", .linkage = .Weak }); \\ @export(strongBar, .{ .name = "strongBarAlias", .linkage = .Strong }); \\} - ); + , + }); - const lib = addStaticLibrary(b, "lib", opts); + const lib = addStaticLibrary(b, opts, .{ .name = "lib" }); lib.addObject(obj1); lib.addObject(obj2); lib.addObject(obj3); @@ -747,30 +810,36 @@ fn testEmitStaticLib(b: *Build, opts: Options) *Step { fn testEmitStaticLibZig(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "emit-static-lib-zig", opts); - const obj1 = addObject(b, "obj1", opts); - addZigSourceBytes(obj1, + const obj1 = addObject(b, opts, .{ + .name = "obj1", + .zig_source_bytes = \\export var foo: i32 = 42; \\export var bar: i32 = 2; - ); + , + }); - const lib = addStaticLibrary(b, "lib", opts); - addZigSourceBytes(lib, + const lib = addStaticLibrary(b, opts, .{ + .name = "lib", + .zig_source_bytes = \\extern var foo: i32; \\extern var bar: i32; \\export fn fooBar() i32 { \\ return foo + bar; \\} - ); + , + }); lib.addObject(obj1); - const exe = addExecutable(b, "test", opts); - addZigSourceBytes(exe, + const exe = addExecutable(b, opts, .{ + .name = "test", + .zig_source_bytes = \\const std = @import("std"); \\extern fn fooBar() i32; \\pub fn main() void { \\ std.debug.print("{d}", .{fooBar()}); \\} - ); + , + }); exe.linkLibrary(lib); const run = addRunArtifact(exe); @@ -783,7 +852,7 @@ fn testEmitStaticLibZig(b: *Build, opts: Options) *Step { fn testEmptyObject(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "empty-object", opts); - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ .name = "test" }); addCSourceBytes(exe, "int main() { return 0; }", &.{}); addCSourceBytes(exe, "", &.{}); exe.linkLibC(); @@ -798,18 +867,23 @@ fn testEmptyObject(b: *Build, opts: Options) *Step { fn testEntryPoint(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "entry-point", opts); - const a_o = addObject(b, "a", opts); - addAsmSourceBytes(a_o, + const a_o = addObject(b, opts, .{ + .name = "a", + .asm_source_bytes = \\.globl foo, bar \\foo = 0x1000 \\bar = 0x2000 - ); + \\ + , + }); - const b_o = addObject(b, "b", opts); - addCSourceBytes(b_o, "int main() { return 0; }", &.{}); + const b_o = addObject(b, opts, .{ + .name = "b", + .c_source_bytes = "int main() { return 0; }", + }); { - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); exe.addObject(a_o); exe.addObject(b_o); exe.entry = .{ .symbol_name = "foo" }; @@ -825,7 +899,7 @@ fn testEntryPoint(b: *Build, opts: Options) *Step { // TODO looks like not assigning a unique name to this executable will // cause an artifact collision taking the cached executable from the above // step instead of generating a new one. - const exe = addExecutable(b, "other", opts); + const exe = addExecutable(b, opts, .{ .name = "other" }); exe.addObject(a_o); exe.addObject(b_o); exe.entry = .{ .symbol_name = "bar" }; @@ -843,8 +917,9 @@ fn testEntryPoint(b: *Build, opts: Options) *Step { fn testExportDynamic(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "export-dynamic", opts); - const obj = addObject(b, "obj", opts); - addAsmSourceBytes(obj, + const obj = addObject(b, opts, .{ + .name = "obj", + .asm_source_bytes = \\.text \\ .globl foo \\ .hidden foo @@ -856,12 +931,14 @@ fn testExportDynamic(b: *Build, opts: Options) *Step { \\ .globl _start \\_start: \\ nop - ); + \\ + , + }); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(dso, "int baz = 10;", &.{}); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\extern int baz; \\int callBaz() { @@ -885,7 +962,7 @@ fn testExportDynamic(b: *Build, opts: Options) *Step { fn testExportSymbolsFromExe(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "export-symbols-from-exe", opts); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(dso, \\void expfn1(); \\void expfn2() {} @@ -895,7 +972,7 @@ fn testExportSymbolsFromExe(b: *Build, opts: Options) *Step { \\} , &.{}); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\void expfn1() {} \\void expfn2() {} @@ -923,10 +1000,10 @@ fn testExportSymbolsFromExe(b: *Build, opts: Options) *Step { fn testFuncAddress(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "func-address", opts); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(dso, "void fn() {}", &.{}); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\#include \\typedef void Func(); @@ -937,7 +1014,7 @@ fn testFuncAddress(b: *Build, opts: Options) *Step { \\} , &.{}); exe.linkLibrary(dso); - exe.force_pic = false; + exe.root_module.pic = false; exe.pie = false; const run = addRunArtifact(exe); @@ -950,8 +1027,9 @@ fn testFuncAddress(b: *Build, opts: Options) *Step { fn testGcSections(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "gc-sections", opts); - const obj = addObject(b, "obj", opts); - addCppSourceBytes(obj, + const obj = addObject(b, opts, .{ + .name = "obj", + .cpp_source_bytes = \\#include \\int two() { return 2; } \\int live_var1 = 1; @@ -966,14 +1044,15 @@ fn testGcSections(b: *Build, opts: Options) *Step { \\ printf("%d %d\n", live_var1, live_var2); \\ live_fn2(); \\} - , &.{}); + , + }); obj.link_function_sections = true; obj.link_data_sections = true; obj.linkLibC(); obj.linkLibCpp(); { - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ .name = "test" }); exe.addObject(obj); exe.link_gc_sections = false; exe.linkLibC(); @@ -1004,7 +1083,7 @@ fn testGcSections(b: *Build, opts: Options) *Step { } { - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ .name = "test" }); exe.addObject(obj); exe.link_gc_sections = true; exe.linkLibC(); @@ -1040,11 +1119,12 @@ fn testGcSections(b: *Build, opts: Options) *Step { fn testGcSectionsZig(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "gc-sections-zig", opts); - const obj = addObject(b, "obj", .{ + const obj = addObject(b, .{ .target = opts.target, .use_llvm = true, - }); - addCSourceBytes(obj, + }, .{ + .name = "obj", + .c_source_bytes = \\int live_var1 = 1; \\int live_var2 = 2; \\int dead_var1 = 3; @@ -1053,13 +1133,15 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step { \\void live_fn2() { live_fn1(); } \\void dead_fn1() {} \\void dead_fn2() { dead_fn1(); } - , &.{}); + , + }); obj.link_function_sections = true; obj.link_data_sections = true; { - const exe = addExecutable(b, "test1", opts); - addZigSourceBytes(exe, + const exe = addExecutable(b, opts, .{ + .name = "test1", + .zig_source_bytes = \\const std = @import("std"); \\extern var live_var1: i32; \\extern var live_var2: i32; @@ -1069,7 +1151,8 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step { \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable; \\ live_fn2(); \\} - ); + , + }); exe.addObject(obj); exe.link_gc_sections = false; @@ -1098,8 +1181,9 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step { } { - const exe = addExecutable(b, "test2", opts); - addZigSourceBytes(exe, + const exe = addExecutable(b, opts, .{ + .name = "test2", + .zig_source_bytes = \\const std = @import("std"); \\extern var live_var1: i32; \\extern var live_var2: i32; @@ -1109,7 +1193,8 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step { \\ stdout.writer().print("{d} {d}\n", .{ live_var1, live_var2 }) catch unreachable; \\ live_fn2(); \\} - ); + , + }); exe.addObject(obj); exe.link_gc_sections = true; @@ -1143,7 +1228,7 @@ fn testGcSectionsZig(b: *Build, opts: Options) *Step { fn testHiddenWeakUndef(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "hidden-weak-undef", opts); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(dso, \\__attribute__((weak, visibility("hidden"))) void foo(); \\void bar() { foo(); } @@ -1162,7 +1247,7 @@ fn testHiddenWeakUndef(b: *Build, opts: Options) *Step { fn testIFuncAlias(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "ifunc-alias", opts); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\#include \\void foo() {} @@ -1173,7 +1258,7 @@ fn testIFuncAlias(b: *Build, opts: Options) *Step { \\ assert(bar == bar2); \\} , &.{}); - exe.force_pic = true; + exe.root_module.pic = true; exe.linkLibC(); // https://github.com/ziglang/zig/issues/17619 exe.pie = true; @@ -1188,7 +1273,7 @@ fn testIFuncAlias(b: *Build, opts: Options) *Step { fn testIFuncDlopen(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "ifunc-dlopen", opts); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(dso, \\__attribute__((ifunc("resolve_foo"))) \\void foo(void); @@ -1200,7 +1285,7 @@ fn testIFuncDlopen(b: *Build, opts: Options) *Step { \\} , &.{}); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\#include \\#include @@ -1219,7 +1304,7 @@ fn testIFuncDlopen(b: *Build, opts: Options) *Step { exe.linkLibrary(dso); exe.linkLibC(); exe.linkSystemLibrary2("dl", .{}); - exe.force_pic = false; + exe.root_module.pic = false; exe.pie = false; const run = addRunArtifact(exe); @@ -1232,8 +1317,9 @@ fn testIFuncDlopen(b: *Build, opts: Options) *Step { fn testIFuncDso(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "ifunc-dso", opts); - const dso = addSharedLibrary(b, "a", opts); - addCSourceBytes(dso, + const dso = addSharedLibrary(b, opts, .{ + .name = "a", + .c_source_bytes = \\#include \\__attribute__((ifunc("resolve_foobar"))) \\void foobar(void); @@ -1244,16 +1330,19 @@ fn testIFuncDso(b: *Build, opts: Options) *Step { \\static Func *resolve_foobar(void) { \\ return real_foobar; \\} - , &.{}); + , + }); dso.linkLibC(); - const exe = addExecutable(b, "main", opts); - addCSourceBytes(exe, + const exe = addExecutable(b, opts, .{ + .name = "main", + .c_source_bytes = \\void foobar(void); \\int main() { \\ foobar(); \\} - , &.{}); + , + }); exe.linkLibrary(dso); const run = addRunArtifact(exe); @@ -1283,7 +1372,7 @@ fn testIFuncDynamic(b: *Build, opts: Options) *Step { ; { - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, main_c, &.{}); exe.linkLibC(); exe.link_z_lazy = true; @@ -1295,7 +1384,7 @@ fn testIFuncDynamic(b: *Build, opts: Options) *Step { test_step.dependOn(&run.step); } { - const exe = addExecutable(b, "other", opts); + const exe = addExecutable(b, opts, .{ .name = "other" }); addCSourceBytes(exe, main_c, &.{}); exe.linkLibC(); // https://github.com/ziglang/zig/issues/17619 @@ -1312,7 +1401,7 @@ fn testIFuncDynamic(b: *Build, opts: Options) *Step { fn testIFuncExport(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "ifunc-export", opts); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(dso, \\#include \\__attribute__((ifunc("resolve_foobar"))) @@ -1338,7 +1427,7 @@ fn testIFuncExport(b: *Build, opts: Options) *Step { fn testIFuncFuncPtr(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "ifunc-func-ptr", opts); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\typedef int Fn(); \\int foo() __attribute__((ifunc("resolve_foo"))); @@ -1361,7 +1450,7 @@ fn testIFuncFuncPtr(b: *Build, opts: Options) *Step { \\ printf("%d\n", f()); \\} , &.{}); - exe.force_pic = true; + exe.root_module.pic = true; exe.linkLibC(); // https://github.com/ziglang/zig/issues/17619 exe.pie = true; @@ -1376,7 +1465,7 @@ fn testIFuncFuncPtr(b: *Build, opts: Options) *Step { fn testIFuncNoPlt(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "ifunc-noplt", opts); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\#include \\__attribute__((ifunc("resolve_foo"))) @@ -1392,7 +1481,7 @@ fn testIFuncNoPlt(b: *Build, opts: Options) *Step { \\ foo(); \\} , &.{"-fno-plt"}); - exe.force_pic = true; + exe.root_module.pic = true; exe.linkLibC(); // https://github.com/ziglang/zig/issues/17619 exe.pie = true; @@ -1407,7 +1496,7 @@ fn testIFuncNoPlt(b: *Build, opts: Options) *Step { fn testIFuncStatic(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "ifunc-static", opts); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\#include \\void foo() __attribute__((ifunc("resolve_foo"))); @@ -1435,7 +1524,7 @@ fn testIFuncStatic(b: *Build, opts: Options) *Step { fn testIFuncStaticPie(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "ifunc-static-pie", opts); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\#include \\void foo() __attribute__((ifunc("resolve_foo"))); @@ -1451,7 +1540,7 @@ fn testIFuncStaticPie(b: *Build, opts: Options) *Step { \\} , &.{}); exe.linkage = .static; - exe.force_pic = true; + exe.root_module.pic = true; exe.pie = true; exe.linkLibC(); @@ -1478,7 +1567,7 @@ fn testImageBase(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "image-base", opts); { - const exe = addExecutable(b, "main1", opts); + const exe = addExecutable(b, opts, .{ .name = "main1" }); addCSourceBytes(exe, \\#include \\int main() { @@ -1502,7 +1591,7 @@ fn testImageBase(b: *Build, opts: Options) *Step { } { - const exe = addExecutable(b, "main2", opts); + const exe = addExecutable(b, opts, .{ .name = "main2" }); addCSourceBytes(exe, "void _start() {}", &.{}); exe.image_base = 0xffffffff8000000; @@ -1520,22 +1609,26 @@ fn testImageBase(b: *Build, opts: Options) *Step { fn testImportingDataDynamic(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "importing-data-dynamic", opts); - const dso = addSharedLibrary(b, "a", .{ + const dso = addSharedLibrary(b, .{ .target = opts.target, .optimize = opts.optimize, .use_llvm = true, + }, .{ + .name = "a", + .c_source_bytes = "int foo = 42;", }); - addCSourceBytes(dso, "int foo = 42;", &.{}); - const main = addExecutable(b, "main", opts); - addZigSourceBytes(main, + const main = addExecutable(b, opts, .{ + .name = "main", + .zig_source_bytes = \\extern var foo: i32; \\pub fn main() void { \\ @import("std").debug.print("{d}\n", .{foo}); \\} - ); + , + .strip = true, // TODO temp hack + }); main.pie = true; - main.strip = true; // TODO temp hack main.linkLibrary(dso); main.linkLibC(); @@ -1549,28 +1642,34 @@ fn testImportingDataDynamic(b: *Build, opts: Options) *Step { fn testImportingDataStatic(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "importing-data-static", opts); - const obj = addObject(b, "a", .{ + const obj = addObject(b, .{ .target = opts.target, .optimize = opts.optimize, .use_llvm = true, + }, .{ + .name = "a", + .c_source_bytes = "int foo = 42;", }); - addCSourceBytes(obj, "int foo = 42;", &.{}); - const lib = addStaticLibrary(b, "a", .{ + const lib = addStaticLibrary(b, .{ .target = opts.target, .optimize = opts.optimize, .use_llvm = true, + }, .{ + .name = "a", }); lib.addObject(obj); - const main = addExecutable(b, "main", opts); - addZigSourceBytes(main, + const main = addExecutable(b, opts, .{ + .name = "main", + .zig_source_bytes = \\extern var foo: i32; \\pub fn main() void { \\ @import("std").debug.print("{d}\n", .{foo}); \\} - ); - main.strip = true; // TODO temp hack + , + .strip = true, // TODO temp hack + }); main.linkLibrary(lib); main.linkLibC(); @@ -1584,63 +1683,76 @@ fn testImportingDataStatic(b: *Build, opts: Options) *Step { fn testInitArrayOrder(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "init-array-order", opts); - const a_o = addObject(b, "a", opts); - addCSourceBytes(a_o, + const a_o = addObject(b, opts, .{ + .name = "a", + .c_source_bytes = \\#include \\__attribute__((constructor(10000))) void init4() { printf("1"); } - , &.{}); + , + }); a_o.linkLibC(); - const b_o = addObject(b, "b", opts); - addCSourceBytes(b_o, + const b_o = addObject(b, opts, .{ + .name = "b", + .c_source_bytes = \\#include \\__attribute__((constructor(1000))) void init3() { printf("2"); } - , &.{}); + , + }); b_o.linkLibC(); - const c_o = addObject(b, "c", opts); - addCSourceBytes(c_o, + const c_o = addObject(b, opts, .{ + .name = "c", + .c_source_bytes = \\#include \\__attribute__((constructor)) void init1() { printf("3"); } - , &.{}); + , + }); c_o.linkLibC(); - const d_o = addObject(b, "d", opts); - addCSourceBytes(d_o, + const d_o = addObject(b, opts, .{ + .name = "d", + .c_source_bytes = \\#include \\__attribute__((constructor)) void init2() { printf("4"); } - , &.{}); + , + }); d_o.linkLibC(); - const e_o = addObject(b, "e", opts); - addCSourceBytes(e_o, + const e_o = addObject(b, opts, .{ + .name = "e", + .c_source_bytes = \\#include \\__attribute__((destructor(10000))) void fini4() { printf("5"); } - , &.{}); + , + }); e_o.linkLibC(); - const f_o = addObject(b, "f", opts); - addCSourceBytes(f_o, + const f_o = addObject(b, opts, .{ + .name = "f", + .c_source_bytes = \\#include \\__attribute__((destructor(1000))) void fini3() { printf("6"); } - , &.{}); + , + }); f_o.linkLibC(); - const g_o = addObject(b, "g", opts); - addCSourceBytes(g_o, + const g_o = addObject(b, opts, .{ + .name = "g", + .c_source_bytes = \\#include \\__attribute__((destructor)) void fini1() { printf("7"); } - , &.{}); + , + }); g_o.linkLibC(); - const h_o = addObject(b, "h", opts); - addCSourceBytes(h_o, - \\#include - \\__attribute__((destructor)) void fini2() { printf("8"); } - , &.{}); + const h_o = addObject(b, opts, .{ .name = "h", .c_source_bytes = + \\#include + \\__attribute__((destructor)) void fini2() { printf("8"); } + }); h_o.linkLibC(); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, "int main() { return 0; }", &.{}); exe.addObject(a_o); exe.addObject(b_o); @@ -1651,7 +1763,7 @@ fn testInitArrayOrder(b: *Build, opts: Options) *Step { exe.addObject(g_o); exe.addObject(h_o); - if (opts.target.isGnuLibC()) { + if (opts.target.result.isGnuLibC()) { // TODO I think we need to clarify our use of `-fPIC -fPIE` flags for different targets exe.pie = true; } @@ -1666,7 +1778,7 @@ fn testInitArrayOrder(b: *Build, opts: Options) *Step { fn testLargeAlignmentDso(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "large-alignment-dso", opts); - const dso = addSharedLibrary(b, "dso", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "dso" }); addCSourceBytes(dso, \\#include \\#include @@ -1695,7 +1807,7 @@ fn testLargeAlignmentDso(b: *Build, opts: Options) *Step { check.checkComputeCompare("addr2 16 %", .{ .op = .eq, .value = .{ .literal = 0 } }); test_step.dependOn(&check.step); - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ .name = "test" }); addCSourceBytes(exe, \\void greet(); \\int main() { greet(); } @@ -1715,7 +1827,7 @@ fn testLargeAlignmentDso(b: *Build, opts: Options) *Step { fn testLargeAlignmentExe(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "large-alignment-exe", opts); - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ .name = "test" }); addCSourceBytes(exe, \\#include \\#include @@ -1760,7 +1872,7 @@ fn testLargeAlignmentExe(b: *Build, opts: Options) *Step { fn testLargeBss(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "large-bss", opts); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\char arr[0x100000000]; \\int main() { @@ -1781,27 +1893,31 @@ fn testLargeBss(b: *Build, opts: Options) *Step { fn testLinkOrder(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "link-order", opts); - const obj = addObject(b, "obj", opts); - addCSourceBytes(obj, "void foo() {}", &.{}); - obj.force_pic = true; + const obj = addObject(b, opts, .{ + .name = "obj", + .c_source_bytes = "void foo() {}", + .pic = true, + }); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); dso.addObject(obj); - const lib = addStaticLibrary(b, "b", opts); + const lib = addStaticLibrary(b, opts, .{ .name = "b" }); lib.addObject(obj); - const main_o = addObject(b, "main", opts); - addCSourceBytes(main_o, + const main_o = addObject(b, opts, .{ + .name = "main", + .c_source_bytes = \\void foo(); \\int main() { \\ foo(); \\} - , &.{}); + , + }); // https://github.com/ziglang/zig/issues/17450 // { - // const exe = addExecutable(b, "main1", opts); + // const exe = addExecutable(b, opts, .{ .name = "main1"}); // exe.addObject(main_o); // exe.linkSystemLibrary2("a", .{}); // exe.addLibraryPath(dso.getEmittedBinDirectory()); @@ -1818,7 +1934,7 @@ fn testLinkOrder(b: *Build, opts: Options) *Step { // } { - const exe = addExecutable(b, "main2", opts); + const exe = addExecutable(b, opts, .{ .name = "main2" }); exe.addObject(main_o); exe.linkSystemLibrary2("b", .{}); exe.addLibraryPath(lib.getEmittedBinDirectory()); @@ -1840,14 +1956,14 @@ fn testLinkOrder(b: *Build, opts: Options) *Step { fn testLdScript(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "ld-script", opts); - const dso = addSharedLibrary(b, "bar", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "bar" }); addCSourceBytes(dso, "int foo() { return 42; }", &.{}); const scripts = WriteFile.create(b); _ = scripts.add("liba.so", "INPUT(libfoo.so)"); _ = scripts.add("libfoo.so", "GROUP(AS_NEEDED(-lbar))"); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\int foo(); \\int main() { @@ -1875,7 +1991,7 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step { const scripts = WriteFile.create(b); _ = scripts.add("liba.so", "INPUT(libfoo.so)"); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, "int main() { return 0; }", &.{}); exe.linkSystemLibrary2("a", .{}); exe.addLibraryPath(scripts.getDirectory()); @@ -1895,13 +2011,15 @@ fn testLdScriptPathError(b: *Build, opts: Options) *Step { fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "mismatched-cpu-architecture-error", opts); - const obj = addObject(b, "a", .{ - .target = .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .gnu }, + const obj = addObject(b, .{ + .target = b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .gnu }), + }, .{ + .name = "a", + .c_source_bytes = "int foo;", + .strip = true, }); - addCSourceBytes(obj, "int foo;", &.{}); - obj.strip = true; - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\extern int foo; \\int main() { @@ -1922,7 +2040,7 @@ fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step { fn testLinkingC(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "linking-c", opts); - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ .name = "test" }); addCSourceBytes(exe, \\#include \\int main() { @@ -1951,7 +2069,7 @@ fn testLinkingC(b: *Build, opts: Options) *Step { fn testLinkingCpp(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "linking-cpp", opts); - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ .name = "test" }); addCppSourceBytes(exe, \\#include \\int main() { @@ -1981,24 +2099,28 @@ fn testLinkingCpp(b: *Build, opts: Options) *Step { fn testLinkingObj(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "linking-obj", opts); - const obj = addObject(b, "aobj", opts); - addZigSourceBytes(obj, + const obj = addObject(b, opts, .{ + .name = "aobj", + .zig_source_bytes = \\extern var mod: usize; \\export fn callMe() usize { \\ return me * mod; \\} \\var me: usize = 42; - ); + , + }); - const exe = addExecutable(b, "testobj", opts); - addZigSourceBytes(exe, + const exe = addExecutable(b, opts, .{ + .name = "testobj", + .zig_source_bytes = \\const std = @import("std"); \\extern fn callMe() usize; \\export var mod: usize = 2; \\pub fn main() void { \\ std.debug.print("{d}\n", .{callMe()}); \\} - ); + , + }); exe.addObject(obj); const run = addRunArtifact(exe); @@ -2011,26 +2133,32 @@ fn testLinkingObj(b: *Build, opts: Options) *Step { fn testLinkingStaticLib(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "linking-static-lib", opts); - const obj = addObject(b, "bobj", opts); - addZigSourceBytes(obj, "export var bar: i32 = -42;"); + const obj = addObject(b, opts, .{ + .name = "bobj", + .zig_source_bytes = "export var bar: i32 = -42;", + }); - const lib = addStaticLibrary(b, "alib", opts); - addZigSourceBytes(lib, + const lib = addStaticLibrary(b, opts, .{ + .name = "alib", + .zig_source_bytes = \\export fn foo() i32 { \\ return 42; \\} - ); + , + }); lib.addObject(obj); - const exe = addExecutable(b, "testlib", opts); - addZigSourceBytes(exe, + const exe = addExecutable(b, opts, .{ + .name = "testlib", + .zig_source_bytes = \\const std = @import("std"); \\extern fn foo() i32; \\extern var bar: i32; \\pub fn main() void { \\ std.debug.print("{d}\n", .{foo() + bar}); \\} - ); + , + }); exe.linkLibrary(lib); const run = addRunArtifact(exe); @@ -2043,12 +2171,14 @@ fn testLinkingStaticLib(b: *Build, opts: Options) *Step { fn testLinkingZig(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "linking-zig-static", opts); - const exe = addExecutable(b, "test", opts); - addZigSourceBytes(exe, + const exe = addExecutable(b, opts, .{ + .name = "test", + .zig_source_bytes = \\pub fn main() void { \\ @import("std").debug.print("Hello World!\n", .{}); \\} - ); + , + }); const run = addRunArtifact(exe); run.expectStdErrEqual("Hello World!\n"); @@ -2069,7 +2199,7 @@ fn testLinkingZig(b: *Build, opts: Options) *Step { fn testNoEhFrameHdr(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "no-eh-frame-hdr", opts); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, "int main() { return 0; }", &.{}); exe.link_eh_frame_hdr = false; exe.linkLibC(); @@ -2086,7 +2216,7 @@ fn testNoEhFrameHdr(b: *Build, opts: Options) *Step { fn testPie(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "hello-pie", opts); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\#include \\int main() { @@ -2095,7 +2225,7 @@ fn testPie(b: *Build, opts: Options) *Step { \\} , &.{}); exe.linkLibC(); - exe.force_pic = true; + exe.root_module.pic = true; exe.pie = true; const run = addRunArtifact(exe); @@ -2117,7 +2247,7 @@ fn testPie(b: *Build, opts: Options) *Step { fn testPltGot(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "plt-got", opts); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(dso, \\#include \\void ignore(void *foo) {} @@ -2127,7 +2257,7 @@ fn testPltGot(b: *Build, opts: Options) *Step { , &.{}); dso.linkLibC(); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\void ignore(void *); \\int hello(); @@ -2135,7 +2265,7 @@ fn testPltGot(b: *Build, opts: Options) *Step { \\int main() { hello(); } , &.{}); exe.linkLibrary(dso); - exe.force_pic = true; + exe.root_module.pic = true; exe.linkLibC(); // https://github.com/ziglang/zig/issues/17619 exe.pie = true; @@ -2151,10 +2281,12 @@ fn testPreinitArray(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "preinit-array", opts); { - const obj = addObject(b, "obj", opts); - addCSourceBytes(obj, "void _start() {}", &.{}); + const obj = addObject(b, opts, .{ + .name = "obj", + .c_source_bytes = "void _start() {}", + }); - const exe = addExecutable(b, "main1", opts); + const exe = addExecutable(b, opts, .{ .name = "main1" }); exe.addObject(obj); const check = exe.checkObject(); @@ -2163,7 +2295,7 @@ fn testPreinitArray(b: *Build, opts: Options) *Step { } { - const exe = addExecutable(b, "main2", opts); + const exe = addExecutable(b, opts, .{ .name = "main2" }); addCSourceBytes(exe, \\void preinit_fn() {} \\int main() {} @@ -2183,38 +2315,48 @@ fn testPreinitArray(b: *Build, opts: Options) *Step { fn testRelocatableArchive(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "relocatable-archive", opts); - const obj1 = addObject(b, "obj1", opts); - addCSourceBytes(obj1, + const obj1 = addObject(b, opts, .{ + .name = "obj1", + .c_source_bytes = \\void bar(); \\void foo() { \\ bar(); \\} - , &.{}); + , + }); - const obj2 = addObject(b, "obj2", opts); - addCSourceBytes(obj2, + const obj2 = addObject(b, opts, .{ + .name = "obj2", + .c_source_bytes = \\void bar() {} - , &.{}); + , + }); - const obj3 = addObject(b, "obj3", opts); - addCSourceBytes(obj3, + const obj3 = addObject(b, opts, .{ + .name = "obj3", + .c_source_bytes = \\void baz(); - , &.{}); + , + }); - const obj4 = addObject(b, "obj4", opts); - addCSourceBytes(obj4, + const obj4 = addObject(b, opts, .{ + .name = "obj4", + .c_source_bytes = \\void foo(); \\int main() { \\ foo(); \\} - , &.{}); + , + }); - const lib = addStaticLibrary(b, "lib", opts); + const lib = addStaticLibrary(b, opts, .{ .name = "lib" }); lib.addObject(obj1); lib.addObject(obj2); lib.addObject(obj3); - const obj5 = addObject(b, "obj5", opts); + const obj5 = addObject(b, opts, .{ + .name = "obj5", + }); obj5.addObject(obj4); obj5.linkLibrary(lib); @@ -2234,13 +2376,15 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "relocatable-eh-frame", opts); { - const obj = addObject(b, "obj1", opts); - addCppSourceBytes(obj, + const obj = addObject(b, opts, .{ + .name = "obj1", + .cpp_source_bytes = \\#include \\int try_me() { \\ throw std::runtime_error("Oh no!"); \\} - , &.{}); + , + }); addCppSourceBytes(obj, \\extern int try_me(); \\int try_again() { @@ -2249,7 +2393,7 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step { , &.{}); obj.linkLibCpp(); - const exe = addExecutable(b, "test1", opts); + const exe = addExecutable(b, opts, .{ .name = "test1" }); addCppSourceBytes(exe, \\#include \\#include @@ -2273,13 +2417,15 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step { { // Let's make the object file COMDAT group heavy! - const obj = addObject(b, "obj2", opts); - addCppSourceBytes(obj, + const obj = addObject(b, opts, .{ + .name = "obj2", + .cpp_source_bytes = \\#include \\int try_me() { \\ throw std::runtime_error("Oh no!"); \\} - , &.{}); + , + }); addCppSourceBytes(obj, \\extern int try_me(); \\int try_again() { @@ -2301,7 +2447,7 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step { , &.{}); obj.linkLibCpp(); - const exe = addExecutable(b, "test2", opts); + const exe = addExecutable(b, opts, .{ .name = "test2" }); exe.addObject(obj); exe.linkLibCpp(); @@ -2316,13 +2462,18 @@ fn testRelocatableEhFrame(b: *Build, opts: Options) *Step { fn testRelocatableNoEhFrame(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "relocatable-no-eh-frame", opts); - const obj1 = addObject(b, "obj1", opts); - addCSourceBytes(obj1, "int bar() { return 42; }", &.{ - "-fno-unwind-tables", - "-fno-asynchronous-unwind-tables", + const obj1 = addObject(b, opts, .{ + .name = "obj1", + .c_source_bytes = "int bar() { return 42; }", + .c_source_flags = &.{ + "-fno-unwind-tables", + "-fno-asynchronous-unwind-tables", + }, }); - const obj2 = addObject(b, "obj2", opts); + const obj2 = addObject(b, opts, .{ + .name = "obj2", + }); obj2.addObject(obj1); const check1 = obj1.checkObject(); @@ -2343,23 +2494,25 @@ fn testRelocatableNoEhFrame(b: *Build, opts: Options) *Step { fn testSharedAbsSymbol(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "shared-abs-symbol", opts); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); addAsmSourceBytes(dso, \\.globl foo \\foo = 3; ); - const obj = addObject(b, "obj", opts); - addCSourceBytes(obj, + const obj = addObject(b, opts, .{ + .name = "obj", + .c_source_bytes = \\#include \\extern char foo; \\int main() { printf("foo=%p\n", &foo); } - , &.{}); - obj.force_pic = true; + , + .pic = true, + }); obj.linkLibC(); { - const exe = addExecutable(b, "main1", opts); + const exe = addExecutable(b, opts, .{ .name = "main1" }); exe.addObject(obj); exe.linkLibrary(dso); exe.pie = true; @@ -2380,7 +2533,7 @@ fn testSharedAbsSymbol(b: *Build, opts: Options) *Step { // https://github.com/ziglang/zig/issues/17430 // { - // const exe = addExecutable(b, "main2", opts); + // const exe = addExecutable(b, opts, .{ .name = "main2"}); // exe.addObject(obj); // exe.linkLibrary(dso); // exe.pie = false; @@ -2405,20 +2558,22 @@ fn testSharedAbsSymbol(b: *Build, opts: Options) *Step { fn testStrip(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "strip", opts); - const obj = addObject(b, "obj", opts); - addCSourceBytes(obj, + const obj = addObject(b, opts, .{ + .name = "obj", + .c_source_bytes = \\#include \\int main() { \\ printf("Hello!\n"); \\ return 0; \\} - , &.{}); + , + }); obj.linkLibC(); { - const exe = addExecutable(b, "main1", opts); + const exe = addExecutable(b, opts, .{ .name = "main1" }); exe.addObject(obj); - exe.strip = false; + exe.root_module.strip = false; exe.linkLibC(); const check = exe.checkObject(); @@ -2429,9 +2584,9 @@ fn testStrip(b: *Build, opts: Options) *Step { } { - const exe = addExecutable(b, "main2", opts); + const exe = addExecutable(b, opts, .{ .name = "main2" }); exe.addObject(obj); - exe.strip = true; + exe.root_module.strip = true; exe.linkLibC(); const check = exe.checkObject(); @@ -2447,16 +2602,19 @@ fn testStrip(b: *Build, opts: Options) *Step { fn testTlsDfStaticTls(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-df-static-tls", opts); - const obj = addObject(b, "obj", opts); - addCSourceBytes(obj, + const obj = addObject(b, opts, .{ + .name = "obj", + .c_source_bytes = \\static _Thread_local int foo = 5; \\void mutate() { ++foo; } \\int bar() { return foo; } - , &.{"-ftls-model=initial-exec"}); - obj.force_pic = true; + , + .c_source_flags = &.{"-ftls-model=initial-exec"}, + .pic = true, + }); { - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); dso.addObject(obj); // dso.link_relax = true; @@ -2468,7 +2626,7 @@ fn testTlsDfStaticTls(b: *Build, opts: Options) *Step { // TODO add -Wl,--no-relax // { - // const dso = addSharedLibrary(b, "a", opts); + // const dso = addSharedLibrary(b, opts, .{ .name = "a"}); // dso.addObject(obj); // dso.link_relax = false; @@ -2484,7 +2642,7 @@ fn testTlsDfStaticTls(b: *Build, opts: Options) *Step { fn testTlsDso(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-dso", opts); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(dso, \\extern _Thread_local int foo; \\_Thread_local int bar; @@ -2492,7 +2650,7 @@ fn testTlsDso(b: *Build, opts: Options) *Step { \\int get_bar1() { return bar; } , &.{}); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\#include \\_Thread_local int foo; @@ -2526,8 +2684,9 @@ fn testTlsDso(b: *Build, opts: Options) *Step { fn testTlsGd(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-gd", opts); - const main_o = addObject(b, "main", opts); - addCSourceBytes(main_o, + const main_o = addObject(b, opts, .{ + .name = "main", + .c_source_bytes = \\#include \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1; \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x2; @@ -2540,37 +2699,42 @@ fn testTlsGd(b: *Build, opts: Options) *Step { \\ printf("%d %d %d %d %d %d\n", x1, x2, x3, x4, get_x5(), get_x6()); \\ return 0; \\} - , &.{}); + , + .pic = true, + }); main_o.linkLibC(); - main_o.force_pic = true; - const a_o = addObject(b, "a", opts); - addCSourceBytes(a_o, + const a_o = addObject(b, opts, .{ + .name = "a", + .c_source_bytes = \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3; \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5; \\int get_x5() { return x5; } - , &.{}); - a_o.force_pic = true; + , + .pic = true, + }); - const b_o = addObject(b, "b", opts); - addCSourceBytes(b_o, + const b_o = addObject(b, opts, .{ + .name = "b", + .c_source_bytes = \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4; \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6; \\int get_x6() { return x6; } - , &.{}); - b_o.force_pic = true; + , + .pic = true, + }); const exp_stdout = "1 2 3 4 5 6\n"; - const dso1 = addSharedLibrary(b, "a", opts); + const dso1 = addSharedLibrary(b, opts, .{ .name = "a" }); dso1.addObject(a_o); - const dso2 = addSharedLibrary(b, "b", opts); + const dso2 = addSharedLibrary(b, opts, .{ .name = "b" }); dso2.addObject(b_o); // dso2.link_relax = false; // TODO { - const exe = addExecutable(b, "main1", opts); + const exe = addExecutable(b, opts, .{ .name = "main1" }); exe.addObject(main_o); exe.linkLibrary(dso1); exe.linkLibrary(dso2); @@ -2581,7 +2745,7 @@ fn testTlsGd(b: *Build, opts: Options) *Step { } { - const exe = addExecutable(b, "main2", opts); + const exe = addExecutable(b, opts, .{ .name = "main2" }); exe.addObject(main_o); // exe.link_relax = false; // TODO exe.linkLibrary(dso1); @@ -2594,7 +2758,7 @@ fn testTlsGd(b: *Build, opts: Options) *Step { // https://github.com/ziglang/zig/issues/17430 ?? // { - // const exe = addExecutable(b, "main3", opts); + // const exe = addExecutable(b, opts, .{ .name = "main3"}); // exe.addObject(main_o); // exe.linkLibrary(dso1); // exe.linkLibrary(dso2); @@ -2606,7 +2770,7 @@ fn testTlsGd(b: *Build, opts: Options) *Step { // } // { - // const exe = addExecutable(b, "main4", opts); + // const exe = addExecutable(b, opts, .{ .name = "main4"}); // exe.addObject(main_o); // // exe.link_relax = false; // TODO // exe.linkLibrary(dso1); @@ -2624,8 +2788,9 @@ fn testTlsGd(b: *Build, opts: Options) *Step { fn testTlsGdNoPlt(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-gd-no-plt", opts); - const obj = addObject(b, "obj", opts); - addCSourceBytes(obj, + const obj = addObject(b, opts, .{ + .name = "obj", + .c_source_bytes = \\#include \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1; \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x2; @@ -2639,18 +2804,20 @@ fn testTlsGdNoPlt(b: *Build, opts: Options) *Step { \\ printf("%d %d %d %d %d %d\n", x1, x2, x3, x4, get_x5(), get_x6()); \\ return 0; \\} - , &.{"-fno-plt"}); - obj.force_pic = true; + , + .c_source_flags = &.{"-fno-plt"}, + .pic = true, + }); obj.linkLibC(); - const a_so = addSharedLibrary(b, "a", opts); + const a_so = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(a_so, \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x3 = 3; \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x5 = 5; \\int get_x5() { return x5; } , &.{"-fno-plt"}); - const b_so = addSharedLibrary(b, "b", opts); + const b_so = addSharedLibrary(b, opts, .{ .name = "b" }); addCSourceBytes(b_so, \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x4 = 4; \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x6 = 6; @@ -2659,7 +2826,7 @@ fn testTlsGdNoPlt(b: *Build, opts: Options) *Step { // b_so.link_relax = false; // TODO { - const exe = addExecutable(b, "main1", opts); + const exe = addExecutable(b, opts, .{ .name = "main1" }); exe.addObject(obj); exe.linkLibrary(a_so); exe.linkLibrary(b_so); @@ -2673,7 +2840,7 @@ fn testTlsGdNoPlt(b: *Build, opts: Options) *Step { } { - const exe = addExecutable(b, "main2", opts); + const exe = addExecutable(b, opts, .{ .name = "main2" }); exe.addObject(obj); exe.linkLibrary(a_so); exe.linkLibrary(b_so); @@ -2693,8 +2860,9 @@ fn testTlsGdNoPlt(b: *Build, opts: Options) *Step { fn testTlsGdToIe(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-gd-to-ie", opts); - const a_o = addObject(b, "a", opts); - addCSourceBytes(a_o, + const a_o = addObject(b, opts, .{ + .name = "a", + .c_source_bytes = \\#include \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int x1 = 1; \\__attribute__((tls_model("global-dynamic"))) _Thread_local int x2 = 2; @@ -2705,22 +2873,25 @@ fn testTlsGdToIe(b: *Build, opts: Options) *Step { \\ printf("%d %d %d\n", x1, x2, x3); \\ return 0; \\} - , &.{}); + , + .pic = true, + }); a_o.linkLibC(); - a_o.force_pic = true; - const b_o = addObject(b, "b", opts); - addCSourceBytes(b_o, + const b_o = addObject(b, opts, .{ + .name = "b", + .c_source_bytes = \\int foo(); \\int main() { foo(); } - , &.{}); - b_o.force_pic = true; + , + .pic = true, + }); { - const dso = addSharedLibrary(b, "a1", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a1" }); dso.addObject(a_o); - const exe = addExecutable(b, "main1", opts); + const exe = addExecutable(b, opts, .{ .name = "main1" }); exe.addObject(b_o); exe.linkLibrary(dso); exe.linkLibC(); @@ -2733,11 +2904,11 @@ fn testTlsGdToIe(b: *Build, opts: Options) *Step { } { - const dso = addSharedLibrary(b, "a2", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a2" }); dso.addObject(a_o); // dso.link_relax = false; // TODO - const exe = addExecutable(b, "main2", opts); + const exe = addExecutable(b, opts, .{ .name = "main2" }); exe.addObject(b_o); exe.linkLibrary(dso); exe.linkLibC(); @@ -2750,11 +2921,11 @@ fn testTlsGdToIe(b: *Build, opts: Options) *Step { } // { - // const dso = addSharedLibrary(b, "a", opts); + // const dso = addSharedLibrary(b, opts, .{ .name = "a"}); // dso.addObject(a_o); // dso.link_z_nodlopen = true; - // const exe = addExecutable(b, "main", opts); + // const exe = addExecutable(b, opts, .{ .name = "main"}); // exe.addObject(b_o); // exe.linkLibrary(dso); @@ -2764,12 +2935,12 @@ fn testTlsGdToIe(b: *Build, opts: Options) *Step { // } // { - // const dso = addSharedLibrary(b, "a", opts); + // const dso = addSharedLibrary(b, opts, .{ .name = "a"}); // dso.addObject(a_o); // dso.link_relax = false; // dso.link_z_nodlopen = true; - // const exe = addExecutable(b, "main", opts); + // const exe = addExecutable(b, opts, .{ .name = "main"}); // exe.addObject(b_o); // exe.linkLibrary(dso); @@ -2784,7 +2955,7 @@ fn testTlsGdToIe(b: *Build, opts: Options) *Step { fn testTlsIe(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-ie", opts); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(dso, \\#include \\__attribute__((tls_model("initial-exec"))) static _Thread_local int foo; @@ -2799,8 +2970,9 @@ fn testTlsIe(b: *Build, opts: Options) *Step { , &.{}); dso.linkLibC(); - const main_o = addObject(b, "main", opts); - addCSourceBytes(main_o, + const main_o = addObject(b, opts, .{ + .name = "main", + .c_source_bytes = \\#include \\_Thread_local int baz; \\void set(); @@ -2812,13 +2984,14 @@ fn testTlsIe(b: *Build, opts: Options) *Step { \\ print(); \\ printf("%d\n", baz); \\} - , &.{}); + , + }); main_o.linkLibC(); const exp_stdout = "0 0 3 5 7\n"; { - const exe = addExecutable(b, "main1", opts); + const exe = addExecutable(b, opts, .{ .name = "main1" }); exe.addObject(main_o); exe.linkLibrary(dso); exe.linkLibC(); @@ -2831,7 +3004,7 @@ fn testTlsIe(b: *Build, opts: Options) *Step { } { - const exe = addExecutable(b, "main2", opts); + const exe = addExecutable(b, opts, .{ .name = "main2" }); exe.addObject(main_o); exe.linkLibrary(dso); exe.linkLibC(); @@ -2850,38 +3023,46 @@ fn testTlsIe(b: *Build, opts: Options) *Step { fn testTlsLargeAlignment(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-large-alignment", opts); - const a_o = addObject(b, "a", opts); - addCSourceBytes(a_o, + const a_o = addObject(b, opts, .{ + .name = "a", + .c_source_bytes = \\__attribute__((section(".tdata1"))) \\_Thread_local int x = 42; - , &.{"-std=c11"}); - a_o.force_pic = true; + , + .c_source_flags = &.{"-std=c11"}, + .pic = true, + }); - const b_o = addObject(b, "b", opts); - addCSourceBytes(b_o, + const b_o = addObject(b, opts, .{ + .name = "b", + .c_source_bytes = \\__attribute__((section(".tdata2"))) \\_Alignas(256) _Thread_local int y[] = { 1, 2, 3 }; - , &.{"-std=c11"}); - b_o.force_pic = true; + , + .c_source_flags = &.{"-std=c11"}, + .pic = true, + }); - const c_o = addObject(b, "c", opts); - addCSourceBytes(c_o, + const c_o = addObject(b, opts, .{ + .name = "c", + .c_source_bytes = \\#include \\extern _Thread_local int x; \\extern _Thread_local int y[]; \\int main() { \\ printf("%d %d %d %d\n", x, y[0], y[1], y[2]); \\} - , &.{}); - c_o.force_pic = true; + , + .pic = true, + }); c_o.linkLibC(); { - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); dso.addObject(a_o); dso.addObject(b_o); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); exe.addObject(c_o); exe.linkLibrary(dso); exe.linkLibC(); @@ -2894,7 +3075,7 @@ fn testTlsLargeAlignment(b: *Build, opts: Options) *Step { } { - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); exe.addObject(a_o); exe.addObject(b_o); exe.addObject(c_o); @@ -2913,7 +3094,7 @@ fn testTlsLargeAlignment(b: *Build, opts: Options) *Step { fn testTlsLargeTbss(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-large-tbss", opts); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addAsmSourceBytes(exe, \\.globl x, y \\.section .tbss,"awT",@nobits @@ -2947,7 +3128,7 @@ fn testTlsLargeTbss(b: *Build, opts: Options) *Step { fn testTlsLargeStaticImage(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-large-static-image", opts); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, "_Thread_local int x[] = { 1, 2, 3, [10000] = 5 };", &.{}); addCSourceBytes(exe, \\#include @@ -2956,7 +3137,7 @@ fn testTlsLargeStaticImage(b: *Build, opts: Options) *Step { \\ printf("%d %d %d %d %d\n", x[0], x[1], x[2], x[3], x[10000]); \\} , &.{}); - exe.force_pic = true; + exe.root_module.pic = true; exe.linkLibC(); // https://github.com/ziglang/zig/issues/17619 exe.pie = true; @@ -2971,8 +3152,9 @@ fn testTlsLargeStaticImage(b: *Build, opts: Options) *Step { fn testTlsLd(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-ld", opts); - const main_o = addObject(b, "main", opts); - addCSourceBytes(main_o, + const main_o = addObject(b, opts, .{ + .name = "main", + .c_source_bytes = \\#include \\extern _Thread_local int foo; \\static _Thread_local int bar; @@ -2983,18 +3165,23 @@ fn testTlsLd(b: *Build, opts: Options) *Step { \\ printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar); \\ return 0; \\} - , &.{"-ftls-model=local-dynamic"}); - main_o.force_pic = true; + , + .c_source_flags = &.{"-ftls-model=local-dynamic"}, + .pic = true, + }); main_o.linkLibC(); - const a_o = addObject(b, "a", opts); - addCSourceBytes(a_o, "_Thread_local int foo = 3;", &.{"-ftls-model=local-dynamic"}); - a_o.force_pic = true; + const a_o = addObject(b, opts, .{ + .name = "a", + .c_source_bytes = "_Thread_local int foo = 3;", + .c_source_flags = &.{"-ftls-model=local-dynamic"}, + .pic = true, + }); const exp_stdout = "3 5 3 5\n"; { - const exe = addExecutable(b, "main1", opts); + const exe = addExecutable(b, opts, .{ .name = "main1" }); exe.addObject(main_o); exe.addObject(a_o); exe.linkLibC(); @@ -3007,7 +3194,7 @@ fn testTlsLd(b: *Build, opts: Options) *Step { } { - const exe = addExecutable(b, "main2", opts); + const exe = addExecutable(b, opts, .{ .name = "main2" }); exe.addObject(main_o); exe.addObject(a_o); exe.linkLibC(); @@ -3026,14 +3213,14 @@ fn testTlsLd(b: *Build, opts: Options) *Step { fn testTlsLdDso(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-ld-dso", opts); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(dso, \\static _Thread_local int def, def1; \\int f0() { return ++def; } \\int f1() { return ++def1 + def; } , &.{"-ftls-model=local-dynamic"}); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\#include \\extern int f0(); @@ -3060,8 +3247,9 @@ fn testTlsLdDso(b: *Build, opts: Options) *Step { fn testTlsLdNoPlt(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-ld-no-plt", opts); - const a_o = addObject(b, "a", opts); - addCSourceBytes(a_o, + const a_o = addObject(b, opts, .{ + .name = "a", + .c_source_bytes = \\#include \\extern _Thread_local int foo; \\static _Thread_local int bar; @@ -3073,16 +3261,21 @@ fn testTlsLdNoPlt(b: *Build, opts: Options) *Step { \\ printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar); \\ return 0; \\} - , &.{ "-ftls-model=local-dynamic", "-fno-plt" }); + , + .c_source_flags = &.{ "-ftls-model=local-dynamic", "-fno-plt" }, + .pic = true, + }); a_o.linkLibC(); - a_o.force_pic = true; - const b_o = addObject(b, "b", opts); - addCSourceBytes(b_o, "_Thread_local int foo = 3;", &.{ "-ftls-model=local-dynamic", "-fno-plt" }); - b_o.force_pic = true; + const b_o = addObject(b, opts, .{ + .name = "b", + .c_source_bytes = "_Thread_local int foo = 3;", + .c_source_flags = &.{ "-ftls-model=local-dynamic", "-fno-plt" }, + .pic = true, + }); { - const exe = addExecutable(b, "main1", opts); + const exe = addExecutable(b, opts, .{ .name = "main1" }); exe.addObject(a_o); exe.addObject(b_o); exe.linkLibC(); @@ -3095,7 +3288,7 @@ fn testTlsLdNoPlt(b: *Build, opts: Options) *Step { } { - const exe = addExecutable(b, "main2", opts); + const exe = addExecutable(b, opts, .{ .name = "main2" }); exe.addObject(a_o); exe.addObject(b_o); exe.linkLibC(); @@ -3114,7 +3307,7 @@ fn testTlsLdNoPlt(b: *Build, opts: Options) *Step { fn testTlsNoPic(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-no-pic", opts); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\#include \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo; @@ -3132,7 +3325,7 @@ fn testTlsNoPic(b: *Build, opts: Options) *Step { addCSourceBytes(exe, \\__attribute__((tls_model("global-dynamic"))) _Thread_local int foo; , &.{}); - exe.force_pic = false; + exe.root_module.pic = false; exe.linkLibC(); const run = addRunArtifact(exe); @@ -3145,7 +3338,7 @@ fn testTlsNoPic(b: *Build, opts: Options) *Step { fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-offset-alignment", opts); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(dso, \\#include \\#include @@ -3163,7 +3356,7 @@ fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step { , &.{}); dso.linkLibC(); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\#include \\#include @@ -3186,7 +3379,7 @@ fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step { , &.{}); exe.addRPath(dso.getEmittedBinDirectory()); exe.linkLibC(); - exe.force_pic = true; + exe.root_module.pic = true; // https://github.com/ziglang/zig/issues/17619 exe.pie = true; @@ -3200,8 +3393,9 @@ fn testTlsOffsetAlignment(b: *Build, opts: Options) *Step { fn testTlsPic(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-pic", opts); - const obj = addObject(b, "obj", opts); - addCSourceBytes(obj, + const obj = addObject(b, opts, .{ + .name = "obj", + .c_source_bytes = \\#include \\__attribute__((tls_model("global-dynamic"))) extern _Thread_local int foo; \\__attribute__((tls_model("global-dynamic"))) static _Thread_local int bar; @@ -3213,11 +3407,12 @@ fn testTlsPic(b: *Build, opts: Options) *Step { \\ printf("%d %d %d %d\n", *get_foo_addr(), *get_bar_addr(), foo, bar); \\ return 0; \\} - , &.{}); + , + .pic = true, + }); obj.linkLibC(); - obj.force_pic = true; - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\__attribute__((tls_model("global-dynamic"))) _Thread_local int foo = 3; , &.{}); @@ -3236,30 +3431,38 @@ fn testTlsPic(b: *Build, opts: Options) *Step { fn testTlsSmallAlignment(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-small-alignment", opts); - const a_o = addObject(b, "a", opts); - addAsmSourceBytes(a_o, + const a_o = addObject(b, opts, .{ + .name = "a", + .asm_source_bytes = \\.text \\.byte 0 - ); - a_o.force_pic = true; + \\ + , + .pic = true, + }); - const b_o = addObject(b, "b", opts); - addCSourceBytes(b_o, "_Thread_local char x = 42;", &.{"-std=c11"}); - b_o.force_pic = true; + const b_o = addObject(b, opts, .{ + .name = "b", + .c_source_bytes = "_Thread_local char x = 42;", + .c_source_flags = &.{"-std=c11"}, + .pic = true, + }); - const c_o = addObject(b, "c", opts); - addCSourceBytes(c_o, + const c_o = addObject(b, opts, .{ + .name = "c", + .c_source_bytes = \\#include \\extern _Thread_local char x; \\int main() { \\ printf("%d\n", x); \\} - , &.{}); + , + .pic = true, + }); c_o.linkLibC(); - c_o.force_pic = true; { - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); exe.addObject(a_o); exe.addObject(b_o); exe.addObject(c_o); @@ -3273,11 +3476,11 @@ fn testTlsSmallAlignment(b: *Build, opts: Options) *Step { } { - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); dso.addObject(a_o); dso.addObject(b_o); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); exe.addObject(c_o); exe.linkLibrary(dso); exe.linkLibC(); @@ -3295,7 +3498,7 @@ fn testTlsSmallAlignment(b: *Build, opts: Options) *Step { fn testTlsStatic(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-static", opts); - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ .name = "test" }); addCSourceBytes(exe, \\#include \\_Thread_local int a = 10; @@ -3326,12 +3529,14 @@ fn testTlsStatic(b: *Build, opts: Options) *Step { fn testUnknownFileTypeError(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "unknown-file-type-error", opts); - const dylib = addSharedLibrary(b, "a", .{ - .target = .{ .cpu_arch = .x86_64, .os_tag = .macos }, + const dylib = addSharedLibrary(b, .{ + .target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .macos }), + }, .{ + .name = "a", + .zig_source_bytes = "export var foo: i32 = 0;", }); - addZigSourceBytes(dylib, "export var foo: i32 = 0;"); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\extern int foo; \\int main() { @@ -3354,28 +3559,34 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step { fn testUnresolvedError(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "unresolved-error", opts); - const obj1 = addObject(b, "a", opts); - addCSourceBytes(obj1, + const obj1 = addObject(b, opts, .{ + .name = "a", + .c_source_bytes = \\#include \\int foo(); \\int bar() { \\ return foo() + 1; \\} - , &.{"-ffunction-sections"}); + , + .c_source_flags = &.{"-ffunction-sections"}, + }); obj1.linkLibC(); - const obj2 = addObject(b, "b", opts); - addCSourceBytes(obj2, + const obj2 = addObject(b, opts, .{ + .name = "b", + .c_source_bytes = \\#include \\int foo(); \\int bar(); \\int main() { \\ return foo() + bar(); \\} - , &.{"-ffunction-sections"}); + , + .c_source_flags = &.{"-ffunction-sections"}, + }); obj2.linkLibC(); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); exe.addObject(obj1); exe.addObject(obj2); exe.linkLibC(); @@ -3392,19 +3603,21 @@ fn testUnresolvedError(b: *Build, opts: Options) *Step { fn testWeakExports(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "weak-exports", opts); - const obj = addObject(b, "obj", opts); - addCSourceBytes(obj, + const obj = addObject(b, opts, .{ + .name = "obj", + .c_source_bytes = \\#include \\__attribute__((weak)) int foo(); \\int main() { \\ printf("%d\n", foo ? foo() : 3); \\} - , &.{}); + , + .pic = true, + }); obj.linkLibC(); - obj.force_pic = true; { - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); dso.addObject(obj); dso.linkLibC(); @@ -3415,7 +3628,7 @@ fn testWeakExports(b: *Build, opts: Options) *Step { } { - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); exe.addObject(obj); exe.linkLibC(); // https://github.com/ziglang/zig/issues/17619 @@ -3437,14 +3650,14 @@ fn testWeakExports(b: *Build, opts: Options) *Step { fn testWeakUndefsDso(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "weak-undef-dso", opts); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(dso, \\__attribute__((weak)) int foo(); \\int bar() { return foo ? foo() : -1; } , &.{}); { - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\#include \\int bar(); @@ -3461,7 +3674,7 @@ fn testWeakUndefsDso(b: *Build, opts: Options) *Step { } { - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\#include \\int foo() { return 5; } @@ -3484,12 +3697,14 @@ fn testWeakUndefsDso(b: *Build, opts: Options) *Step { fn testZNow(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "z-now", opts); - const obj = addObject(b, "obj", opts); - addCSourceBytes(obj, "int main() { return 0; }", &.{}); - obj.force_pic = true; + const obj = addObject(b, opts, .{ + .name = "obj", + .c_source_bytes = "int main() { return 0; }", + .pic = true, + }); { - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); dso.addObject(obj); const check = dso.checkObject(); @@ -3499,7 +3714,7 @@ fn testZNow(b: *Build, opts: Options) *Step { } { - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); dso.addObject(obj); dso.link_z_lazy = true; @@ -3515,7 +3730,7 @@ fn testZNow(b: *Build, opts: Options) *Step { fn testZStackSize(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "z-stack-size", opts); - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, "int main() { return 0; }", &.{}); exe.stack_size = 0x800000; exe.linkLibC(); @@ -3540,8 +3755,9 @@ fn testZText(b: *Build, opts: Options) *Step { // musl supports only a very limited number of text relocations and only in DSOs (and // rightly so!). - const a_o = addObject(b, "a", opts); - addAsmSourceBytes(a_o, + const a_o = addObject(b, opts, .{ + .name = "a", + .asm_source_bytes = \\.globl fn1 \\fn1: \\ sub $8, %rsp @@ -3549,10 +3765,13 @@ fn testZText(b: *Build, opts: Options) *Step { \\ call *%rax \\ add $8, %rsp \\ ret - ); + \\ + , + }); - const b_o = addObject(b, "b", opts); - addCSourceBytes(b_o, + const b_o = addObject(b, opts, .{ + .name = "b", + .c_source_bytes = \\int fn1(); \\int fn2() { \\ return 3; @@ -3561,15 +3780,16 @@ fn testZText(b: *Build, opts: Options) *Step { \\int fnn() { \\ return fn1(); \\} - , &.{}); - b_o.force_pic = true; + , + .pic = true, + }); - const dso = addSharedLibrary(b, "a", opts); + const dso = addSharedLibrary(b, opts, .{ .name = "a" }); dso.addObject(a_o); dso.addObject(b_o); dso.link_z_notext = true; - const exe = addExecutable(b, "main", opts); + const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, \\#include \\int fnn(); @@ -3608,13 +3828,11 @@ const addObject = link.addObject; const addRunArtifact = link.addRunArtifact; const addSharedLibrary = link.addSharedLibrary; const addStaticLibrary = link.addStaticLibrary; -const addZigSourceBytes = link.addZigSourceBytes; const expectLinkErrors = link.expectLinkErrors; const link = @import("link.zig"); const std = @import("std"); const Build = std.Build; -const CrossTarget = std.zig.CrossTarget; const Options = link.Options; const Step = Build.Step; const WriteFile = Step.WriteFile; diff --git a/test/link/glibc_compat/build.zig b/test/link/glibc_compat/build.zig index a104633fd764..bb8d5d056daf 100644 --- a/test/link/glibc_compat/build.zig +++ b/test/link/glibc_compat/build.zig @@ -7,11 +7,11 @@ pub fn build(b: *std.Build) void { for ([_][]const u8{ "aarch64-linux-gnu.2.27", "aarch64-linux-gnu.2.34" }) |t| { const exe = b.addExecutable(.{ .name = t, - .root_source_file = .{ .path = "main.c" }, - .target = std.zig.CrossTarget.parse( + .target = b.resolveTargetQuery(std.Target.Query.parse( .{ .arch_os_abi = t }, - ) catch unreachable, + ) catch unreachable), }); + exe.addCSourceFile(.{ .file = .{ .path = "main.c" } }); exe.linkLibC(); // TODO: actually test the output _ = exe.getEmittedBin(); diff --git a/test/link/interdependent_static_c_libs/build.zig b/test/link/interdependent_static_c_libs/build.zig index 757c0d419b63..01ed218cce3f 100644 --- a/test/link/interdependent_static_c_libs/build.zig +++ b/test/link/interdependent_static_c_libs/build.zig @@ -14,7 +14,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const lib_a = b.addStaticLibrary(.{ .name = "a", .optimize = optimize, - .target = .{}, + .target = b.host, }); lib_a.addCSourceFile(.{ .file = .{ .path = "a.c" }, .flags = &[_][]const u8{} }); lib_a.addIncludePath(.{ .path = "." }); @@ -22,7 +22,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const lib_b = b.addStaticLibrary(.{ .name = "b", .optimize = optimize, - .target = .{}, + .target = b.host, }); lib_b.addCSourceFile(.{ .file = .{ .path = "b.c" }, .flags = &[_][]const u8{} }); lib_b.addIncludePath(.{ .path = "." }); diff --git a/test/link/link.zig b/test/link/link.zig index b08a929d8b30..ccce03bb0b4f 100644 --- a/test/link/link.zig +++ b/test/link/link.zig @@ -7,62 +7,160 @@ pub fn build(b: *Build) void { } pub const Options = struct { - target: CrossTarget, + target: std.Build.ResolvedTarget, optimize: std.builtin.OptimizeMode = .Debug, use_llvm: bool = true, use_lld: bool = false, }; -pub fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step { - const target = opts.target.zigTriple(b.allocator) catch @panic("OOM"); +pub fn addTestStep(b: *Build, prefix: []const u8, opts: Options) *Step { + const target = opts.target.result.zigTriple(b.allocator) catch @panic("OOM"); const optimize = @tagName(opts.optimize); const use_llvm = if (opts.use_llvm) "llvm" else "no-llvm"; - const name = std.fmt.allocPrint(b.allocator, "test-" ++ prefix ++ "-{s}-{s}-{s}", .{ - target, - optimize, - use_llvm, + const name = std.fmt.allocPrint(b.allocator, "test-{s}-{s}-{s}-{s}", .{ + prefix, target, optimize, use_llvm, }) catch @panic("OOM"); return b.step(name, ""); } -pub fn addExecutable(b: *Build, name: []const u8, opts: Options) *Compile { - return b.addExecutable(.{ - .name = name, - .target = opts.target, - .optimize = opts.optimize, - .use_llvm = opts.use_llvm, - .use_lld = opts.use_lld, +const OverlayOptions = struct { + name: []const u8, + asm_source_bytes: ?[]const u8 = null, + c_source_bytes: ?[]const u8 = null, + c_source_flags: []const []const u8 = &.{}, + cpp_source_bytes: ?[]const u8 = null, + cpp_source_flags: []const []const u8 = &.{}, + zig_source_bytes: ?[]const u8 = null, + pic: ?bool = null, + strip: ?bool = null, +}; + +pub fn addExecutable(b: *std.Build, base: Options, overlay: OverlayOptions) *Step.Compile { + const compile_step = b.addExecutable(.{ + .name = overlay.name, + .root_source_file = rsf: { + const bytes = overlay.zig_source_bytes orelse break :rsf null; + break :rsf b.addWriteFiles().add("a.zig", bytes); + }, + .target = base.target, + .optimize = base.optimize, + .use_llvm = base.use_llvm, + .use_lld = base.use_lld, + .pic = overlay.pic, + .strip = overlay.strip, }); + if (overlay.cpp_source_bytes) |bytes| { + compile_step.addCSourceFile(.{ + .file = b.addWriteFiles().add("a.cpp", bytes), + .flags = overlay.cpp_source_flags, + }); + } + if (overlay.c_source_bytes) |bytes| { + compile_step.addCSourceFile(.{ + .file = b.addWriteFiles().add("a.c", bytes), + .flags = overlay.c_source_flags, + }); + } + if (overlay.asm_source_bytes) |bytes| { + compile_step.addAssemblyFile(b.addWriteFiles().add("a.s", bytes)); + } + return compile_step; } -pub fn addObject(b: *Build, name: []const u8, opts: Options) *Compile { - return b.addObject(.{ - .name = name, - .target = opts.target, - .optimize = opts.optimize, - .use_llvm = opts.use_llvm, - .use_lld = opts.use_lld, +pub fn addObject(b: *Build, base: Options, overlay: OverlayOptions) *Step.Compile { + const compile_step = b.addObject(.{ + .name = overlay.name, + .root_source_file = rsf: { + const bytes = overlay.zig_source_bytes orelse break :rsf null; + break :rsf b.addWriteFiles().add("a.zig", bytes); + }, + .target = base.target, + .optimize = base.optimize, + .use_llvm = base.use_llvm, + .use_lld = base.use_lld, + .pic = overlay.pic, + .strip = overlay.strip, }); + if (overlay.cpp_source_bytes) |bytes| { + compile_step.addCSourceFile(.{ + .file = b.addWriteFiles().add("a.cpp", bytes), + .flags = overlay.cpp_source_flags, + }); + } + if (overlay.c_source_bytes) |bytes| { + compile_step.addCSourceFile(.{ + .file = b.addWriteFiles().add("a.c", bytes), + .flags = overlay.c_source_flags, + }); + } + if (overlay.asm_source_bytes) |bytes| { + compile_step.addAssemblyFile(b.addWriteFiles().add("a.s", bytes)); + } + return compile_step; } -pub fn addStaticLibrary(b: *Build, name: []const u8, opts: Options) *Compile { - return b.addStaticLibrary(.{ - .name = name, - .target = opts.target, - .optimize = opts.optimize, - .use_llvm = opts.use_llvm, - .use_lld = opts.use_lld, +pub fn addStaticLibrary(b: *Build, base: Options, overlay: OverlayOptions) *Compile { + const compile_step = b.addStaticLibrary(.{ + .name = overlay.name, + .root_source_file = rsf: { + const bytes = overlay.zig_source_bytes orelse break :rsf null; + break :rsf b.addWriteFiles().add("a.zig", bytes); + }, + .target = base.target, + .optimize = base.optimize, + .use_llvm = base.use_llvm, + .use_lld = base.use_lld, + .pic = overlay.pic, + .strip = overlay.strip, }); + if (overlay.cpp_source_bytes) |bytes| { + compile_step.addCSourceFile(.{ + .file = b.addWriteFiles().add("a.cpp", bytes), + .flags = overlay.cpp_source_flags, + }); + } + if (overlay.c_source_bytes) |bytes| { + compile_step.addCSourceFile(.{ + .file = b.addWriteFiles().add("a.c", bytes), + .flags = overlay.c_source_flags, + }); + } + if (overlay.asm_source_bytes) |bytes| { + compile_step.addAssemblyFile(b.addWriteFiles().add("a.s", bytes)); + } + return compile_step; } -pub fn addSharedLibrary(b: *Build, name: []const u8, opts: Options) *Compile { - return b.addSharedLibrary(.{ - .name = name, - .target = opts.target, - .optimize = opts.optimize, - .use_llvm = opts.use_llvm, - .use_lld = opts.use_lld, +pub fn addSharedLibrary(b: *Build, base: Options, overlay: OverlayOptions) *Compile { + const compile_step = b.addSharedLibrary(.{ + .name = overlay.name, + .root_source_file = rsf: { + const bytes = overlay.zig_source_bytes orelse break :rsf null; + break :rsf b.addWriteFiles().add("a.zig", bytes); + }, + .target = base.target, + .optimize = base.optimize, + .use_llvm = base.use_llvm, + .use_lld = base.use_lld, + .pic = overlay.pic, + .strip = overlay.strip, }); + if (overlay.cpp_source_bytes) |bytes| { + compile_step.addCSourceFile(.{ + .file = b.addWriteFiles().add("a.cpp", bytes), + .flags = overlay.cpp_source_flags, + }); + } + if (overlay.c_source_bytes) |bytes| { + compile_step.addCSourceFile(.{ + .file = b.addWriteFiles().add("a.c", bytes), + .flags = overlay.c_source_flags, + }); + } + if (overlay.asm_source_bytes) |bytes| { + compile_step.addAssemblyFile(b.addWriteFiles().add("a.s", bytes)); + } + return compile_step; } pub fn addRunArtifact(comp: *Compile) *Run { @@ -72,13 +170,6 @@ pub fn addRunArtifact(comp: *Compile) *Run { return run; } -pub fn addZigSourceBytes(comp: *Compile, bytes: []const u8) void { - const b = comp.step.owner; - const file = WriteFile.create(b).add("a.zig", bytes); - file.addStepDependencies(&comp.step); - comp.root_src = file; -} - pub fn addCSourceBytes(comp: *Compile, bytes: []const u8, flags: []const []const u8) void { const b = comp.step.owner; const file = WriteFile.create(b).add("a.c", bytes); @@ -108,7 +199,6 @@ const std = @import("std"); const Build = std.Build; const Compile = Step.Compile; -const CrossTarget = std.zig.CrossTarget; const Run = Step.Run; const Step = Build.Step; const WriteFile = Step.WriteFile; diff --git a/test/link/macho.zig b/test/link/macho.zig index f172d9af17a2..8a5016f9b7d7 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -1,26 +1,29 @@ //! Here we test our MachO linker for correctness and functionality. //! TODO migrate standalone tests from test/link/macho/* to here. -pub fn testAll(b: *Build) *Step { +pub fn testAll(b: *std.Build) *Step { const macho_step = b.step("test-macho", "Run MachO tests"); - const default_target = CrossTarget{ .os_tag = .macos }; - - macho_step.dependOn(testResolvingBoundarySymbols(b, .{ .target = default_target })); + macho_step.dependOn(testResolvingBoundarySymbols(b, .{ + .target = b.resolveTargetQuery(.{ .os_tag = .macos }), + })); return macho_step; } -fn testResolvingBoundarySymbols(b: *Build, opts: Options) *Step { +fn testResolvingBoundarySymbols(b: *std.Build, opts: Options) *Step { const test_step = addTestStep(b, "macho-resolving-boundary-symbols", opts); - const obj1 = addObject(b, "obj1", opts); - addCppSourceBytes(obj1, + const obj1 = addObject(b, opts, .{ + .name = "obj1", + .cpp_source_bytes = \\constexpr const char* MESSAGE __attribute__((used, section("__DATA_CONST,__message_ptr"))) = "codebase"; - , &.{}); + , + }); - const main_o = addObject(b, "main", opts); - addZigSourceBytes(main_o, + const main_o = addObject(b, opts, .{ + .name = "main", + .zig_source_bytes = \\const std = @import("std"); \\extern fn interop() [*:0]const u8; \\pub fn main() !void { @@ -28,23 +31,27 @@ fn testResolvingBoundarySymbols(b: *Build, opts: Options) *Step { \\ std.mem.span(interop()), \\ }); \\} - ); + , + }); { - const obj2 = addObject(b, "obj2", opts); - addCppSourceBytes(obj2, + const obj2 = addObject(b, opts, .{ + .name = "obj2", + .cpp_source_bytes = \\extern const char* message_pointer __asm("section$start$__DATA_CONST$__message_ptr"); \\extern "C" const char* interop() { \\ return message_pointer; \\} - , &.{}); + , + }); - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ .name = "test" }); exe.addObject(obj1); exe.addObject(obj2); exe.addObject(main_o); - const run = addRunArtifact(exe); + const run = b.addRunArtifact(exe); + run.skip_foreign_checks = true; run.expectStdErrEqual("All your codebase are belong to us.\n"); test_step.dependOn(&run.step); @@ -55,15 +62,17 @@ fn testResolvingBoundarySymbols(b: *Build, opts: Options) *Step { } { - const obj3 = addObject(b, "obj3", opts); - addCppSourceBytes(obj3, + const obj3 = addObject(b, opts, .{ + .name = "obj3", + .cpp_source_bytes = \\extern const char* message_pointer __asm("section$start$__DATA$__message_ptr"); \\extern "C" const char* interop() { \\ return message_pointer; \\} - , &.{}); + , + }); - const exe = addExecutable(b, "test", opts); + const exe = addExecutable(b, opts, .{ .name = "test" }); exe.addObject(obj1); exe.addObject(obj3); exe.addObject(main_o); @@ -77,20 +86,14 @@ fn testResolvingBoundarySymbols(b: *Build, opts: Options) *Step { return test_step; } -fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step { +fn addTestStep(b: *std.Build, comptime prefix: []const u8, opts: Options) *Step { return link.addTestStep(b, "macho-" ++ prefix, opts); } -const addCppSourceBytes = link.addCppSourceBytes; -const addExecutable = link.addExecutable; const addObject = link.addObject; -const addRunArtifact = link.addRunArtifact; -const addZigSourceBytes = link.addZigSourceBytes; +const addExecutable = link.addExecutable; const expectLinkErrors = link.expectLinkErrors; const link = @import("link.zig"); const std = @import("std"); - -const Build = std.Build; -const CrossTarget = std.zig.CrossTarget; const Options = link.Options; -const Step = Build.Step; +const Step = std.Build.Step; diff --git a/test/link/macho/bugs/13056/build.zig b/test/link/macho/bugs/13056/build.zig index 5b19daab3cfd..53bcefb9308b 100644 --- a/test/link/macho/bugs/13056/build.zig +++ b/test/link/macho/bugs/13056/build.zig @@ -14,14 +14,14 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; - const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable; - const sdk = std.zig.system.darwin.getSdk(b.allocator, target_info.target) orelse + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); + const sdk = std.zig.system.darwin.getSdk(b.allocator, target.result) orelse @panic("macOS SDK is required to run the test"); const exe = b.addExecutable(.{ .name = "test", .optimize = optimize, + .target = b.host, }); exe.addSystemIncludePath(.{ .path = b.pathJoin(&.{ sdk, "/usr/include" }) }); exe.addIncludePath(.{ .path = b.pathJoin(&.{ sdk, "/usr/include/c++/v1" }) }); diff --git a/test/link/macho/bugs/13457/build.zig b/test/link/macho/bugs/13457/build.zig index 89096bba387d..fe835b5715a0 100644 --- a/test/link/macho/bugs/13457/build.zig +++ b/test/link/macho/bugs/13457/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); const exe = b.addExecutable(.{ .name = "test", diff --git a/test/link/macho/bugs/16308/build.zig b/test/link/macho/bugs/16308/build.zig index 3d7a884bd028..f4456b111cc8 100644 --- a/test/link/macho/bugs/16308/build.zig +++ b/test/link/macho/bugs/16308/build.zig @@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void { const test_step = b.step("test", "Test it"); b.default_step = test_step; - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); const lib = b.addSharedLibrary(.{ .name = "a", diff --git a/test/link/macho/bugs/16628/build.zig b/test/link/macho/bugs/16628/build.zig index aa396f7a3bc5..59c666aa27ec 100644 --- a/test/link/macho/bugs/16628/build.zig +++ b/test/link/macho/bugs/16628/build.zig @@ -15,7 +15,7 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); const exe = b.addExecutable(.{ .name = "test", diff --git a/test/link/macho/dead_strip/build.zig b/test/link/macho/dead_strip/build.zig index 8b8449b6b214..a5bb28df9f57 100644 --- a/test/link/macho/dead_strip/build.zig +++ b/test/link/macho/dead_strip/build.zig @@ -4,7 +4,7 @@ pub const requires_symlinks = true; pub fn build(b: *std.Build) void { const optimize: std.builtin.OptimizeMode = .Debug; - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); const test_step = b.step("test", "Test the program"); b.default_step = test_step; @@ -44,7 +44,7 @@ pub fn build(b: *std.Build) void { fn createScenario( b: *std.Build, optimize: std.builtin.OptimizeMode, - target: std.zig.CrossTarget, + target: std.Build.ResolvedTarget, name: []const u8, ) *std.Build.Step.Compile { const exe = b.addExecutable(.{ diff --git a/test/link/macho/dead_strip_dylibs/build.zig b/test/link/macho/dead_strip_dylibs/build.zig index 52d7f2eac8a3..7ab8c16dfeba 100644 --- a/test/link/macho/dead_strip_dylibs/build.zig +++ b/test/link/macho/dead_strip_dylibs/build.zig @@ -52,6 +52,7 @@ fn createScenario( const exe = b.addExecutable(.{ .name = name, .optimize = optimize, + .target = b.host, }); exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &[0][]const u8{} }); exe.linkLibC(); diff --git a/test/link/macho/dylib/build.zig b/test/link/macho/dylib/build.zig index beebfbdd021c..abd7175eae40 100644 --- a/test/link/macho/dylib/build.zig +++ b/test/link/macho/dylib/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); const dylib = b.addSharedLibrary(.{ .name = "a", @@ -55,7 +55,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize check_exe.checkInHeaders(); check_exe.checkExact("cmd RPATH"); - check_exe.checkExactPath("path", dylib.getOutputDirectorySource()); + check_exe.checkExactPath("path", dylib.getEmittedBinDirectory()); test_step.dependOn(&check_exe.step); const run = b.addRunArtifact(exe); diff --git a/test/link/macho/empty/build.zig b/test/link/macho/empty/build.zig index 6a1350770271..af38930ae88d 100644 --- a/test/link/macho/empty/build.zig +++ b/test/link/macho/empty/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); const exe = b.addExecutable(.{ .name = "test", diff --git a/test/link/macho/entry/build.zig b/test/link/macho/entry/build.zig index 9675935a9c3c..0ef717f2927e 100644 --- a/test/link/macho/entry/build.zig +++ b/test/link/macho/entry/build.zig @@ -16,7 +16,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const exe = b.addExecutable(.{ .name = "main", .optimize = optimize, - .target = .{ .os_tag = .macos }, + .target = b.resolveTargetQuery(.{ .os_tag = .macos }), }); exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} }); exe.linkLibC(); diff --git a/test/link/macho/entry_in_archive/build.zig b/test/link/macho/entry_in_archive/build.zig index f48e612d95d4..72f340b204bf 100644 --- a/test/link/macho/entry_in_archive/build.zig +++ b/test/link/macho/entry_in_archive/build.zig @@ -16,7 +16,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const lib = b.addStaticLibrary(.{ .name = "main", .optimize = optimize, - .target = .{ .os_tag = .macos }, + .target = b.resolveTargetQuery(.{ .os_tag = .macos }), }); lib.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} }); lib.linkLibC(); @@ -24,7 +24,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const exe = b.addExecutable(.{ .name = "main", .optimize = optimize, - .target = .{ .os_tag = .macos }, + .target = b.resolveTargetQuery(.{ .os_tag = .macos }), }); exe.linkLibrary(lib); exe.linkLibC(); diff --git a/test/link/macho/entry_in_dylib/build.zig b/test/link/macho/entry_in_dylib/build.zig index 5b93541a5a0b..7827552bcfdc 100644 --- a/test/link/macho/entry_in_dylib/build.zig +++ b/test/link/macho/entry_in_dylib/build.zig @@ -16,7 +16,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const lib = b.addSharedLibrary(.{ .name = "bootstrap", .optimize = optimize, - .target = .{ .os_tag = .macos }, + .target = b.resolveTargetQuery(.{ .os_tag = .macos }), }); lib.addCSourceFile(.{ .file = .{ .path = "bootstrap.c" }, .flags = &.{} }); lib.linkLibC(); @@ -25,7 +25,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const exe = b.addExecutable(.{ .name = "main", .optimize = optimize, - .target = .{ .os_tag = .macos }, + .target = b.resolveTargetQuery(.{ .os_tag = .macos }), }); exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} }); exe.linkLibrary(lib); diff --git a/test/link/macho/headerpad/build.zig b/test/link/macho/headerpad/build.zig index 3c0bb7c5725c..b982224e8566 100644 --- a/test/link/macho/headerpad/build.zig +++ b/test/link/macho/headerpad/build.zig @@ -112,6 +112,7 @@ fn simpleExe( const exe = b.addExecutable(.{ .name = name, .optimize = optimize, + .target = b.host, }); exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &.{} }); exe.linkLibC(); diff --git a/test/link/macho/linksection/build.zig b/test/link/macho/linksection/build.zig index 113034f96f9f..91ddfe530c5a 100644 --- a/test/link/macho/linksection/build.zig +++ b/test/link/macho/linksection/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const target = std.zig.CrossTarget{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); const obj = b.addObject(.{ .name = "test", diff --git a/test/link/macho/needed_framework/build.zig b/test/link/macho/needed_framework/build.zig index 45c046fe0bf5..83a3e75e2db4 100644 --- a/test/link/macho/needed_framework/build.zig +++ b/test/link/macho/needed_framework/build.zig @@ -19,6 +19,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const exe = b.addExecutable(.{ .name = "test", .optimize = optimize, + .target = b.host, }); exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &[0][]const u8{} }); exe.linkLibC(); diff --git a/test/link/macho/needed_library/build.zig b/test/link/macho/needed_library/build.zig index cc9d2bd6465f..a07493a8b155 100644 --- a/test/link/macho/needed_library/build.zig +++ b/test/link/macho/needed_library/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); const dylib = b.addSharedLibrary(.{ .name = "a", @@ -33,7 +33,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize }); exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &[0][]const u8{} }); exe.linkLibC(); - exe.linkSystemLibraryNeeded("a"); + exe.root_module.linkSystemLibrary("a", .{ .needed = true }); exe.addLibraryPath(dylib.getEmittedBinDirectory()); exe.addRPath(dylib.getEmittedBinDirectory()); exe.dead_strip_dylibs = true; diff --git a/test/link/macho/objc/build.zig b/test/link/macho/objc/build.zig index 979c0aa94327..26bbdc267364 100644 --- a/test/link/macho/objc/build.zig +++ b/test/link/macho/objc/build.zig @@ -17,6 +17,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const exe = b.addExecutable(.{ .name = "test", .optimize = optimize, + .target = b.host, }); exe.addIncludePath(.{ .path = "." }); exe.addCSourceFile(.{ .file = .{ .path = "Foo.m" }, .flags = &[0][]const u8{} }); diff --git a/test/link/macho/objcpp/build.zig b/test/link/macho/objcpp/build.zig index 183d3cb9d3ef..53743fafdfd5 100644 --- a/test/link/macho/objcpp/build.zig +++ b/test/link/macho/objcpp/build.zig @@ -17,6 +17,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const exe = b.addExecutable(.{ .name = "test", .optimize = optimize, + .target = b.host, }); b.default_step.dependOn(&exe.step); exe.addIncludePath(.{ .path = "." }); diff --git a/test/link/macho/pagezero/build.zig b/test/link/macho/pagezero/build.zig index 1ca7f28f1108..cee2aa9fd506 100644 --- a/test/link/macho/pagezero/build.zig +++ b/test/link/macho/pagezero/build.zig @@ -7,7 +7,7 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const optimize: std.builtin.OptimizeMode = .Debug; - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); { const exe = b.addExecutable(.{ diff --git a/test/link/macho/reexports/build.zig b/test/link/macho/reexports/build.zig index d9a9481fa0be..44c96ecf7eba 100644 --- a/test/link/macho/reexports/build.zig +++ b/test/link/macho/reexports/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); const lib = b.addStaticLibrary(.{ .name = "a", diff --git a/test/link/macho/search_strategy/build.zig b/test/link/macho/search_strategy/build.zig index b76de65b0eac..181d2df91ab4 100644 --- a/test/link/macho/search_strategy/build.zig +++ b/test/link/macho/search_strategy/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); { // -search_dylibs_first @@ -45,9 +45,9 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize fn createScenario( b: *std.Build, optimize: std.builtin.OptimizeMode, - target: std.zig.CrossTarget, + target: std.Build.ResolvedTarget, name: []const u8, - search_strategy: std.Build.Step.Compile.SystemLib.SearchStrategy, + search_strategy: std.Build.Module.SystemLib.SearchStrategy, ) *std.Build.Step.Compile { const static = b.addStaticLibrary(.{ .name = name, diff --git a/test/link/macho/stack_size/build.zig b/test/link/macho/stack_size/build.zig index 48496bd9d51e..28a2602ea5e3 100644 --- a/test/link/macho/stack_size/build.zig +++ b/test/link/macho/stack_size/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); const exe = b.addExecutable(.{ .name = "main", diff --git a/test/link/macho/strict_validation/build.zig b/test/link/macho/strict_validation/build.zig index 1cfaac49103f..f35438369c90 100644 --- a/test/link/macho/strict_validation/build.zig +++ b/test/link/macho/strict_validation/build.zig @@ -14,7 +14,7 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); const exe = b.addExecutable(.{ .name = "main", diff --git a/test/link/macho/tbdv3/build.zig b/test/link/macho/tbdv3/build.zig index ba11004f29b9..547f72c25fc3 100644 --- a/test/link/macho/tbdv3/build.zig +++ b/test/link/macho/tbdv3/build.zig @@ -15,7 +15,7 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); const lib = b.addSharedLibrary(.{ .name = "a", diff --git a/test/link/macho/tls/build.zig b/test/link/macho/tls/build.zig index 443f698ea6be..af2ac8a9c630 100644 --- a/test/link/macho/tls/build.zig +++ b/test/link/macho/tls/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); const lib = b.addSharedLibrary(.{ .name = "a", diff --git a/test/link/macho/unwind_info/build.zig b/test/link/macho/unwind_info/build.zig index 96c4a67568bf..534cc4e51a1d 100644 --- a/test/link/macho/unwind_info/build.zig +++ b/test/link/macho/unwind_info/build.zig @@ -14,7 +14,7 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); testUnwindInfo(b, test_step, optimize, target, false, "no-dead-strip"); testUnwindInfo(b, test_step, optimize, target, true, "yes-dead-strip"); @@ -24,7 +24,7 @@ fn testUnwindInfo( b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode, - target: std.zig.CrossTarget, + target: std.Build.ResolvedTarget, dead_strip: bool, name: []const u8, ) void { @@ -66,7 +66,7 @@ fn testUnwindInfo( fn createScenario( b: *std.Build, optimize: std.builtin.OptimizeMode, - target: std.zig.CrossTarget, + target: std.Build.ResolvedTarget, name: []const u8, ) *std.Build.Step.Compile { const exe = b.addExecutable(.{ diff --git a/test/link/macho/weak_framework/build.zig b/test/link/macho/weak_framework/build.zig index dfb41b7457e7..2a430443d455 100644 --- a/test/link/macho/weak_framework/build.zig +++ b/test/link/macho/weak_framework/build.zig @@ -17,6 +17,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const exe = b.addExecutable(.{ .name = "test", .optimize = optimize, + .target = b.host, }); exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &[0][]const u8{} }); exe.linkLibC(); diff --git a/test/link/macho/weak_library/build.zig b/test/link/macho/weak_library/build.zig index 7ae7c7c2d652..0d73b9f4eaa4 100644 --- a/test/link/macho/weak_library/build.zig +++ b/test/link/macho/weak_library/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { } fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { - const target: std.zig.CrossTarget = .{ .os_tag = .macos }; + const target = b.resolveTargetQuery(.{ .os_tag = .macos }); const dylib = b.addSharedLibrary(.{ .name = "a", @@ -32,7 +32,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize }); exe.addCSourceFile(.{ .file = .{ .path = "main.c" }, .flags = &[0][]const u8{} }); exe.linkLibC(); - exe.linkSystemLibraryWeak("a"); + exe.root_module.linkSystemLibrary("a", .{ .weak = true }); exe.addLibraryPath(dylib.getEmittedBinDirectory()); exe.addRPath(dylib.getEmittedBinDirectory()); diff --git a/test/link/static_libs_from_object_files/build.zig b/test/link/static_libs_from_object_files/build.zig index e5a4b79acd8f..d0ea78bbd66c 100644 --- a/test/link/static_libs_from_object_files/build.zig +++ b/test/link/static_libs_from_object_files/build.zig @@ -59,7 +59,7 @@ fn add(b: *Build, test_step: *Step, files: []const LazyPath, optimize: std.built .name = "test1", .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, - .target = .{}, + .target = b.host, }); for (files) |file| { @@ -77,12 +77,12 @@ fn add(b: *Build, test_step: *Step, files: []const LazyPath, optimize: std.built { const lib_a = b.addStaticLibrary(.{ .name = "test2_a", - .target = .{}, + .target = b.host, .optimize = optimize, }); const lib_b = b.addStaticLibrary(.{ .name = "test2_b", - .target = .{}, + .target = b.host, .optimize = optimize, }); @@ -94,6 +94,7 @@ fn add(b: *Build, test_step: *Step, files: []const LazyPath, optimize: std.built const exe = b.addExecutable(.{ .name = "test2", .root_source_file = .{ .path = "main.zig" }, + .target = b.host, .optimize = optimize, }); exe.linkLibrary(lib_a); @@ -110,19 +111,19 @@ fn add(b: *Build, test_step: *Step, files: []const LazyPath, optimize: std.built { const lib_a = b.addStaticLibrary(.{ .name = "test3_a", - .target = .{}, + .target = b.host, .optimize = optimize, }); const lib_b = b.addStaticLibrary(.{ .name = "test3_b", - .target = .{}, + .target = b.host, .optimize = optimize, }); for (files, 1..) |file, i| { const obj = b.addObject(.{ .name = b.fmt("obj_{}", .{i}), - .target = .{}, + .target = b.host, .optimize = optimize, }); obj.addCSourceFile(.{ .file = file, .flags = &flags }); @@ -134,6 +135,7 @@ fn add(b: *Build, test_step: *Step, files: []const LazyPath, optimize: std.built const exe = b.addExecutable(.{ .name = "test3", .root_source_file = .{ .path = "main.zig" }, + .target = b.host, .optimize = optimize, }); exe.linkLibrary(lib_a); diff --git a/test/link/wasm/archive/build.zig b/test/link/wasm/archive/build.zig index 95aea2913e9f..1d5e031848b9 100644 --- a/test/link/wasm/archive/build.zig +++ b/test/link/wasm/archive/build.zig @@ -20,11 +20,11 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, + .strip = false, }); lib.entry = .disabled; lib.use_llvm = false; lib.use_lld = false; - lib.strip = false; const check = lib.checkObject(); check.checkInHeaders(); diff --git a/test/link/wasm/basic-features/build.zig b/test/link/wasm/basic-features/build.zig index 9cf28ea31ef8..6f9e82d09df3 100644 --- a/test/link/wasm/basic-features/build.zig +++ b/test/link/wasm/basic-features/build.zig @@ -8,12 +8,12 @@ pub fn build(b: *std.Build) void { .name = "lib", .root_source_file = .{ .path = "main.zig" }, .optimize = .Debug, - .target = .{ + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, .cpu_features_add = std.Target.wasm.featureSet(&.{.atomics}), .os_tag = .freestanding, - }, + }), }); lib.entry = .disabled; lib.use_llvm = false; diff --git a/test/link/wasm/bss/build.zig b/test/link/wasm/bss/build.zig index c569225f8b25..3a1592f394d5 100644 --- a/test/link/wasm/bss/build.zig +++ b/test/link/wasm/bss/build.zig @@ -17,13 +17,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, - .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize_mode, + .strip = false, }); lib.entry = .disabled; lib.use_llvm = false; lib.use_lld = false; - lib.strip = false; // to make sure the bss segment is emitted, we must import memory lib.import_memory = true; lib.link_gc_sections = false; @@ -65,13 +65,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "lib2.zig" }, - .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize_mode, + .strip = false, }); lib.entry = .disabled; lib.use_llvm = false; lib.use_lld = false; - lib.strip = false; // to make sure the bss segment is emitted, we must import memory lib.import_memory = true; lib.link_gc_sections = false; diff --git a/test/link/wasm/export-data/build.zig b/test/link/wasm/export-data/build.zig index f1bba98d5f4b..bba00d5c1281 100644 --- a/test/link/wasm/export-data/build.zig +++ b/test/link/wasm/export-data/build.zig @@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void { }); lib.entry = .disabled; lib.use_lld = false; - lib.export_symbol_names = &.{ "foo", "bar" }; + lib.root_module.export_symbol_names = &.{ "foo", "bar" }; lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse const check_lib = lib.checkObject(); diff --git a/test/link/wasm/export/build.zig b/test/link/wasm/export/build.zig index 2183ccd5855a..ab2893ce3c6a 100644 --- a/test/link/wasm/export/build.zig +++ b/test/link/wasm/export/build.zig @@ -17,7 +17,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize .name = "no-export", .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, - .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), }); no_export.entry = .disabled; no_export.use_llvm = false; @@ -27,7 +27,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize .name = "dynamic", .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, - .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), }); dynamic_export.entry = .disabled; dynamic_export.rdynamic = true; @@ -38,10 +38,10 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize .name = "force", .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, - .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), }); force_export.entry = .disabled; - force_export.export_symbol_names = &.{"foo"}; + force_export.root_module.export_symbol_names = &.{"foo"}; force_export.use_llvm = false; force_export.use_lld = false; diff --git a/test/link/wasm/extern-mangle/build.zig b/test/link/wasm/extern-mangle/build.zig index d361dc140e96..41a00eefc97d 100644 --- a/test/link/wasm/extern-mangle/build.zig +++ b/test/link/wasm/extern-mangle/build.zig @@ -14,7 +14,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, - .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize, }); lib.entry = .disabled; diff --git a/test/link/wasm/extern/build.zig b/test/link/wasm/extern/build.zig index a50589952042..baa2b6d61e81 100644 --- a/test/link/wasm/extern/build.zig +++ b/test/link/wasm/extern/build.zig @@ -17,7 +17,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize .name = "extern", .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, - .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi }, + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .wasi }), }); exe.addCSourceFile(.{ .file = .{ .path = "foo.c" }, .flags = &.{} }); exe.use_llvm = false; diff --git a/test/link/wasm/function-table/build.zig b/test/link/wasm/function-table/build.zig index 5a139acb3e6b..aca66e4f7157 100644 --- a/test/link/wasm/function-table/build.zig +++ b/test/link/wasm/function-table/build.zig @@ -16,7 +16,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const import_table = b.addExecutable(.{ .name = "import_table", .root_source_file = .{ .path = "lib.zig" }, - .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize, }); import_table.entry = .disabled; @@ -28,7 +28,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const export_table = b.addExecutable(.{ .name = "export_table", .root_source_file = .{ .path = "lib.zig" }, - .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize, }); export_table.entry = .disabled; @@ -40,7 +40,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const regular_table = b.addExecutable(.{ .name = "regular_table", .root_source_file = .{ .path = "lib.zig" }, - .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize, }); regular_table.entry = .disabled; diff --git a/test/link/wasm/infer-features/build.zig b/test/link/wasm/infer-features/build.zig index c883063dc514..e3fe860f5406 100644 --- a/test/link/wasm/infer-features/build.zig +++ b/test/link/wasm/infer-features/build.zig @@ -7,11 +7,11 @@ pub fn build(b: *std.Build) void { const c_obj = b.addObject(.{ .name = "c_obj", .optimize = .Debug, - .target = .{ + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge }, .os_tag = .freestanding, - }, + }), }); c_obj.addCSourceFile(.{ .file = .{ .path = "foo.c" }, .flags = &.{} }); @@ -21,11 +21,11 @@ pub fn build(b: *std.Build) void { .name = "lib", .root_source_file = .{ .path = "main.zig" }, .optimize = .Debug, - .target = .{ + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, .os_tag = .freestanding, - }, + }), }); lib.entry = .disabled; lib.use_llvm = false; diff --git a/test/link/wasm/producers/build.zig b/test/link/wasm/producers/build.zig index b66521a45691..4fb777e1232d 100644 --- a/test/link/wasm/producers/build.zig +++ b/test/link/wasm/producers/build.zig @@ -17,13 +17,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, - .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize, + .strip = false, }); lib.entry = .disabled; lib.use_llvm = false; lib.use_lld = false; - lib.strip = false; b.installArtifact(lib); const version_fmt = "version " ++ builtin.zig_version_string; diff --git a/test/link/wasm/segments/build.zig b/test/link/wasm/segments/build.zig index 6ab2e4a7a320..86073ff9770d 100644 --- a/test/link/wasm/segments/build.zig +++ b/test/link/wasm/segments/build.zig @@ -16,13 +16,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, - .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize, + .strip = false, }); lib.entry = .disabled; lib.use_llvm = false; lib.use_lld = false; - lib.strip = false; lib.link_gc_sections = false; // so data is not garbage collected and we can verify data section b.installArtifact(lib); diff --git a/test/link/wasm/shared-memory/build.zig b/test/link/wasm/shared-memory/build.zig index a1d660d13582..f8751d99c049 100644 --- a/test/link/wasm/shared-memory/build.zig +++ b/test/link/wasm/shared-memory/build.zig @@ -21,16 +21,16 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt .os_tag = .freestanding, }, .optimize = optimize_mode, + .strip = false, + .single_threaded = false, }); lib.entry = .disabled; lib.use_lld = false; - lib.strip = false; lib.import_memory = true; lib.export_memory = true; lib.shared_memory = true; lib.max_memory = 67108864; - lib.single_threaded = false; - lib.export_symbol_names = &.{"foo"}; + lib.root_module.export_symbol_names = &.{"foo"}; const check_lib = lib.checkObject(); diff --git a/test/link/wasm/stack_pointer/build.zig b/test/link/wasm/stack_pointer/build.zig index 93c0f10e1475..e95c27827ed0 100644 --- a/test/link/wasm/stack_pointer/build.zig +++ b/test/link/wasm/stack_pointer/build.zig @@ -16,13 +16,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, - .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize, + .strip = false, }); lib.entry = .disabled; lib.use_llvm = false; lib.use_lld = false; - lib.strip = false; lib.stack_size = std.wasm.page_size * 2; // set an explicit stack size lib.link_gc_sections = false; b.installArtifact(lib); diff --git a/test/link/wasm/type/build.zig b/test/link/wasm/type/build.zig index 88ded91403a5..a318ddb1f97d 100644 --- a/test/link/wasm/type/build.zig +++ b/test/link/wasm/type/build.zig @@ -16,13 +16,13 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const lib = b.addExecutable(.{ .name = "lib", .root_source_file = .{ .path = "lib.zig" }, - .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, + .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize, + .strip = false, }); lib.entry = .disabled; lib.use_llvm = false; lib.use_lld = false; - lib.strip = false; b.installArtifact(lib); const check_lib = lib.checkObject(); diff --git a/test/llvm_targets.zig b/test/llvm_targets.zig index 6f01925a9fc2..99eb161a5892 100644 --- a/test/llvm_targets.zig +++ b/test/llvm_targets.zig @@ -1,7 +1,7 @@ const std = @import("std"); const Cases = @import("src/Cases.zig"); -const targets = [_]std.zig.CrossTarget{ +const targets = [_]std.Target.Query{ .{ .cpu_arch = .aarch64, .os_tag = .freestanding, .abi = .none }, .{ .cpu_arch = .aarch64, .os_tag = .ios, .abi = .none }, .{ .cpu_arch = .aarch64, .os_tag = .ios, .abi = .simulator }, @@ -127,17 +127,21 @@ const targets = [_]std.zig.CrossTarget{ .{ .cpu_arch = .xtensa, .os_tag = .linux, .abi = .none }, }; -pub fn addCases(ctx: *Cases, build_options: @import("cases.zig").BuildOptions) !void { +pub fn addCases( + ctx: *Cases, + build_options: @import("cases.zig").BuildOptions, + b: *std.Build, +) !void { if (!build_options.enable_llvm) return; - for (targets) |target| { - if (target.cpu_arch) |arch| switch (arch) { + for (targets) |target_query| { + if (target_query.cpu_arch) |arch| switch (arch) { .m68k => if (!build_options.llvm_has_m68k) continue, .csky => if (!build_options.llvm_has_csky) continue, .arc => if (!build_options.llvm_has_arc) continue, .xtensa => if (!build_options.llvm_has_xtensa) continue, else => {}, }; - var case = ctx.noEmitUsingLlvmBackend("llvm_targets", target); + var case = ctx.noEmitUsingLlvmBackend("llvm_targets", b.resolveTargetQuery(target_query)); case.addCompile(""); } } diff --git a/test/nvptx.zig b/test/nvptx.zig index c3748570e89c..fd4c66db8978 100644 --- a/test/nvptx.zig +++ b/test/nvptx.zig @@ -1,9 +1,14 @@ const std = @import("std"); const Cases = @import("src/Cases.zig"); -pub fn addCases(ctx: *Cases) !void { +pub fn addCases(ctx: *Cases, b: *std.Build) !void { + const target = b.resolveTargetQuery(.{ + .cpu_arch = .nvptx64, + .os_tag = .cuda, + }); + { - var case = addPtx(ctx, "simple addition and subtraction"); + var case = addPtx(ctx, target, "simple addition and subtraction"); case.addCompile( \\fn add(a: i32, b: i32) i32 { @@ -20,7 +25,7 @@ pub fn addCases(ctx: *Cases) !void { } { - var case = addPtx(ctx, "read special registers"); + var case = addPtx(ctx, target, "read special registers"); case.addCompile( \\fn threadIdX() u32 { @@ -37,7 +42,7 @@ pub fn addCases(ctx: *Cases) !void { } { - var case = addPtx(ctx, "address spaces"); + var case = addPtx(ctx, target, "address spaces"); case.addCompile( \\var x: i32 addrspace(.global) = 0; @@ -50,7 +55,7 @@ pub fn addCases(ctx: *Cases) !void { } { - var case = addPtx(ctx, "reduce in shared mem"); + var case = addPtx(ctx, target, "reduce in shared mem"); case.addCompile( \\fn threadIdX() u32 { \\ return asm ("mov.u32 \t%[r], %tid.x;" @@ -82,18 +87,10 @@ pub fn addCases(ctx: *Cases) !void { } } -const nvptx_target = std.zig.CrossTarget{ - .cpu_arch = .nvptx64, - .os_tag = .cuda, -}; - -pub fn addPtx( - ctx: *Cases, - name: []const u8, -) *Cases.Case { +fn addPtx(ctx: *Cases, target: std.Build.ResolvedTarget, name: []const u8) *Cases.Case { ctx.cases.append(.{ .name = name, - .target = nvptx_target, + .target = target, .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator), .output_mode = .Obj, .deps = std.ArrayList(Cases.DepModule).init(ctx.cases.allocator), diff --git a/test/src/Cases.zig b/test/src/Cases.zig index 262f8e9ac76e..6fedf43bf729 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -76,7 +76,7 @@ pub const Case = struct { name: []const u8, /// The platform the test targets. For non-native platforms, an emulator /// such as QEMU is required for tests to complete. - target: CrossTarget, + target: std.Build.ResolvedTarget, /// In order to be able to run e.g. Execution updates, this must be set /// to Executable. output_mode: std.builtin.OutputMode, @@ -155,7 +155,7 @@ pub const Translate = struct { name: []const u8, input: [:0]const u8, - target: CrossTarget, + target: std.Build.ResolvedTarget, link_libc: bool, c_frontend: CFrontend, kind: union(enum) { @@ -171,7 +171,7 @@ pub const Translate = struct { pub fn addExe( ctx: *Cases, name: []const u8, - target: CrossTarget, + target: std.Build.ResolvedTarget, ) *Case { ctx.cases.append(Case{ .name = name, @@ -184,16 +184,16 @@ pub fn addExe( } /// Adds a test case for Zig input, producing an executable -pub fn exe(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { +pub fn exe(ctx: *Cases, name: []const u8, target: std.Build.ResolvedTarget) *Case { return ctx.addExe(name, target); } -pub fn exeFromCompiledC(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { - var target_adjusted = target; - target_adjusted.ofmt = .c; +pub fn exeFromCompiledC(ctx: *Cases, name: []const u8, target_query: std.Target.Query, b: *std.Build) *Case { + var adjusted_query = target_query; + adjusted_query.ofmt = .c; ctx.cases.append(Case{ .name = name, - .target = target_adjusted, + .target = b.resolveTargetQuery(adjusted_query), .updates = std.ArrayList(Update).init(ctx.cases.allocator), .output_mode = .Exe, .deps = std.ArrayList(DepModule).init(ctx.arena), @@ -202,7 +202,7 @@ pub fn exeFromCompiledC(ctx: *Cases, name: []const u8, target: CrossTarget) *Cas return &ctx.cases.items[ctx.cases.items.len - 1]; } -pub fn noEmitUsingLlvmBackend(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { +pub fn noEmitUsingLlvmBackend(ctx: *Cases, name: []const u8, target: std.Build.ResolvedTarget) *Case { ctx.cases.append(Case{ .name = name, .target = target, @@ -217,7 +217,7 @@ pub fn noEmitUsingLlvmBackend(ctx: *Cases, name: []const u8, target: CrossTarget /// Adds a test case that uses the LLVM backend to emit an executable. /// Currently this implies linking libc, because only then we can generate a testable executable. -pub fn exeUsingLlvmBackend(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { +pub fn exeUsingLlvmBackend(ctx: *Cases, name: []const u8, target: std.Build.ResolvedTarget) *Case { ctx.cases.append(Case{ .name = name, .target = target, @@ -233,7 +233,7 @@ pub fn exeUsingLlvmBackend(ctx: *Cases, name: []const u8, target: CrossTarget) * pub fn addObj( ctx: *Cases, name: []const u8, - target: CrossTarget, + target: std.Build.ResolvedTarget, ) *Case { ctx.cases.append(Case{ .name = name, @@ -248,7 +248,7 @@ pub fn addObj( pub fn addTest( ctx: *Cases, name: []const u8, - target: CrossTarget, + target: std.Build.ResolvedTarget, ) *Case { ctx.cases.append(Case{ .name = name, @@ -262,17 +262,17 @@ pub fn addTest( } /// Adds a test case for Zig input, producing an object file. -pub fn obj(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { +pub fn obj(ctx: *Cases, name: []const u8, target: std.Build.ResolvedTarget) *Case { return ctx.addObj(name, target); } /// Adds a test case for ZIR input, producing an object file. -pub fn objZIR(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { +pub fn objZIR(ctx: *Cases, name: []const u8, target: std.Build.ResolvedTarget) *Case { return ctx.addObj(name, target, .ZIR); } /// Adds a test case for Zig or ZIR input, producing C code. -pub fn addC(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { +pub fn addC(ctx: *Cases, name: []const u8, target: std.Build.ResolvedTarget) *Case { var target_adjusted = target; target_adjusted.ofmt = std.Target.ObjectFormat.c; ctx.cases.append(Case{ @@ -308,7 +308,7 @@ pub fn compareOutput( pub fn addTransform( ctx: *Cases, name: []const u8, - target: CrossTarget, + target: std.Build.ResolvedTarget, src: [:0]const u8, result: [:0]const u8, ) void { @@ -320,7 +320,7 @@ pub fn addTransform( pub fn transform( ctx: *Cases, name: []const u8, - target: CrossTarget, + target: std.Build.ResolvedTarget, src: [:0]const u8, result: [:0]const u8, ) void { @@ -330,7 +330,7 @@ pub fn transform( pub fn addError( ctx: *Cases, name: []const u8, - target: CrossTarget, + target: std.Build.ResolvedTarget, src: [:0]const u8, expected_errors: []const []const u8, ) void { @@ -343,7 +343,7 @@ pub fn addError( pub fn compileError( ctx: *Cases, name: []const u8, - target: CrossTarget, + target: std.Build.ResolvedTarget, src: [:0]const u8, expected_errors: []const []const u8, ) void { @@ -355,7 +355,7 @@ pub fn compileError( pub fn addCompile( ctx: *Cases, name: []const u8, - target: CrossTarget, + target: std.Build.ResolvedTarget, src: [:0]const u8, ) void { ctx.addObj(name, target).addCompile(src); @@ -368,9 +368,9 @@ pub fn addCompile( /// Each file should include a test manifest as a contiguous block of comments at /// the end of the file. The first line should be the test type, followed by a set of /// key-value config values, followed by a blank line, then the expected output. -pub fn addFromDir(ctx: *Cases, dir: std.fs.Dir) void { +pub fn addFromDir(ctx: *Cases, dir: std.fs.Dir, b: *std.Build) void { var current_file: []const u8 = "none"; - ctx.addFromDirInner(dir, ¤t_file) catch |err| { + ctx.addFromDirInner(dir, ¤t_file, b) catch |err| { std.debug.panicExtra( @errorReturnTrace(), @returnAddress(), @@ -386,6 +386,7 @@ fn addFromDirInner( /// This is kept up to date with the currently being processed file so /// that if any errors occur the caller knows it happened during this file. current_file: *[]const u8, + b: *std.Build, ) !void { var it = try iterable_dir.walk(ctx.arena); var filenames = std.ArrayList([]const u8).init(ctx.arena); @@ -422,7 +423,7 @@ fn addFromDirInner( var manifest = try TestManifest.parse(ctx.arena, src); const backends = try manifest.getConfigForKeyAlloc(ctx.arena, "backend", Backend); - const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", CrossTarget); + const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", std.Target.Query); const c_frontends = try manifest.getConfigForKeyAlloc(ctx.arena, "c_frontend", CFrontend); const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool); const link_libc = try manifest.getConfigForKeyAssertSingle("link_libc", bool); @@ -430,12 +431,12 @@ fn addFromDirInner( if (manifest.type == .translate_c) { for (c_frontends) |c_frontend| { - for (targets) |target| { + for (targets) |target_query| { const output = try manifest.trailingLinesSplit(ctx.arena); try ctx.translate.append(.{ .name = std.fs.path.stem(filename), .c_frontend = c_frontend, - .target = target, + .target = b.resolveTargetQuery(target_query), .link_libc = link_libc, .input = src, .kind = .{ .translate = output }, @@ -446,12 +447,12 @@ fn addFromDirInner( } if (manifest.type == .run_translated_c) { for (c_frontends) |c_frontend| { - for (targets) |target| { + for (targets) |target_query| { const output = try manifest.trailingSplit(ctx.arena); try ctx.translate.append(.{ .name = std.fs.path.stem(filename), .c_frontend = c_frontend, - .target = target, + .target = b.resolveTargetQuery(target_query), .link_libc = link_libc, .input = src, .kind = .{ .run = output }, @@ -464,16 +465,18 @@ fn addFromDirInner( var cases = std.ArrayList(usize).init(ctx.arena); // Cross-product to get all possible test combinations - for (backends) |backend| { - for (targets) |target| { + for (targets) |target_query| { + const resolved_target = b.resolveTargetQuery(target_query); + const target = resolved_target.result; + for (backends) |backend| { if (backend == .stage2 and - target.getCpuArch() != .wasm32 and target.getCpuArch() != .x86_64) + target.cpu.arch != .wasm32 and target.cpu.arch != .x86_64) { // Other backends don't support new liveness format continue; } - if (backend == .stage2 and target.getOsTag() == .macos and - target.getCpuArch() == .x86_64 and builtin.cpu.arch == .aarch64) + if (backend == .stage2 and target.os.tag == .macos and + target.cpu.arch == .x86_64 and builtin.cpu.arch == .aarch64) { // Rosetta has issues with ZLD continue; @@ -482,7 +485,7 @@ fn addFromDirInner( const next = ctx.cases.items.len; try ctx.cases.append(.{ .name = std.fs.path.stem(filename), - .target = target, + .target = resolved_target, .backend = backend, .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator), .is_test = is_test, @@ -538,7 +541,7 @@ pub fn lowerToBuildSteps( cases_dir_path: []const u8, incremental_exe: *std.Build.Step.Compile, ) void { - const host = std.zig.system.NativeTargetInfo.detect(.{}) catch |err| + const host = std.zig.system.resolveTargetQuery(.{}) catch |err| std.debug.panic("unable to detect native host: {s}\n", .{@errorName(err)}); for (self.incremental_cases.items) |incr_case| { @@ -622,8 +625,8 @@ pub fn lowerToBuildSteps( } for (case.deps.items) |dep| { - artifact.addAnonymousModule(dep.name, .{ - .source_file = file_sources.get(dep.path).?, + artifact.root_module.addAnonymousImport(dep.name, .{ + .root_source_file = file_sources.get(dep.path).?, }); } @@ -644,10 +647,8 @@ pub fn lowerToBuildSteps( parent_step.dependOn(&artifact.step); }, .Execution => |expected_stdout| no_exec: { - const run = if (case.target.ofmt == .c) run_step: { - const target_info = std.zig.system.NativeTargetInfo.detect(case.target) catch |err| - std.debug.panic("unable to detect target host: {s}\n", .{@errorName(err)}); - if (host.getExternalExecutor(&target_info, .{ .link_libc = true }) != .native) { + const run = if (case.target.result.ofmt == .c) run_step: { + if (getExternalExecutor(host, &case.target.result, .{ .link_libc = true }) != .native) { // We wouldn't be able to run the compiled C code. break :no_exec; } @@ -666,7 +667,7 @@ pub fn lowerToBuildSteps( "--", "-lc", "-target", - case.target.zigTriple(b.allocator) catch @panic("OOM"), + case.target.result.zigTriple(b.allocator) catch @panic("OOM"), }); run_c.addArtifactArg(artifact); break :run_step run_c; @@ -692,9 +693,7 @@ pub fn lowerToBuildSteps( continue; // Pass test. } - const target_info = std.zig.system.NativeTargetInfo.detect(case.target) catch |err| - std.debug.panic("unable to detect target host: {s}\n", .{@errorName(err)}); - if (host.getExternalExecutor(&target_info, .{ .link_libc = true }) != .native) { + if (getExternalExecutor(host, &case.target.result, .{ .link_libc = true }) != .native) { // We wouldn't be able to run the compiled C code. continue; // Pass test. } @@ -1159,9 +1158,9 @@ const TestManifest = struct { } fn getDefaultParser(comptime T: type) ParseFn(T) { - if (T == CrossTarget) return struct { + if (T == std.Target.Query) return struct { fn parse(str: []const u8) anyerror!T { - return CrossTarget.parse(.{ .arch_os_abi = str }); + return std.Target.Query.parse(.{ .arch_os_abi = str }); } }.parse; @@ -1198,7 +1197,8 @@ const builtin = @import("builtin"); const std = @import("std"); const assert = std.debug.assert; const Allocator = std.mem.Allocator; -const CrossTarget = std.zig.CrossTarget; +const getExternalExecutor = std.zig.system.getExternalExecutor; + const Compilation = @import("../../src/Compilation.zig"); const zig_h = @import("../../src/link.zig").File.C.zig_h; const introspect = @import("../../src/introspect.zig"); @@ -1287,7 +1287,7 @@ pub fn main() !void { if (cases.items.len == 0) { const backends = try manifest.getConfigForKeyAlloc(arena, "backend", Backend); - const targets = try manifest.getConfigForKeyAlloc(arena, "target", CrossTarget); + const targets = try manifest.getConfigForKeyAlloc(arena, "target", std.Target.Query); const c_frontends = try manifest.getConfigForKeyAlloc(ctx.arena, "c_frontend", CFrontend); const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool); const link_libc = try manifest.getConfigForKeyAssertSingle("link_libc", bool); @@ -1295,12 +1295,12 @@ pub fn main() !void { if (manifest.type == .translate_c) { for (c_frontends) |c_frontend| { - for (targets) |target| { + for (targets) |target_query| { const output = try manifest.trailingLinesSplit(ctx.arena); try ctx.translate.append(.{ .name = std.fs.path.stem(filename), .c_frontend = c_frontend, - .target = target, + .target = resolveTargetQuery(target_query), .is_test = is_test, .link_libc = link_libc, .input = src, @@ -1312,12 +1312,12 @@ pub fn main() !void { } if (manifest.type == .run_translated_c) { for (c_frontends) |c_frontend| { - for (targets) |target| { + for (targets) |target_query| { const output = try manifest.trailingSplit(ctx.arena); try ctx.translate.append(.{ .name = std.fs.path.stem(filename), .c_frontend = c_frontend, - .target = target, + .target = resolveTargetQuery(target_query), .is_test = is_test, .link_libc = link_libc, .output = output, @@ -1385,8 +1385,16 @@ pub fn main() !void { return runCases(&ctx, zig_exe_path); } +fn resolveTargetQuery(query: std.Target.Query) std.Build.ResolvedTarget { + return .{ + .query = query, + .target = std.zig.system.resolveTargetQuery(query) catch + @panic("unable to resolve target query"), + }; +} + fn runCases(self: *Cases, zig_exe_path: []const u8) !void { - const host = try std.zig.system.NativeTargetInfo.detect(.{}); + const host = try std.zig.system.resolveTargetQuery(.{}); var progress = std.Progress{}; const root_node = progress.start("compiler", self.cases.items.len); @@ -1467,7 +1475,7 @@ fn runOneCase( zig_exe_path: []const u8, thread_pool: *ThreadPool, global_cache_directory: Compilation.Directory, - host: std.zig.system.NativeTargetInfo, + host: std.Target, ) !void { const tmp_src_path = "tmp.zig"; const enable_rosetta = build_options.enable_rosetta; @@ -1477,8 +1485,7 @@ fn runOneCase( const enable_darling = build_options.enable_darling; const glibc_runtimes_dir: ?[]const u8 = build_options.glibc_runtimes_dir; - const target_info = try std.zig.system.NativeTargetInfo.detect(case.target); - const target = target_info.target; + const target = try std.zig.system.resolveTargetQuery(case.target); var arena_allocator = std.heap.ArenaAllocator.init(allocator); defer arena_allocator.deinit(); @@ -1568,7 +1575,7 @@ fn runOneCase( .keep_source_files_loaded = true, .is_native_os = case.target.isNativeOs(), .is_native_abi = case.target.isNativeAbi(), - .dynamic_linker = target_info.dynamic_linker.get(), + .dynamic_linker = target.dynamic_linker.get(), .link_libc = case.link_libc, .use_llvm = use_llvm, .self_exe_path = zig_exe_path, @@ -1704,7 +1711,7 @@ fn runOneCase( .{ &tmp.sub_path, bin_name }, ); if (case.target.ofmt != null and case.target.ofmt.? == .c) { - if (host.getExternalExecutor(target_info, .{ .link_libc = true }) != .native) { + if (getExternalExecutor(host, &target, .{ .link_libc = true }) != .native) { // We wouldn't be able to run the compiled C code. continue :update; // Pass test. } @@ -1723,7 +1730,7 @@ fn runOneCase( if (zig_lib_directory.path) |p| { try argv.appendSlice(&.{ "-I", p }); } - } else switch (host.getExternalExecutor(target_info, .{ .link_libc = case.link_libc })) { + } else switch (getExternalExecutor(host, &target, .{ .link_libc = case.link_libc })) { .native => { if (case.backend == .stage2 and case.target.getCpuArch().isArmOrThumb()) { // https://github.com/ziglang/zig/issues/13623 diff --git a/test/src/CompareOutput.zig b/test/src/CompareOutput.zig index 39226f036929..b3570b6ee224 100644 --- a/test/src/CompareOutput.zig +++ b/test/src/CompareOutput.zig @@ -96,7 +96,7 @@ pub fn addCase(self: *CompareOutput, case: TestCase) void { const exe = b.addExecutable(.{ .name = "test", - .target = .{}, + .target = b.host, .optimize = .Debug, }); exe.addAssemblyFile(write_src.files.items[0].getPath()); @@ -121,7 +121,7 @@ pub fn addCase(self: *CompareOutput, case: TestCase) void { .name = "test", .root_source_file = write_src.files.items[0].getPath(), .optimize = optimize, - .target = .{}, + .target = b.host, }); if (case.link_libc) { exe.linkSystemLibrary("c"); @@ -146,7 +146,7 @@ pub fn addCase(self: *CompareOutput, case: TestCase) void { const exe = b.addExecutable(.{ .name = "test", .root_source_file = write_src.files.items[0].getPath(), - .target = .{}, + .target = b.host, .optimize = .Debug, }); if (case.link_libc) { diff --git a/test/src/StackTrace.zig b/test/src/StackTrace.zig index c42e9114be8c..191c35009147 100644 --- a/test/src/StackTrace.zig +++ b/test/src/StackTrace.zig @@ -5,44 +5,33 @@ test_filter: ?[]const u8, optimize_modes: []const OptimizeMode, check_exe: *std.Build.Step.Compile, -const Expect = [@typeInfo(OptimizeMode).Enum.fields.len][]const u8; +const Config = struct { + name: []const u8, + source: []const u8, + Debug: ?PerMode = null, + ReleaseSmall: ?PerMode = null, + ReleaseSafe: ?PerMode = null, + ReleaseFast: ?PerMode = null, -pub fn addCase(self: *StackTrace, config: anytype) void { - if (@hasField(@TypeOf(config), "exclude")) { - if (config.exclude.exclude()) return; - } - if (@hasField(@TypeOf(config), "exclude_arch")) { - const exclude_arch: []const std.Target.Cpu.Arch = &config.exclude_arch; - for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return; - } - if (@hasField(@TypeOf(config), "exclude_os")) { - const exclude_os: []const std.Target.Os.Tag = &config.exclude_os; - for (exclude_os) |os| if (os == builtin.os.tag) return; - } - for (self.optimize_modes) |optimize_mode| { - switch (optimize_mode) { - .Debug => { - if (@hasField(@TypeOf(config), "Debug")) { - self.addExpect(config.name, config.source, optimize_mode, config.Debug); - } - }, - .ReleaseSafe => { - if (@hasField(@TypeOf(config), "ReleaseSafe")) { - self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSafe); - } - }, - .ReleaseFast => { - if (@hasField(@TypeOf(config), "ReleaseFast")) { - self.addExpect(config.name, config.source, optimize_mode, config.ReleaseFast); - } - }, - .ReleaseSmall => { - if (@hasField(@TypeOf(config), "ReleaseSmall")) { - self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSmall); - } - }, - } - } + const PerMode = struct { + expect: []const u8, + exclude_os: []const std.Target.Os.Tag = &.{}, + error_tracing: ?bool = null, + }; +}; + +pub fn addCase(self: *StackTrace, config: Config) void { + if (config.Debug) |per_mode| + self.addExpect(config.name, config.source, .Debug, per_mode); + + if (config.ReleaseSmall) |per_mode| + self.addExpect(config.name, config.source, .ReleaseSmall, per_mode); + + if (config.ReleaseFast) |per_mode| + self.addExpect(config.name, config.source, .ReleaseFast, per_mode); + + if (config.ReleaseSafe) |per_mode| + self.addExpect(config.name, config.source, .ReleaseSafe, per_mode); } fn addExpect( @@ -50,19 +39,9 @@ fn addExpect( name: []const u8, source: []const u8, optimize_mode: OptimizeMode, - mode_config: anytype, + mode_config: Config.PerMode, ) void { - if (@hasField(@TypeOf(mode_config), "exclude")) { - if (mode_config.exclude.exclude()) return; - } - if (@hasField(@TypeOf(mode_config), "exclude_arch")) { - const exclude_arch: []const std.Target.Cpu.Arch = &mode_config.exclude_arch; - for (exclude_arch) |arch| if (arch == builtin.cpu.arch) return; - } - if (@hasField(@TypeOf(mode_config), "exclude_os")) { - const exclude_os: []const std.Target.Os.Tag = &mode_config.exclude_os; - for (exclude_os) |os| if (os == builtin.os.tag) return; - } + for (mode_config.exclude_os) |tag| if (tag == builtin.os.tag) return; const b = self.b; const annotated_case_name = fmt.allocPrint(b.allocator, "check {s} ({s})", .{ @@ -77,7 +56,8 @@ fn addExpect( .name = "test", .root_source_file = write_src.files.items[0].getPath(), .optimize = optimize_mode, - .target = .{}, + .target = b.host, + .error_tracing = mode_config.error_tracing, }); const run = b.addRunArtifact(exe); diff --git a/test/src/run_translated_c.zig b/test/src/run_translated_c.zig index cd2d0d9e3b16..544f9a53098a 100644 --- a/test/src/run_translated_c.zig +++ b/test/src/run_translated_c.zig @@ -11,7 +11,7 @@ pub const RunTranslatedCContext = struct { step: *std.Build.Step, test_index: usize, test_filter: ?[]const u8, - target: std.zig.CrossTarget, + target: std.Build.ResolvedTarget, const TestCase = struct { name: []const u8, @@ -86,7 +86,7 @@ pub const RunTranslatedCContext = struct { } const translate_c = b.addTranslateC(.{ .source_file = write_src.files.items[0].getPath(), - .target = .{}, + .target = b.host, .optimize = .Debug, }); diff --git a/test/src/translate_c.zig b/test/src/translate_c.zig index a71886099f7a..528e370918c3 100644 --- a/test/src/translate_c.zig +++ b/test/src/translate_c.zig @@ -5,7 +5,6 @@ const ArrayList = std.ArrayList; const fmt = std.fmt; const mem = std.mem; const fs = std.fs; -const CrossTarget = std.zig.CrossTarget; pub const TranslateCContext = struct { b: *std.Build, @@ -18,7 +17,7 @@ pub const TranslateCContext = struct { sources: ArrayList(SourceFile), expected_lines: ArrayList([]const u8), allow_warnings: bool, - target: CrossTarget = CrossTarget{}, + target: std.Target.Query = .{}, const SourceFile = struct { filename: []const u8, @@ -74,7 +73,7 @@ pub const TranslateCContext = struct { pub fn addWithTarget( self: *TranslateCContext, name: []const u8, - target: CrossTarget, + target: std.Target.Query, source: []const u8, expected_lines: []const []const u8, ) void { @@ -109,7 +108,7 @@ pub const TranslateCContext = struct { const translate_c = b.addTranslateC(.{ .source_file = write_src.files.items[0].getPath(), - .target = case.target, + .target = b.resolveTargetQuery(case.target), .optimize = .Debug, }); diff --git a/test/stack_traces.zig b/test/stack_traces.zig index 0ace004bc64c..467c53bcb935 100644 --- a/test/stack_traces.zig +++ b/test/stack_traces.zig @@ -20,7 +20,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , }, .ReleaseSafe = .{ - .exclude_os = .{ + .exclude_os = &.{ .windows, // TODO .linux, // defeated by aggressive inlining }, @@ -31,6 +31,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ ^ \\ , + .error_tracing = true, }, .ReleaseFast = .{ .expect = @@ -70,7 +71,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , }, .ReleaseSafe = .{ - .exclude_os = .{ + .exclude_os = &.{ .windows, // TODO }, .expect = @@ -83,6 +84,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ ^ \\ , + .error_tracing = true, }, .ReleaseFast = .{ .expect = @@ -125,7 +127,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , }, .ReleaseSafe = .{ - .exclude_os = .{ + .exclude_os = &.{ .windows, // TODO .linux, // defeated by aggressive inlining }, @@ -136,6 +138,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ ^ \\ , + .error_tracing = true, }, .ReleaseFast = .{ .expect = @@ -176,7 +179,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , }, .ReleaseSafe = .{ - .exclude_os = .{ + .exclude_os = &.{ .windows, // TODO .linux, // defeated by aggressive inlining }, @@ -187,6 +190,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ ^ \\ , + .error_tracing = true, }, .ReleaseFast = .{ .expect = @@ -230,7 +234,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , }, .ReleaseSafe = .{ - .exclude_os = .{ + .exclude_os = &.{ .windows, // TODO .linux, // defeated by aggressive inlining }, @@ -244,6 +248,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ ^ \\ , + .error_tracing = true, }, .ReleaseFast = .{ .expect = @@ -286,7 +291,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , }, .ReleaseSafe = .{ - .exclude_os = .{ + .exclude_os = &.{ .windows, // TODO .linux, // defeated by aggressive inlining }, @@ -297,6 +302,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ ^ \\ , + .error_tracing = true, }, .ReleaseFast = .{ .expect = @@ -336,7 +342,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , }, .ReleaseSafe = .{ - .exclude_os = .{ + .exclude_os = &.{ .windows, // TODO .linux, // defeated by aggressive inlining }, @@ -350,6 +356,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ ^ \\ , + .error_tracing = true, }, .ReleaseFast = .{ .expect = @@ -391,7 +398,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , }, .ReleaseSafe = .{ - .exclude_os = .{ + .exclude_os = &.{ .windows, // TODO .linux, // defeated by aggressive inlining }, @@ -402,6 +409,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ ^ \\ , + .error_tracing = true, }, .ReleaseFast = .{ .expect = @@ -461,7 +469,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , }, .ReleaseSafe = .{ - .exclude_os = .{ + .exclude_os = &.{ .windows, // TODO .linux, // defeated by aggressive inlining }, @@ -478,6 +486,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ ^ \\ , + .error_tracing = true, }, .ReleaseFast = .{ .expect = @@ -531,7 +540,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , }, .ReleaseSafe = .{ - .exclude_os = .{ + .exclude_os = &.{ .windows, // TODO }, .expect = @@ -547,6 +556,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ ^ \\ , + .error_tracing = true, }, .ReleaseFast = .{ .expect = @@ -595,7 +605,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , }, .ReleaseSafe = .{ - .exclude_os = .{ + .exclude_os = &.{ .windows, // TODO }, .expect = @@ -611,6 +621,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ ^ \\ , + .error_tracing = true, }, .ReleaseFast = .{ .expect = @@ -659,7 +670,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , }, .ReleaseSafe = .{ - .exclude_os = .{ + .exclude_os = &.{ .windows, // TODO }, .expect = @@ -675,6 +686,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ ^ \\ , + .error_tracing = true, }, .ReleaseFast = .{ .expect = @@ -728,7 +740,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { , }, .ReleaseSafe = .{ - .exclude_os = .{ + .exclude_os = &.{ .windows, // TODO }, .expect = @@ -747,6 +759,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\ ^ \\ , + .error_tracing = true, }, .ReleaseFast = .{ .expect = @@ -763,10 +776,6 @@ pub fn addCases(cases: *tests.StackTracesContext) void { }); cases.addCase(.{ - .exclude_os = .{ - .openbsd, // integer overflow - .windows, // TODO intermittent failures - }, .name = "dumpCurrentStackTrace", .source = \\const std = @import("std"); @@ -783,6 +792,10 @@ pub fn addCases(cases: *tests.StackTracesContext) void { \\} , .Debug = .{ + .exclude_os = &.{ + .openbsd, // integer overflow + .windows, // TODO intermittent failures + }, .expect = \\source.zig:7:8: [address] in foo (test) \\ bar(); diff --git a/test/standalone.zig b/test/standalone.zig index fc455a2aa71c..b26e85c15930 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -2,7 +2,7 @@ pub const SimpleCase = struct { src_path: []const u8, link_libc: bool = false, all_modes: bool = false, - target: std.zig.CrossTarget = .{}, + target: std.Target.Query = .{}, is_test: bool = false, is_exe: bool = true, /// Run only on this OS. @@ -89,10 +89,6 @@ pub const build_cases = [_]BuildCase{ // .build_root = "test/standalone/issue_13970", // .import = @import("standalone/issue_13970/build.zig"), //}, - .{ - .build_root = "test/standalone/main_pkg_path", - .import = @import("standalone/main_pkg_path/build.zig"), - }, .{ .build_root = "test/standalone/shared_library", .import = @import("standalone/shared_library/build.zig"), @@ -262,6 +258,10 @@ pub const build_cases = [_]BuildCase{ .build_root = "test/standalone/ios", .import = @import("standalone/ios/build.zig"), }, + .{ + .build_root = "test/standalone/depend_on_main_mod", + .import = @import("standalone/depend_on_main_mod/build.zig"), + }, }; const std = @import("std"); diff --git a/test/standalone/c_compiler/build.zig b/test/standalone/c_compiler/build.zig index a2191f2f2eb5..0550ad8cee75 100644 --- a/test/standalone/c_compiler/build.zig +++ b/test/standalone/c_compiler/build.zig @@ -23,7 +23,7 @@ fn add( cpp_name: []const u8, optimize: std.builtin.OptimizeMode, ) void { - const target: std.zig.CrossTarget = .{}; + const target = b.host; const exe_c = b.addExecutable(.{ .name = c_name, @@ -42,7 +42,7 @@ fn add( exe_cpp.addCSourceFile(.{ .file = .{ .path = "test.cpp" }, .flags = &[0][]const u8{} }); exe_cpp.linkLibCpp(); - switch (target.getOsTag()) { + switch (target.result.os.tag) { .windows => { // https://github.com/ziglang/zig/issues/8531 exe_cpp.want_lto = false; diff --git a/test/standalone/child_process/build.zig b/test/standalone/child_process/build.zig index b874a7acc5e6..89558c00e609 100644 --- a/test/standalone/child_process/build.zig +++ b/test/standalone/child_process/build.zig @@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const optimize: std.builtin.OptimizeMode = .Debug; - const target: std.zig.CrossTarget = .{}; + const target = b.host; if (builtin.os.tag == .wasi) return; diff --git a/test/standalone/coff_dwarf/build.zig b/test/standalone/coff_dwarf/build.zig index b85b44b686cb..cd7c17efb37e 100644 --- a/test/standalone/coff_dwarf/build.zig +++ b/test/standalone/coff_dwarf/build.zig @@ -11,6 +11,11 @@ pub fn build(b: *std.Build) void { if (builtin.os.tag != .windows) return; + if (builtin.cpu.arch == .aarch64) { + // https://github.com/ziglang/zig/issues/18427 + return; + } + const exe = b.addExecutable(.{ .name = "main", .root_source_file = .{ .path = "main.zig" }, diff --git a/test/standalone/compiler_rt_panic/build.zig b/test/standalone/compiler_rt_panic/build.zig index 3d22319bacf5..8ad7732a931b 100644 --- a/test/standalone/compiler_rt_panic/build.zig +++ b/test/standalone/compiler_rt_panic/build.zig @@ -7,8 +7,8 @@ pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); - const abi = target.getAbi(); - if (target.getObjectFormat() != .elf or !(abi.isMusl() or abi.isGnu())) return; + if (target.result.ofmt != .elf or !(target.result.abi.isMusl() or target.result.abi.isGnu())) + return; const exe = b.addExecutable(.{ .name = "main", diff --git a/test/standalone/dep_diamond/build.zig b/test/standalone/dep_diamond/build.zig index 6c6d9b6d1a25..9190b29594cf 100644 --- a/test/standalone/dep_diamond/build.zig +++ b/test/standalone/dep_diamond/build.zig @@ -7,21 +7,22 @@ pub fn build(b: *std.Build) void { const optimize: std.builtin.OptimizeMode = .Debug; const shared = b.createModule(.{ - .source_file = .{ .path = "shared.zig" }, + .root_source_file = .{ .path = "shared.zig" }, }); const exe = b.addExecutable(.{ .name = "test", .root_source_file = .{ .path = "test.zig" }, + .target = b.host, .optimize = optimize, }); - exe.addAnonymousModule("foo", .{ - .source_file = .{ .path = "foo.zig" }, - .dependencies = &.{.{ .name = "shared", .module = shared }}, + exe.root_module.addAnonymousImport("foo", .{ + .root_source_file = .{ .path = "foo.zig" }, + .imports = &.{.{ .name = "shared", .module = shared }}, }); - exe.addAnonymousModule("bar", .{ - .source_file = .{ .path = "bar.zig" }, - .dependencies = &.{.{ .name = "shared", .module = shared }}, + exe.root_module.addAnonymousImport("bar", .{ + .root_source_file = .{ .path = "bar.zig" }, + .imports = &.{.{ .name = "shared", .module = shared }}, }); const run = b.addRunArtifact(exe); diff --git a/test/standalone/dep_mutually_recursive/build.zig b/test/standalone/dep_mutually_recursive/build.zig index e70e77540182..04589d9b5ba4 100644 --- a/test/standalone/dep_mutually_recursive/build.zig +++ b/test/standalone/dep_mutually_recursive/build.zig @@ -7,20 +7,21 @@ pub fn build(b: *std.Build) void { const optimize: std.builtin.OptimizeMode = .Debug; const foo = b.createModule(.{ - .source_file = .{ .path = "foo.zig" }, + .root_source_file = .{ .path = "foo.zig" }, }); const bar = b.createModule(.{ - .source_file = .{ .path = "bar.zig" }, + .root_source_file = .{ .path = "bar.zig" }, }); - foo.dependencies.put("bar", bar) catch @panic("OOM"); - bar.dependencies.put("foo", foo) catch @panic("OOM"); + foo.addImport("bar", bar); + bar.addImport("foo", foo); const exe = b.addExecutable(.{ .name = "test", .root_source_file = .{ .path = "test.zig" }, + .target = b.host, .optimize = optimize, }); - exe.addModule("foo", foo); + exe.root_module.addImport("foo", foo); const run = b.addRunArtifact(exe); test_step.dependOn(&run.step); diff --git a/test/standalone/dep_recursive/build.zig b/test/standalone/dep_recursive/build.zig index 2e2cf4ea4d4f..a6334e6a9763 100644 --- a/test/standalone/dep_recursive/build.zig +++ b/test/standalone/dep_recursive/build.zig @@ -7,16 +7,17 @@ pub fn build(b: *std.Build) void { const optimize: std.builtin.OptimizeMode = .Debug; const foo = b.createModule(.{ - .source_file = .{ .path = "foo.zig" }, + .root_source_file = .{ .path = "foo.zig" }, }); - foo.dependencies.put("foo", foo) catch @panic("OOM"); + foo.addImport("foo", foo); const exe = b.addExecutable(.{ .name = "test", .root_source_file = .{ .path = "test.zig" }, + .target = b.host, .optimize = optimize, }); - exe.addModule("foo", foo); + exe.root_module.addImport("foo", foo); const run = b.addRunArtifact(exe); test_step.dependOn(&run.step); diff --git a/test/standalone/dep_shared_builtin/build.zig b/test/standalone/dep_shared_builtin/build.zig index 175118d33192..33d53ad166ba 100644 --- a/test/standalone/dep_shared_builtin/build.zig +++ b/test/standalone/dep_shared_builtin/build.zig @@ -9,10 +9,11 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = "test", .root_source_file = .{ .path = "test.zig" }, + .target = b.host, .optimize = optimize, }); - exe.addAnonymousModule("foo", .{ - .source_file = .{ .path = "foo.zig" }, + exe.root_module.addAnonymousImport("foo", .{ + .root_source_file = .{ .path = "foo.zig" }, }); const run = b.addRunArtifact(exe); diff --git a/test/standalone/dep_triangle/build.zig b/test/standalone/dep_triangle/build.zig index 63eed997ceb6..b15555499753 100644 --- a/test/standalone/dep_triangle/build.zig +++ b/test/standalone/dep_triangle/build.zig @@ -7,19 +7,20 @@ pub fn build(b: *std.Build) void { const optimize: std.builtin.OptimizeMode = .Debug; const shared = b.createModule(.{ - .source_file = .{ .path = "shared.zig" }, + .root_source_file = .{ .path = "shared.zig" }, }); const exe = b.addExecutable(.{ .name = "test", .root_source_file = .{ .path = "test.zig" }, + .target = b.host, .optimize = optimize, }); - exe.addAnonymousModule("foo", .{ - .source_file = .{ .path = "foo.zig" }, - .dependencies = &.{.{ .name = "shared", .module = shared }}, + exe.root_module.addAnonymousImport("foo", .{ + .root_source_file = .{ .path = "foo.zig" }, + .imports = &.{.{ .name = "shared", .module = shared }}, }); - exe.addModule("shared", shared); + exe.root_module.addImport("shared", shared); const run = b.addRunArtifact(exe); test_step.dependOn(&run.step); diff --git a/test/standalone/depend_on_main_mod/build.zig b/test/standalone/depend_on_main_mod/build.zig new file mode 100644 index 000000000000..bbef64693e59 --- /dev/null +++ b/test/standalone/depend_on_main_mod/build.zig @@ -0,0 +1,28 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const exe = b.addExecutable(.{ + .name = "depend_on_main_mod", + .root_source_file = .{ .path = "src/main.zig" }, + .target = target, + .optimize = optimize, + }); + + const foo_module = b.addModule("foo", .{ + .root_source_file = .{ .path = "src/foo.zig" }, + }); + + foo_module.addImport("root2", &exe.root_module); + exe.root_module.addImport("foo", foo_module); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.expectExitCode(0); + + test_step.dependOn(&run_cmd.step); +} diff --git a/test/standalone/depend_on_main_mod/src/foo.zig b/test/standalone/depend_on_main_mod/src/foo.zig new file mode 100644 index 000000000000..0a52bce8aa6f --- /dev/null +++ b/test/standalone/depend_on_main_mod/src/foo.zig @@ -0,0 +1,6 @@ +const std = @import("std"); +const assert = std.debug.assert; + +pub fn run() void { + comptime assert(@import("root") == @import("root2")); +} diff --git a/test/standalone/depend_on_main_mod/src/main.zig b/test/standalone/depend_on_main_mod/src/main.zig new file mode 100644 index 000000000000..0735fff4c790 --- /dev/null +++ b/test/standalone/depend_on_main_mod/src/main.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +pub fn main() !void { + @import("foo").run(); +} diff --git a/test/standalone/embed_generated_file/build.zig b/test/standalone/embed_generated_file/build.zig index ca05c33c39a6..3f67cdea5421 100644 --- a/test/standalone/embed_generated_file/build.zig +++ b/test/standalone/embed_generated_file/build.zig @@ -7,10 +7,10 @@ pub fn build(b: *std.Build) void { const bootloader = b.addExecutable(.{ .name = "bootloader", .root_source_file = .{ .path = "bootloader.zig" }, - .target = .{ + .target = b.resolveTargetQuery(.{ .cpu_arch = .x86, .os_tag = .freestanding, - }, + }), .optimize = .ReleaseSmall, }); @@ -18,8 +18,8 @@ pub fn build(b: *std.Build) void { .root_source_file = .{ .path = "main.zig" }, .optimize = .Debug, }); - exe.addAnonymousModule("bootloader.elf", .{ - .source_file = bootloader.getEmittedBin(), + exe.root_module.addAnonymousImport("bootloader.elf", .{ + .root_source_file = bootloader.getEmittedBin(), }); // TODO: actually check the output diff --git a/test/standalone/empty_env/build.zig b/test/standalone/empty_env/build.zig index 27ec75be22df..a2fe48312827 100644 --- a/test/standalone/empty_env/build.zig +++ b/test/standalone/empty_env/build.zig @@ -15,6 +15,7 @@ pub fn build(b: *std.Build) void { const main = b.addExecutable(.{ .name = "main", .root_source_file = .{ .path = "main.zig" }, + .target = b.host, .optimize = optimize, }); diff --git a/test/standalone/extern/build.zig b/test/standalone/extern/build.zig index 153380e91ded..401deb3dc4d1 100644 --- a/test/standalone/extern/build.zig +++ b/test/standalone/extern/build.zig @@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void { const obj = b.addObject(.{ .name = "exports", .root_source_file = .{ .path = "exports.zig" }, - .target = .{}, + .target = b.host, .optimize = optimize, }); const main = b.addTest(.{ diff --git a/test/standalone/global_linkage/build.zig b/test/standalone/global_linkage/build.zig index d560027265cb..13b7fcfa0e5f 100644 --- a/test/standalone/global_linkage/build.zig +++ b/test/standalone/global_linkage/build.zig @@ -5,7 +5,7 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const optimize: std.builtin.OptimizeMode = .Debug; - const target: std.zig.CrossTarget = .{}; + const target = b.host; const obj1 = b.addStaticLibrary(.{ .name = "obj1", diff --git a/test/standalone/install_raw_hex/build.zig b/test/standalone/install_raw_hex/build.zig index c05490a3e5b9..d1ec55ab5349 100644 --- a/test/standalone/install_raw_hex/build.zig +++ b/test/standalone/install_raw_hex/build.zig @@ -5,12 +5,12 @@ pub fn build(b: *std.Build) void { const test_step = b.step("test", "Test it"); b.default_step = test_step; - const target = .{ + const target = b.resolveTargetQuery(.{ .cpu_arch = .thumb, .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 }, .os_tag = .freestanding, .abi = .gnueabihf, - }; + }); const optimize: std.builtin.OptimizeMode = .Debug; diff --git a/test/standalone/ios/build.zig b/test/standalone/ios/build.zig index 18168688445d..356f12a3d902 100644 --- a/test/standalone/ios/build.zig +++ b/test/standalone/ios/build.zig @@ -8,12 +8,12 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const optimize: std.builtin.OptimizeMode = .Debug; - const target: std.zig.CrossTarget = .{ + const target = b.resolveTargetQuery(.{ .cpu_arch = .aarch64, .os_tag = .ios, - }; - const target_info = std.zig.system.NativeTargetInfo.detect(target) catch @panic("couldn't detect native target"); - const sdk = std.zig.system.darwin.getSdk(b.allocator, target_info.target) orelse @panic("no iOS SDK found"); + }); + const sdk = std.zig.system.darwin.getSdk(b.allocator, target.result) orelse + @panic("no iOS SDK found"); b.sysroot = sdk; const exe = b.addExecutable(.{ diff --git a/test/standalone/issue_11595/build.zig b/test/standalone/issue_11595/build.zig index e69b4bfdd593..c591b3058b5d 100644 --- a/test/standalone/issue_11595/build.zig +++ b/test/standalone/issue_11595/build.zig @@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const optimize: std.builtin.OptimizeMode = .Debug; - const target: std.zig.CrossTarget = .{}; + const target = b.host; if (builtin.os.tag == .windows) { // https://github.com/ziglang/zig/issues/12419 diff --git a/test/standalone/issue_12588/build.zig b/test/standalone/issue_12588/build.zig index e6d01192d3cf..aaf8f6c3149b 100644 --- a/test/standalone/issue_12588/build.zig +++ b/test/standalone/issue_12588/build.zig @@ -5,13 +5,12 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const optimize: std.builtin.OptimizeMode = .Debug; - const target: std.zig.CrossTarget = .{}; const obj = b.addObject(.{ .name = "main", .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, - .target = target, + .target = b.host, }); _ = obj.getEmittedLlvmIr(); _ = obj.getEmittedLlvmBc(); diff --git a/test/standalone/issue_12706/build.zig b/test/standalone/issue_12706/build.zig index fb60ac28a114..9a80dae256fe 100644 --- a/test/standalone/issue_12706/build.zig +++ b/test/standalone/issue_12706/build.zig @@ -1,13 +1,12 @@ const std = @import("std"); const builtin = @import("builtin"); -const CrossTarget = std.zig.CrossTarget; pub fn build(b: *std.Build) void { const test_step = b.step("test", "Test it"); b.default_step = test_step; const optimize: std.builtin.OptimizeMode = .Debug; - const target: std.zig.CrossTarget = .{}; + const target = b.host; const exe = b.addExecutable(.{ .name = "main", diff --git a/test/standalone/issue_339/build.zig b/test/standalone/issue_339/build.zig index d53ba992ea3e..5dc686a77924 100644 --- a/test/standalone/issue_339/build.zig +++ b/test/standalone/issue_339/build.zig @@ -5,7 +5,7 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const optimize: std.builtin.OptimizeMode = .Debug; - const target: std.zig.CrossTarget = .{}; + const target = b.host; const obj = b.addObject(.{ .name = "test", diff --git a/test/standalone/issue_5825/build.zig b/test/standalone/issue_5825/build.zig index a6573175399b..078a9415701b 100644 --- a/test/standalone/issue_5825/build.zig +++ b/test/standalone/issue_5825/build.zig @@ -8,11 +8,11 @@ pub fn build(b: *std.Build) void { // Building for the msvc abi requires a native MSVC installation if (builtin.os.tag != .windows or builtin.cpu.arch != .x86_64) return; - const target = .{ + const target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .msvc, - }; + }); const optimize: std.builtin.OptimizeMode = .Debug; const obj = b.addObject(.{ .name = "issue_5825", diff --git a/test/standalone/issue_8550/build.zig b/test/standalone/issue_8550/build.zig index a7815618744b..557019cfdc04 100644 --- a/test/standalone/issue_8550/build.zig +++ b/test/standalone/issue_8550/build.zig @@ -5,13 +5,13 @@ pub fn build(b: *std.Build) !void { b.default_step = test_step; const optimize: std.builtin.OptimizeMode = .Debug; - const target = std.zig.CrossTarget{ + const target = b.resolveTargetQuery(.{ .os_tag = .freestanding, .cpu_arch = .arm, .cpu_model = .{ .explicit = &std.Target.arm.cpu.arm1176jz_s, }, - }; + }); const kernel = b.addExecutable(.{ .name = "kernel", diff --git a/test/standalone/load_dynamic_library/build.zig b/test/standalone/load_dynamic_library/build.zig index 5dc605e71111..140f276ebe04 100644 --- a/test/standalone/load_dynamic_library/build.zig +++ b/test/standalone/load_dynamic_library/build.zig @@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const optimize: std.builtin.OptimizeMode = .Debug; - const target: std.zig.CrossTarget = .{}; + const target = b.host; if (builtin.os.tag == .wasi) return; diff --git a/test/standalone/main_pkg_path/a/test.zig b/test/standalone/main_pkg_path/a/test.zig deleted file mode 100644 index 286489912b39..000000000000 --- a/test/standalone/main_pkg_path/a/test.zig +++ /dev/null @@ -1,5 +0,0 @@ -const b = @import("../b.zig"); - -test "main pkg path" { - b.foo(); -} diff --git a/test/standalone/main_pkg_path/b.zig b/test/standalone/main_pkg_path/b.zig deleted file mode 100644 index 87d61fa63e88..000000000000 --- a/test/standalone/main_pkg_path/b.zig +++ /dev/null @@ -1 +0,0 @@ -pub fn foo() void {} diff --git a/test/standalone/main_pkg_path/build.zig b/test/standalone/main_pkg_path/build.zig deleted file mode 100644 index d1b67592b5b0..000000000000 --- a/test/standalone/main_pkg_path/build.zig +++ /dev/null @@ -1,13 +0,0 @@ -const std = @import("std"); - -pub fn build(b: *std.Build) void { - const test_step = b.step("test", "Test it"); - b.default_step = test_step; - - const test_exe = b.addTest(.{ - .root_source_file = .{ .path = "a/test.zig" }, - .main_pkg_path = .{ .path = "." }, - }); - - test_step.dependOn(&b.addRunArtifact(test_exe).step); -} diff --git a/test/standalone/mix_c_files/build.zig b/test/standalone/mix_c_files/build.zig index b675be9eebe4..e91f87e13284 100644 --- a/test/standalone/mix_c_files/build.zig +++ b/test/standalone/mix_c_files/build.zig @@ -19,6 +19,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const exe = b.addExecutable(.{ .name = "test", .root_source_file = .{ .path = "main.zig" }, + .target = b.host, .optimize = optimize, }); exe.addCSourceFile(.{ .file = .{ .path = "test.c" }, .flags = &[_][]const u8{"-std=c11"} }); diff --git a/test/standalone/mix_o_files/build.zig b/test/standalone/mix_o_files/build.zig index 980e52ad3baa..aa648145a831 100644 --- a/test/standalone/mix_o_files/build.zig +++ b/test/standalone/mix_o_files/build.zig @@ -5,7 +5,7 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const optimize: std.builtin.OptimizeMode = .Debug; - const target: std.zig.CrossTarget = .{}; + const target = b.host; const obj = b.addObject(.{ .name = "base64", diff --git a/test/standalone/options/build.zig b/test/standalone/options/build.zig index d87610ba220a..97cf91c1fbc3 100644 --- a/test/standalone/options/build.zig +++ b/test/standalone/options/build.zig @@ -3,7 +3,7 @@ const std = @import("std"); pub fn build(b: *std.Build) void { const main = b.addTest(.{ .root_source_file = .{ .path = "src/main.zig" }, - .target = .{}, + .target = b.host, .optimize = .Debug, }); diff --git a/test/standalone/pie/build.zig b/test/standalone/pie/build.zig index 1efcd5c26e87..2e9f99ff4b67 100644 --- a/test/standalone/pie/build.zig +++ b/test/standalone/pie/build.zig @@ -5,10 +5,10 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const optimize: std.builtin.OptimizeMode = .Debug; - const target: std.zig.CrossTarget = .{ + const target = b.resolveTargetQuery(.{ .os_tag = .linux, .cpu_arch = .x86_64, - }; + }); const main = b.addTest(.{ .root_source_file = .{ .path = "main.zig" }, diff --git a/test/standalone/pkg_import/build.zig b/test/standalone/pkg_import/build.zig index 30f4b2ede49b..207f924f9be9 100644 --- a/test/standalone/pkg_import/build.zig +++ b/test/standalone/pkg_import/build.zig @@ -10,8 +10,9 @@ pub fn build(b: *std.Build) void { .name = "test", .root_source_file = .{ .path = "test.zig" }, .optimize = optimize, + .target = b.host, }); - exe.addAnonymousModule("my_pkg", .{ .source_file = .{ .path = "pkg.zig" } }); + exe.root_module.addAnonymousImport("my_pkg", .{ .root_source_file = .{ .path = "pkg.zig" } }); const run = b.addRunArtifact(exe); test_step.dependOn(&run.step); diff --git a/test/standalone/self_exe_symlink/build.zig b/test/standalone/self_exe_symlink/build.zig index 10e0a6a5127d..d61d50257433 100644 --- a/test/standalone/self_exe_symlink/build.zig +++ b/test/standalone/self_exe_symlink/build.zig @@ -7,11 +7,11 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const optimize: std.builtin.OptimizeMode = .Debug; - const target: std.zig.CrossTarget = .{}; + const target = b.host; // The test requires getFdPath in order to to get the path of the // File returned by openSelfExe - if (!std.os.isGetFdPathSupportedOnTarget(target.getOs())) return; + if (!std.os.isGetFdPathSupportedOnTarget(target.result.os)) return; const main = b.addExecutable(.{ .name = "main", diff --git a/test/standalone/shared_library/build.zig b/test/standalone/shared_library/build.zig index 4b4a154225c4..4d409295e866 100644 --- a/test/standalone/shared_library/build.zig +++ b/test/standalone/shared_library/build.zig @@ -10,7 +10,7 @@ pub fn build(b: *std.Build) void { } const optimize: std.builtin.OptimizeMode = .Debug; - const target: std.zig.CrossTarget = .{}; + const target = b.host; const lib = b.addSharedLibrary(.{ .name = "mathtest", .root_source_file = .{ .path = "mathtest.zig" }, diff --git a/test/standalone/stack_iterator/build.zig b/test/standalone/stack_iterator/build.zig index 628210452c42..463a533ac400 100644 --- a/test/standalone/stack_iterator/build.zig +++ b/test/standalone/stack_iterator/build.zig @@ -22,11 +22,10 @@ pub fn build(b: *std.Build) void { .root_source_file = .{ .path = "unwind.zig" }, .target = target, .optimize = optimize, + .unwind_tables = if (target.result.isDarwin()) true else null, + .omit_frame_pointer = false, }); - if (target.isDarwin()) exe.unwind_tables = true; - exe.omit_frame_pointer = false; - const run_cmd = b.addRunArtifact(exe); test_step.dependOn(&run_cmd.step); } @@ -46,11 +45,10 @@ pub fn build(b: *std.Build) void { .root_source_file = .{ .path = "unwind.zig" }, .target = target, .optimize = optimize, + .unwind_tables = true, + .omit_frame_pointer = true, }); - exe.omit_frame_pointer = true; - exe.unwind_tables = true; - const run_cmd = b.addRunArtifact(exe); test_step.dependOn(&run_cmd.step); } @@ -69,11 +67,12 @@ pub fn build(b: *std.Build) void { .name = "c_shared_lib", .target = target, .optimize = optimize, + .strip = false, }); - if (target.isWindows()) c_shared_lib.defineCMacro("LIB_API", "__declspec(dllexport)"); + if (target.result.os.tag == .windows) + c_shared_lib.defineCMacro("LIB_API", "__declspec(dllexport)"); - c_shared_lib.strip = false; c_shared_lib.addCSourceFile(.{ .file = .{ .path = "shared_lib.c" }, .flags = &.{"-fomit-frame-pointer"}, @@ -85,10 +84,10 @@ pub fn build(b: *std.Build) void { .root_source_file = .{ .path = "shared_lib_unwind.zig" }, .target = target, .optimize = optimize, + .unwind_tables = if (target.result.isDarwin()) true else null, + .omit_frame_pointer = true, }); - if (target.isDarwin()) exe.unwind_tables = true; - exe.omit_frame_pointer = true; exe.linkLibrary(c_shared_lib); const run_cmd = b.addRunArtifact(exe); diff --git a/test/standalone/static_c_lib/build.zig b/test/standalone/static_c_lib/build.zig index 1e3e80c36323..244107b0f1e1 100644 --- a/test/standalone/static_c_lib/build.zig +++ b/test/standalone/static_c_lib/build.zig @@ -9,7 +9,7 @@ pub fn build(b: *std.Build) void { const foo = b.addStaticLibrary(.{ .name = "foo", .optimize = optimize, - .target = .{}, + .target = b.host, }); foo.addCSourceFile(.{ .file = .{ .path = "foo.c" }, .flags = &[_][]const u8{} }); foo.addIncludePath(.{ .path = "." }); diff --git a/test/standalone/strip_empty_loop/build.zig b/test/standalone/strip_empty_loop/build.zig index e64781d99142..4875bd9128c7 100644 --- a/test/standalone/strip_empty_loop/build.zig +++ b/test/standalone/strip_empty_loop/build.zig @@ -5,15 +5,15 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const optimize = std.builtin.OptimizeMode.Debug; - const target = std.zig.CrossTarget{}; + const target = b.host; const main = b.addExecutable(.{ .name = "main", .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, .target = target, + .strip = true, }); - main.strip = true; // TODO: actually check the output _ = main.getEmittedBin(); diff --git a/test/standalone/strip_struct_init/build.zig b/test/standalone/strip_struct_init/build.zig index b67da4681905..2d903f973a1f 100644 --- a/test/standalone/strip_struct_init/build.zig +++ b/test/standalone/strip_struct_init/build.zig @@ -9,8 +9,8 @@ pub fn build(b: *std.Build) void { const main = b.addTest(.{ .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, + .strip = true, }); - main.strip = true; test_step.dependOn(&b.addRunArtifact(main).step); } diff --git a/test/standalone/test_runner_module_imports/build.zig b/test/standalone/test_runner_module_imports/build.zig index f27771889827..af8e68d71789 100644 --- a/test/standalone/test_runner_module_imports/build.zig +++ b/test/standalone/test_runner_module_imports/build.zig @@ -6,13 +6,13 @@ pub fn build(b: *std.Build) void { .test_runner = "test_runner/main.zig", }); - const module1 = b.createModule(.{ .source_file = .{ .path = "module1/main.zig" } }); + const module1 = b.createModule(.{ .root_source_file = .{ .path = "module1/main.zig" } }); const module2 = b.createModule(.{ - .source_file = .{ .path = "module2/main.zig" }, - .dependencies = &.{.{ .name = "module1", .module = module1 }}, + .root_source_file = .{ .path = "module2/main.zig" }, + .imports = &.{.{ .name = "module1", .module = module1 }}, }); - t.addModule("module2", module2); + t.root_module.addImport("module2", module2); const test_step = b.step("test", "Run unit tests"); test_step.dependOn(&b.addRunArtifact(t).step); diff --git a/test/standalone/windows_resources/build.zig b/test/standalone/windows_resources/build.zig index 9476fa7839db..51117515715a 100644 --- a/test/standalone/windows_resources/build.zig +++ b/test/standalone/windows_resources/build.zig @@ -4,21 +4,25 @@ pub fn build(b: *std.Build) void { const test_step = b.step("test", "Test it"); b.default_step = test_step; - const native_target: std.zig.CrossTarget = .{}; - const cross_target = .{ + const target = b.resolveTargetQuery(.{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .gnu, - }; + }); - add(b, native_target, .any, test_step); - add(b, cross_target, .any, test_step); + add(b, b.host, .any, test_step); + add(b, target, .any, test_step); - add(b, native_target, .gnu, test_step); - add(b, cross_target, .gnu, test_step); + add(b, b.host, .gnu, test_step); + add(b, target, .gnu, test_step); } -fn add(b: *std.Build, target: std.zig.CrossTarget, rc_includes: enum { any, gnu }, test_step: *std.Build.Step) void { +fn add( + b: *std.Build, + target: std.Build.ResolvedTarget, + rc_includes: enum { any, gnu }, + test_step: *std.Build.Step, +) void { const exe = b.addExecutable(.{ .name = "zig_resource_test", .root_source_file = .{ .path = "main.zig" }, diff --git a/test/standalone/windows_spawn/build.zig b/test/standalone/windows_spawn/build.zig index 6c865f0a9fc1..a52cbd7202a3 100644 --- a/test/standalone/windows_spawn/build.zig +++ b/test/standalone/windows_spawn/build.zig @@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const optimize: std.builtin.OptimizeMode = .Debug; - const target: std.zig.CrossTarget = .{}; + const target = b.host; if (builtin.os.tag != .windows) return; diff --git a/test/standalone/zerolength_check/build.zig b/test/standalone/zerolength_check/build.zig index 4e5c1937f733..dacfc841c1a7 100644 --- a/test/standalone/zerolength_check/build.zig +++ b/test/standalone/zerolength_check/build.zig @@ -13,11 +13,11 @@ pub fn build(b: *std.Build) void { fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const unit_tests = b.addTest(.{ .root_source_file = .{ .path = "src/main.zig" }, - .target = .{ + .target = b.resolveTargetQuery(.{ .os_tag = .wasi, .cpu_arch = .wasm32, .cpu_features_add = std.Target.wasm.featureSet(&.{.bulk_memory}), - }, + }), .optimize = optimize, }); diff --git a/test/tests.zig b/test/tests.zig index 808ffad6ee17..b2fb1e4bca9c 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -1,7 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); const assert = std.debug.assert; -const CrossTarget = std.zig.CrossTarget; const mem = std.mem; const OptimizeMode = std.builtin.OptimizeMode; const Step = std.Build.Step; @@ -22,13 +21,13 @@ pub const CompareOutputContext = @import("src/CompareOutput.zig"); pub const StackTracesContext = @import("src/StackTrace.zig"); const TestTarget = struct { - target: CrossTarget = .{}, + target: std.Target.Query = .{}, optimize_mode: std.builtin.OptimizeMode = .Debug, link_libc: ?bool = null, single_threaded: ?bool = null, use_llvm: ?bool = null, use_lld: ?bool = null, - force_pic: ?bool = null, + pic: ?bool = null, strip: ?bool = null, }; @@ -105,7 +104,7 @@ const test_targets = blk: { }, .use_llvm = false, .use_lld = false, - .force_pic = true, + .pic = true, }, .{ .target = .{ @@ -146,7 +145,7 @@ const test_targets = blk: { //}, // https://github.com/ziglang/zig/issues/13623 //.{ - // .target = CrossTarget.parse(.{ + // .target = std.Target.Query.parse(.{ // .arch_os_abi = "arm-linux-none", // .cpu_features = "generic+v8a", // }) catch unreachable, @@ -287,13 +286,13 @@ const test_targets = blk: { }, .{ - .target = CrossTarget.parse(.{ + .target = std.Target.Query.parse(.{ .arch_os_abi = "arm-linux-none", .cpu_features = "generic+v8a", }) catch unreachable, }, .{ - .target = CrossTarget.parse(.{ + .target = std.Target.Query.parse(.{ .arch_os_abi = "arm-linux-musleabihf", .cpu_features = "generic+v8a", }) catch unreachable, @@ -301,7 +300,7 @@ const test_targets = blk: { }, // https://github.com/ziglang/zig/issues/3287 //.{ - // .target = CrossTarget.parse(.{ + // .target = std.Target.Query.parse(.{ // .arch_os_abi = "arm-linux-gnueabihf", // .cpu_features = "generic+v8a", // }) catch unreachable, @@ -495,10 +494,10 @@ const test_targets = blk: { }; const CAbiTarget = struct { - target: CrossTarget = .{}, + target: std.Target.Query = .{}, use_llvm: ?bool = null, use_lld: ?bool = null, - force_pic: ?bool = null, + pic: ?bool = null, strip: ?bool = null, c_defines: []const []const u8 = &.{}, }; @@ -543,7 +542,7 @@ const c_abi_targets = [_]CAbiTarget{ }, .use_llvm = false, .use_lld = false, - .force_pic = true, + .pic = true, .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, }, .{ @@ -645,7 +644,7 @@ pub fn addStackTraceTests( const check_exe = b.addExecutable(.{ .name = "check-stack-trace", .root_source_file = .{ .path = "test/src/check-stack-trace.zig" }, - .target = .{}, + .target = b.host, .optimize = .Debug, }); @@ -682,12 +681,14 @@ pub fn addStandaloneTests( if (os_tag != builtin.os.tag) continue; } + const resolved_target = b.resolveTargetQuery(case.target); + if (case.is_exe) { const exe = b.addExecutable(.{ .name = std.fs.path.stem(case.src_path), .root_source_file = .{ .path = case.src_path }, .optimize = optimize, - .target = case.target, + .target = resolved_target, }); if (case.link_libc) exe.linkLibC(); @@ -701,7 +702,7 @@ pub fn addStandaloneTests( .name = std.fs.path.stem(case.src_path), .root_source_file = .{ .path = case.src_path }, .optimize = optimize, - .target = case.target, + .target = resolved_target, }); if (case.link_libc) exe.linkLibC(); @@ -1001,7 +1002,7 @@ pub fn addTranslateCTests(b: *std.Build, test_filter: ?[]const u8) *Step { pub fn addRunTranslatedCTests( b: *std.Build, test_filter: ?[]const u8, - target: std.zig.CrossTarget, + target: std.Build.ResolvedTarget, ) *Step { const cases = b.allocator.create(RunTranslatedCContext) catch @panic("OOM"); cases.* = .{ @@ -1035,14 +1036,17 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { for (test_targets) |test_target| { const is_native = test_target.target.isNative() or - (test_target.target.getOsTag() == builtin.os.tag and - test_target.target.getCpuArch() == builtin.cpu.arch); + (test_target.target.os_tag == builtin.os.tag and + test_target.target.cpu_arch == builtin.cpu.arch); if (options.skip_non_native and !is_native) continue; + const resolved_target = b.resolveTargetQuery(test_target.target); + const target = resolved_target.result; + if (options.skip_cross_glibc and !test_target.target.isNative() and - test_target.target.isGnuLibC() and test_target.link_libc == true) + target.isGnuLibC() and test_target.link_libc == true) continue; if (options.skip_libc and test_target.link_libc == true) @@ -1052,35 +1056,30 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { continue; // TODO get compiler-rt tests passing for self-hosted backends. - if ((test_target.target.getCpuArch() != .x86_64 or - test_target.target.getObjectFormat() != .elf) and + if ((target.cpu.arch != .x86_64 or target.ofmt != .elf) and test_target.use_llvm == false and mem.eql(u8, options.name, "compiler-rt")) continue; // TODO get compiler-rt tests passing for wasm32-wasi // currently causes "LLVM ERROR: Unable to expand fixed point multiplication." - if (test_target.target.getCpuArch() == .wasm32 and - test_target.target.getOsTag() == .wasi and + if (target.cpu.arch == .wasm32 and target.os.tag == .wasi and mem.eql(u8, options.name, "compiler-rt")) { continue; } // TODO get universal-libc tests passing for other self-hosted backends. - if (test_target.target.getCpuArch() != .x86_64 and + if (target.cpu.arch != .x86_64 and test_target.use_llvm == false and mem.eql(u8, options.name, "universal-libc")) continue; // TODO get std lib tests passing for other self-hosted backends. - if ((test_target.target.getCpuArch() != .x86_64 or - test_target.target.getOsTag() != .linux) and + if ((target.cpu.arch != .x86_64 or target.os.tag != .linux) and test_target.use_llvm == false and mem.eql(u8, options.name, "std")) continue; - if (test_target.target.getCpuArch() == .x86_64 and - test_target.target.getOsTag() == .windows and - test_target.target.cpu_arch == null and - test_target.optimize_mode != .Debug and + if (target.cpu.arch == .x86_64 and target.os.tag == .windows and + test_target.target.cpu_arch == null and test_target.optimize_mode != .Debug and mem.eql(u8, options.name, "std")) { // https://github.com/ziglang/zig/issues/17902 @@ -1093,11 +1092,11 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { if (!want_this_mode) continue; const libc_suffix = if (test_target.link_libc == true) "-libc" else ""; - const triple_txt = test_target.target.zigTriple(b.allocator) catch @panic("OOM"); - const model_txt = test_target.target.getCpuModel().name; + const triple_txt = target.zigTriple(b.allocator) catch @panic("OOM"); + const model_txt = target.cpu.model.name; // wasm32-wasi builds need more RAM, idk why - const max_rss = if (test_target.target.getOs().tag == .wasi) + const max_rss = if (target.os.tag == .wasi) options.max_rss * 2 else options.max_rss; @@ -1105,7 +1104,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { const these_tests = b.addTest(.{ .root_source_file = .{ .path = options.root_src }, .optimize = test_target.optimize_mode, - .target = test_target.target, + .target = resolved_target, .max_rss = max_rss, .filter = options.test_filter, .link_libc = test_target.link_libc, @@ -1113,24 +1112,24 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { .use_llvm = test_target.use_llvm, .use_lld = test_target.use_lld, .zig_lib_dir = .{ .path = "lib" }, + .pic = test_target.pic, + .strip = test_target.strip, }); - these_tests.force_pic = test_target.force_pic; - these_tests.strip = test_target.strip; const single_threaded_suffix = if (test_target.single_threaded == true) "-single" else ""; const backend_suffix = if (test_target.use_llvm == true) "-llvm" - else if (test_target.target.ofmt == std.Target.ObjectFormat.c) + else if (target.ofmt == std.Target.ObjectFormat.c) "-cbe" else if (test_target.use_llvm == false) "-selfhosted" else ""; const use_lld = if (test_target.use_lld == false) "-no-lld" else ""; - const use_pic = if (test_target.force_pic == true) "-pic" else ""; + const use_pic = if (test_target.pic == true) "-pic" else ""; these_tests.addIncludePath(.{ .path = "test" }); - if (test_target.target.getOs().tag == .wasi) { + if (target.os.tag == .wasi) { // WASI's default stack size can be too small for some big tests. these_tests.stack_size = 2 * 1024 * 1024; } @@ -1147,14 +1146,14 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { use_pic, }); - if (test_target.target.ofmt == std.Target.ObjectFormat.c) { - var altered_target = test_target.target; - altered_target.ofmt = null; + if (target.ofmt == std.Target.ObjectFormat.c) { + var altered_query = test_target.target; + altered_query.ofmt = null; const compile_c = b.addExecutable(.{ .name = qualified_name, .link_libc = test_target.link_libc, - .target = altered_target, + .target = b.resolveTargetQuery(altered_query), .zig_lib_dir = .{ .path = "lib" }, }); compile_c.addCSourceFile(.{ @@ -1178,7 +1177,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { }, }); compile_c.addIncludePath(.{ .path = "lib" }); // for zig.h - if (test_target.target.getOsTag() == .windows) { + if (target.os.tag == .windows) { if (true) { // Unfortunately this requires about 8G of RAM for clang to compile // and our Windows CI runners do not have this much. @@ -1229,41 +1228,39 @@ pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *S for (c_abi_targets) |c_abi_target| { if (skip_non_native and !c_abi_target.target.isNative()) continue; - if (c_abi_target.target.isWindows() and c_abi_target.target.getCpuArch() == .aarch64) { + const resolved_target = b.resolveTargetQuery(c_abi_target.target); + const target = resolved_target.result; + + if (target.os.tag == .windows and target.cpu.arch == .aarch64) { // https://github.com/ziglang/zig/issues/14908 continue; } const test_step = b.addTest(.{ .name = b.fmt("test-c-abi-{s}-{s}-{s}{s}{s}{s}", .{ - c_abi_target.target.zigTriple(b.allocator) catch @panic("OOM"), - c_abi_target.target.getCpuModel().name, + target.zigTriple(b.allocator) catch @panic("OOM"), + target.cpu.model.name, @tagName(optimize_mode), if (c_abi_target.use_llvm == true) "-llvm" - else if (c_abi_target.target.ofmt == std.Target.ObjectFormat.c) + else if (target.ofmt == .c) "-cbe" else if (c_abi_target.use_llvm == false) "-selfhosted" else "", if (c_abi_target.use_lld == false) "-no-lld" else "", - if (c_abi_target.force_pic == true) "-pic" else "", + if (c_abi_target.pic == true) "-pic" else "", }), .root_source_file = .{ .path = "test/c_abi/main.zig" }, - .target = c_abi_target.target, + .target = resolved_target, .optimize = optimize_mode, .link_libc = true, .use_llvm = c_abi_target.use_llvm, .use_lld = c_abi_target.use_lld, + .pic = c_abi_target.pic, + .strip = c_abi_target.strip, }); - test_step.force_pic = c_abi_target.force_pic; - test_step.strip = c_abi_target.strip; - if (c_abi_target.target.abi != null and c_abi_target.target.abi.?.isMusl()) { - // TODO NativeTargetInfo insists on dynamically linking musl - // for some reason? - test_step.target_info.dynamic_linker.max_byte = null; - } test_step.addCSourceFile(.{ .file = .{ .path = "test/c_abi/cfuncs.c" }, .flags = &.{"-std=c99"}, @@ -1297,8 +1294,8 @@ pub fn addCases( var dir = try b.build_root.handle.openDir("test/cases", .{ .iterate = true }); defer dir.close(); - cases.addFromDir(dir); - try @import("cases.zig").addCases(&cases, build_options); + cases.addFromDir(dir, b); + try @import("cases.zig").addCases(&cases, build_options, b); const cases_dir_path = try b.build_root.join(b.allocator, &.{ "test", "cases" }); cases.lowerToBuildSteps( diff --git a/test/translate_c.zig b/test/translate_c.zig index 2b87da406755..a3908108306f 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1,7 +1,6 @@ const std = @import("std"); const builtin = @import("builtin"); const tests = @import("tests.zig"); -const CrossTarget = std.zig.CrossTarget; // ******************************************************** // * * @@ -1846,7 +1845,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub extern fn foo5(a: [*c]f32) callconv(.Thiscall) void; }); - cases.addWithTarget("Calling convention", CrossTarget.parse(.{ + cases.addWithTarget("Calling convention", std.Target.Query.parse(.{ .arch_os_abi = "arm-linux-none", .cpu_features = "generic+v8_5a", }) catch unreachable, @@ -1857,7 +1856,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub extern fn foo2(a: [*c]f32) callconv(.AAPCSVFP) void; }); - cases.addWithTarget("Calling convention", CrossTarget.parse(.{ + cases.addWithTarget("Calling convention", std.Target.Query.parse(.{ .arch_os_abi = "aarch64-linux-none", .cpu_features = "generic+v8_5a", }) catch unreachable, diff --git a/tools/docgen.zig b/tools/docgen.zig index 669f6bca8d5d..1003db0b5ac2 100644 --- a/tools/docgen.zig +++ b/tools/docgen.zig @@ -9,13 +9,12 @@ const print = std.debug.print; const mem = std.mem; const testing = std.testing; const Allocator = std.mem.Allocator; +const getExternalExecutor = std.zig.system.getExternalExecutor; const max_doc_file_size = 10 * 1024 * 1024; -const exe_ext = @as(std.zig.CrossTarget, .{}).exeFileExt(); const obj_ext = builtin.object_format.fileExt(builtin.cpu.arch); const tmp_dir_name = "docgen_tmp"; -const test_out_path = tmp_dir_name ++ fs.path.sep_str ++ "test" ++ exe_ext; const usage = \\Usage: docgen [--zig] [--skip-code-tests] input output" @@ -1309,7 +1308,7 @@ fn genHtml( var env_map = try process.getEnvMap(allocator); try env_map.put("YES_COLOR", "1"); - const host = try std.zig.system.NativeTargetInfo.detect(.{}); + const host = try std.zig.system.resolveTargetQuery(.{}); const builtin_code = try getBuiltinCode(allocator, &env_map, zig_exe, opt_zig_lib_dir); for (toc.nodes) |node| { @@ -1424,9 +1423,7 @@ fn genHtml( try build_args.append("-lc"); try shell_out.print("-lc ", .{}); } - const target = try std.zig.CrossTarget.parse(.{ - .arch_os_abi = code.target_str orelse "native", - }); + if (code.target_str) |triple| { try build_args.appendSlice(&[_][]const u8{ "-target", triple }); try shell_out.print("-target {s} ", .{triple}); @@ -1490,9 +1487,13 @@ fn genHtml( } } + const target_query = try std.Target.Query.parse(.{ + .arch_os_abi = code.target_str orelse "native", + }); + const target = try std.zig.system.resolveTargetQuery(target_query); + const path_to_exe = try std.fmt.allocPrint(allocator, "./{s}{s}", .{ - code.name, - target.exeFileExt(), + code.name, target.exeFileExt(), }); const run_args = &[_][]const u8{path_to_exe}; @@ -1565,13 +1566,13 @@ fn genHtml( try test_args.appendSlice(&[_][]const u8{ "-target", triple }); try shell_out.print("-target {s} ", .{triple}); - const cross_target = try std.zig.CrossTarget.parse(.{ + const target_query = try std.Target.Query.parse(.{ .arch_os_abi = triple, }); - const target_info = try std.zig.system.NativeTargetInfo.detect( - cross_target, + const target = try std.zig.system.resolveTargetQuery( + target_query, ); - switch (host.getExternalExecutor(&target_info, .{ + switch (getExternalExecutor(host, &target, .{ .link_libc = code.link_libc, })) { .native => {},