commit 289ae45c1b58e952867c4fa1e246d0ef7bc2ff64 (tree)
parent c89bb3e141ee215add0b52930d48bffd8dae8342
Author: Andrew Kelley <andrew@ziglang.org>
Date: Tue, 2 Jan 2024 14:11:27 -0800
Merge pull request #18160 from ziglang/std-build-module
Move many settings from being per-Compilation to being per-Module
Diffstat:
236 files changed, 17665 insertions(+), 15265 deletions(-)
diff --git 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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");
},
@@ -633,45 +601,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,
});
}
@@ -679,77 +659,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,
});
}
@@ -757,44 +759,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,
@@ -802,38 +815,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,
@@ -843,9 +867,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,
});
@@ -856,41 +881,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
@@ -918,10 +919,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;
}
@@ -1143,7 +1140,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) });
@@ -1208,19 +1205,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;
@@ -1228,8 +1231,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,
@@ -1242,7 +1245,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 => {
@@ -1257,7 +1260,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 => {
@@ -1266,83 +1269,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 {
@@ -1422,7 +1380,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,
@@ -1490,7 +1448,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);
}
@@ -1536,7 +1494,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);
}
@@ -1593,7 +1551,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)) {
@@ -1895,15 +1853,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 {
@@ -2048,34 +1997,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,
@@ -2107,34 +2028,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
@@ -2165,6 +2058,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
@@ -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
@@ -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
@@ -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
@@ -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/<SHA256 hash of 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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/Target/Query.zig b/lib/std/Target/Query.zig
@@ -0,0 +1,777 @@
+//! 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,
+
+cpu_model: CpuModel = CpuModel.determined_by_cpu_arch,
+
+/// Sparse set of CPU features to add to the set from `cpu_model`.
+cpu_features_add: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
+
+/// Sparse set of CPU features to remove from the set from `cpu_model`.
+cpu_features_sub: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
+
+/// `null` means native.
+os_tag: ?Target.Os.Tag = null,
+
+/// `null` means the default version range for `os_tag`. If `os_tag` is `null` (native)
+/// then `null` for this field means native.
+os_version_min: ?OsVersion = null,
+
+/// When cross compiling, `null` means default (latest known OS version).
+/// When `os_tag` is native, `null` means equal to the native OS version.
+os_version_max: ?OsVersion = null,
+
+/// `null` means default when cross compiling, or native when os_tag is native.
+/// If `isGnuLibC()` is `false`, this must be `null` and is ignored.
+glibc_version: ?SemanticVersion = null,
+
+/// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI.
+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: Target.DynamicLinker = Target.DynamicLinker.none,
+
+/// `null` means default for the cpu/arch/os combo.
+ofmt: ?Target.ObjectFormat = null,
+
+pub const CpuModel = union(enum) {
+ /// Always native
+ native,
+
+ /// Always baseline
+ baseline,
+
+ /// If CPU Architecture is native, then the CPU model will be native. Otherwise,
+ /// it will be baseline.
+ 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 fn fromTarget(target: Target) Query {
+ var result: Query = .{
+ .cpu_arch = target.cpu.arch,
+ .cpu_model = .{ .explicit = target.cpu.model },
+ .os_tag = target.os.tag,
+ .os_version_min = undefined,
+ .os_version_max = undefined,
+ .abi = target.abi,
+ .glibc_version = if (target.isGnuLibC())
+ target.os.version_range.linux.glibc
+ else
+ null,
+ };
+ result.updateOsVersionRange(target.os);
+
+ const all_features = target.cpu.arch.allFeaturesList();
+ var cpu_model_set = target.cpu.model.features;
+ cpu_model_set.populateDependencies(all_features);
+ {
+ // The "add" set is the full set with the CPU Model set removed.
+ const add_set = &result.cpu_features_add;
+ add_set.* = target.cpu.features;
+ add_set.removeFeatureSet(cpu_model_set);
+ }
+ {
+ // The "sub" set is the features that are on in CPU Model set and off in the full set.
+ const sub_set = &result.cpu_features_sub;
+ sub_set.* = cpu_model_set;
+ sub_set.removeFeatureSet(target.cpu.features);
+ }
+ return result;
+}
+
+fn updateOsVersionRange(self: *Query, os: Target.Os) void {
+ switch (os.tag) {
+ .freestanding,
+ .ananas,
+ .cloudabi,
+ .fuchsia,
+ .kfreebsd,
+ .lv2,
+ .solaris,
+ .illumos,
+ .zos,
+ .haiku,
+ .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,
+ => {
+ self.os_version_min = .{ .none = {} };
+ self.os_version_max = .{ .none = {} };
+ },
+
+ .freebsd,
+ .macos,
+ .ios,
+ .tvos,
+ .watchos,
+ .netbsd,
+ .openbsd,
+ .dragonfly,
+ => {
+ self.os_version_min = .{ .semver = os.version_range.semver.min };
+ self.os_version_max = .{ .semver = os.version_range.semver.max };
+ },
+
+ .linux => {
+ self.os_version_min = .{ .semver = os.version_range.linux.range.min };
+ self.os_version_max = .{ .semver = os.version_range.linux.range.max };
+ },
+
+ .windows => {
+ self.os_version_min = .{ .windows = os.version_range.windows.min };
+ self.os_version_max = .{ .windows = os.version_range.windows.max };
+ },
+ }
+}
+
+pub const ParseOptions = struct {
+ /// This is sometimes called a "triple". It looks roughly like this:
+ /// riscv64-linux-musl
+ /// The fields are, respectively:
+ /// * CPU Architecture
+ /// * Operating System (and optional version range)
+ /// * C ABI (optional, with optional glibc version)
+ /// The string "native" can be used for CPU architecture as well as Operating System.
+ /// If the CPU Architecture is specified as "native", then the Operating System and C ABI may be omitted.
+ arch_os_abi: []const u8 = "native",
+
+ /// Looks like "name+a+b-c-d+e", where "name" is a CPU Model name, "a", "b", and "e"
+ /// are examples of CPU features to add to the set, and "c" and "d" are examples of CPU features
+ /// to remove from the set.
+ /// The following special strings are recognized for CPU Model name:
+ /// * "baseline" - The "default" set of CPU features for cross-compiling. A conservative set
+ /// of features that is expected to be supported on most available hardware.
+ /// * "native" - The native CPU model is to be detected when compiling.
+ /// If this field is not provided (`null`), then the value will depend on the
+ /// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline".
+ cpu_features: ?[]const u8 = null,
+
+ /// Absolute path to dynamic linker, to override the default, which is either a natively
+ /// detected path, or a standard path.
+ dynamic_linker: ?[]const u8 = null,
+
+ object_format: ?[]const u8 = null,
+
+ /// If this is provided, the function will populate some information about parsing failures,
+ /// so that user-friendly error messages can be delivered.
+ diagnostics: ?*Diagnostics = null,
+
+ pub const Diagnostics = struct {
+ /// If the architecture was determined, this will be populated.
+ arch: ?Target.Cpu.Arch = null,
+
+ /// If the OS name was determined, this will be populated.
+ os_name: ?[]const u8 = null,
+
+ /// If the OS tag was determined, this will be populated.
+ os_tag: ?Target.Os.Tag = null,
+
+ /// If the ABI was determined, this will be populated.
+ abi: ?Target.Abi = null,
+
+ /// If the CPU name was determined, this will be populated.
+ cpu_name: ?[]const u8 = null,
+
+ /// If error.UnknownCpuFeature is returned, this will be populated.
+ unknown_feature_name: ?[]const u8 = null,
+ };
+};
+
+pub fn parse(args: ParseOptions) !Query {
+ var dummy_diags: ParseOptions.Diagnostics = undefined;
+ const diags = args.diagnostics orelse &dummy_diags;
+
+ var result: Query = .{
+ .dynamic_linker = Target.DynamicLinker.init(args.dynamic_linker),
+ };
+
+ var it = mem.splitScalar(u8, args.arch_os_abi, '-');
+ const arch_name = it.first();
+ const arch_is_native = mem.eql(u8, arch_name, "native");
+ if (!arch_is_native) {
+ result.cpu_arch = std.meta.stringToEnum(Target.Cpu.Arch, arch_name) orelse
+ return error.UnknownArchitecture;
+ }
+ const arch = result.cpu_arch orelse builtin.cpu.arch;
+ diags.arch = arch;
+
+ if (it.next()) |os_text| {
+ try parseOs(&result, diags, os_text);
+ } else if (!arch_is_native) {
+ return error.MissingOperatingSystem;
+ }
+
+ const opt_abi_text = it.next();
+ if (opt_abi_text) |abi_text| {
+ var abi_it = mem.splitScalar(u8, abi_text, '.');
+ const abi = std.meta.stringToEnum(Target.Abi, abi_it.first()) orelse
+ return error.UnknownApplicationBinaryInterface;
+ result.abi = abi;
+ diags.abi = abi;
+
+ const abi_ver_text = abi_it.rest();
+ if (abi_it.next() != null) {
+ 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,
+ };
+ } else {
+ return error.InvalidAbiVersion;
+ }
+ }
+ }
+
+ if (it.next() != null) return error.UnexpectedExtraField;
+
+ if (args.cpu_features) |cpu_features| {
+ const all_features = arch.allFeaturesList();
+ var index: usize = 0;
+ while (index < cpu_features.len and
+ cpu_features[index] != '+' and
+ cpu_features[index] != '-')
+ {
+ index += 1;
+ }
+ const cpu_name = cpu_features[0..index];
+ diags.cpu_name = cpu_name;
+
+ const add_set = &result.cpu_features_add;
+ const sub_set = &result.cpu_features_sub;
+ if (mem.eql(u8, cpu_name, "native")) {
+ result.cpu_model = .native;
+ } else if (mem.eql(u8, cpu_name, "baseline")) {
+ result.cpu_model = .baseline;
+ } else {
+ result.cpu_model = .{ .explicit = try arch.parseCpuModel(cpu_name) };
+ }
+
+ while (index < cpu_features.len) {
+ const op = cpu_features[index];
+ const set = switch (op) {
+ '+' => add_set,
+ '-' => sub_set,
+ else => unreachable,
+ };
+ index += 1;
+ const start = index;
+ while (index < cpu_features.len and
+ cpu_features[index] != '+' and
+ cpu_features[index] != '-')
+ {
+ index += 1;
+ }
+ const feature_name = cpu_features[start..index];
+ for (all_features, 0..) |feature, feat_index_usize| {
+ const feat_index = @as(Target.Cpu.Feature.Set.Index, @intCast(feat_index_usize));
+ if (mem.eql(u8, feature_name, feature.name)) {
+ set.addFeature(feat_index);
+ break;
+ }
+ } else {
+ diags.unknown_feature_name = feature_name;
+ return error.UnknownCpuFeature;
+ }
+ }
+ }
+
+ if (args.object_format) |ofmt_name| {
+ result.ofmt = std.meta.stringToEnum(Target.ObjectFormat, ofmt_name) orelse
+ return error.UnknownObjectFormat;
+ }
+
+ return result;
+}
+
+/// 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 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, '-');
+ const arch_name = it.first();
+ const arch_is_native = mem.eql(u8, arch_name, "native");
+ if (arch_is_native) {
+ return builtin.cpu.arch;
+ } else {
+ return std.meta.stringToEnum(Target.Cpu.Arch, arch_name);
+ }
+}
+
+/// Similar to `SemanticVersion.parse`, but with following changes:
+/// * Leading zeroes are allowed.
+/// * Supports only 2 or 3 version components (major, minor, [patch]). If 3-rd component is omitted, it will be 0.
+pub fn parseVersion(ver: []const u8) error{ InvalidVersion, Overflow }!SemanticVersion {
+ const parseVersionComponentFn = (struct {
+ fn parseVersionComponentInner(component: []const u8) error{ InvalidVersion, Overflow }!usize {
+ return std.fmt.parseUnsigned(usize, component, 10) catch |err| switch (err) {
+ error.InvalidCharacter => return error.InvalidVersion,
+ error.Overflow => return error.Overflow,
+ };
+ }
+ }).parseVersionComponentInner;
+ var version_components = mem.splitScalar(u8, ver, '.');
+ const major = version_components.first();
+ const minor = version_components.next() orelse return error.InvalidVersion;
+ const patch = version_components.next() orelse "0";
+ if (version_components.next() != null) return error.InvalidVersion;
+ return .{
+ .major = try parseVersionComponentFn(major),
+ .minor = try parseVersionComponentFn(minor),
+ .patch = try parseVersionComponentFn(patch),
+ };
+}
+
+test parseVersion {
+ try std.testing.expectError(error.InvalidVersion, parseVersion("1"));
+ try std.testing.expectEqual(SemanticVersion{ .major = 1, .minor = 2, .patch = 0 }, try parseVersion("1.2"));
+ try std.testing.expectEqual(SemanticVersion{ .major = 1, .minor = 2, .patch = 3 }, try parseVersion("1.2.3"));
+ try std.testing.expectError(error.InvalidVersion, parseVersion("1.2.3.4"));
+}
+
+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: 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: Query) bool {
+ return self.os_tag == null and self.abi == null;
+}
+
+pub fn isNative(self: Query) bool {
+ return self.isNativeCpu() and self.isNativeOs() and self.isNativeAbi();
+}
+
+/// Formats a version with the patch component omitted if it is zero,
+/// unlike SemanticVersion.format which formats all its version components regardless.
+fn formatVersion(version: SemanticVersion, writer: anytype) !void {
+ if (version.patch == 0) {
+ try writer.print("{d}.{d}", .{ version.major, version.minor });
+ } else {
+ try writer.print("{d}.{d}.{d}", .{ version.major, version.minor, version.patch });
+ }
+}
+
+pub fn zigTriple(self: Query, allocator: Allocator) Allocator.Error![]u8 {
+ if (self.isNative()) {
+ return allocator.dupe(u8, "native");
+ }
+
+ const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native";
+ const os_name = if (self.os_tag) |os_tag| @tagName(os_tag) else "native";
+
+ var result = std.ArrayList(u8).init(allocator);
+ defer result.deinit();
+
+ try result.writer().print("{s}-{s}", .{ arch_name, os_name });
+
+ // 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) |min| {
+ switch (min) {
+ .none => {},
+ .semver => |v| {
+ try result.writer().writeAll(".");
+ try formatVersion(v, result.writer());
+ },
+ .windows => |v| {
+ try result.writer().print("{s}", .{v});
+ },
+ }
+ }
+ if (self.os_version_max) |max| {
+ switch (max) {
+ .none => {},
+ .semver => |v| {
+ try result.writer().writeAll("...");
+ try formatVersion(v, result.writer());
+ },
+ .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| {
+ 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| {
+ const name = @tagName(abi);
+ try result.ensureUnusedCapacity(name.len + 1);
+ result.appendAssumeCapacity('-');
+ result.appendSliceAssumeCapacity(name);
+ }
+
+ return result.toOwnedSlice();
+}
+
+/// 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);
+ },
+ }
+
+ if (q.cpu_features_add.isEmpty() and q.cpu_features_sub.isEmpty()) {
+ // The CPU name alone is sufficient.
+ return;
+ }
+
+ 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 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 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: *Query, major: u32, minor: u32, patch: u32) void {
+ self.glibc_version = SemanticVersion{ .major = major, .minor = minor, .patch = patch };
+}
+
+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;
+ const os_is_native = mem.eql(u8, os_name, "native");
+ if (!os_is_native) {
+ result.os_tag = std.meta.stringToEnum(Target.Os.Tag, os_name) orelse
+ return error.UnknownOperatingSystem;
+ }
+ const tag = result.os_tag orelse builtin.os.tag;
+ diags.os_tag = tag;
+
+ const version_text = it.rest();
+ if (it.next() == null) return;
+
+ switch (tag) {
+ .freestanding,
+ .ananas,
+ .cloudabi,
+ .fuchsia,
+ .kfreebsd,
+ .lv2,
+ .solaris,
+ .illumos,
+ .zos,
+ .haiku,
+ .minix,
+ .rtems,
+ .nacl,
+ .aix,
+ .cuda,
+ .nvcl,
+ .amdhsa,
+ .ps4,
+ .ps5,
+ .elfiamcu,
+ .mesa3d,
+ .contiki,
+ .amdpal,
+ .hermit,
+ .hurd,
+ .wasi,
+ .emscripten,
+ .uefi,
+ .opencl,
+ .glsl450,
+ .vulkan,
+ .plan9,
+ .driverkit,
+ .shadermodel,
+ .liteos,
+ .other,
+ => return error.InvalidOperatingSystemVersion,
+
+ .freebsd,
+ .macos,
+ .ios,
+ .tvos,
+ .watchos,
+ .netbsd,
+ .openbsd,
+ .linux,
+ .dragonfly,
+ => {
+ var range_it = mem.splitSequence(u8, version_text, "...");
+
+ const min_text = range_it.next().?;
+ const min_ver = parseVersion(min_text) catch |err| switch (err) {
+ error.Overflow => return error.InvalidOperatingSystemVersion,
+ error.InvalidVersion => return error.InvalidOperatingSystemVersion,
+ };
+ result.os_version_min = .{ .semver = min_ver };
+
+ const max_text = range_it.next() orelse return;
+ const max_ver = parseVersion(max_text) catch |err| switch (err) {
+ error.Overflow => return error.InvalidOperatingSystemVersion,
+ error.InvalidVersion => return error.InvalidOperatingSystemVersion,
+ };
+ result.os_version_max = .{ .semver = max_ver };
+ },
+
+ .windows => {
+ var range_it = mem.splitSequence(u8, version_text, "...");
+
+ const min_text = range_it.first();
+ const min_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, min_text) orelse
+ return error.InvalidOperatingSystemVersion;
+ result.os_version_min = .{ .windows = min_ver };
+
+ const max_text = range_it.next() orelse return;
+ const max_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, max_text) orelse
+ return error.InvalidOperatingSystemVersion;
+ result.os_version_max = .{ .windows = max_ver };
+ },
+ }
+}
+
+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 query = try Query.parse(.{});
+ query.setGnuLibCVersion(2, 1, 1);
+
+ 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.target.abi)},
+ ) catch unreachable;
+
+ try std.testing.expectEqualSlices(u8, triple, text);
+ }
+ {
+ const query = try Query.parse(.{
+ .arch_os_abi = "aarch64-linux",
+ .cpu_features = "native",
+ });
+
+ try std.testing.expect(query.cpu_arch.? == .aarch64);
+ try std.testing.expect(query.cpu_model == .native);
+ }
+ {
+ const query = try Query.parse(.{ .arch_os_abi = "native" });
+
+ try std.testing.expect(query.cpu_arch == null);
+ try std.testing.expect(query.isNative());
+
+ const text = try query.zigTriple(std.testing.allocator);
+ defer std.testing.allocator.free(text);
+ try std.testing.expectEqualSlices(u8, "native", text);
+ }
+ {
+ const query = try Query.parse(.{
+ .arch_os_abi = "x86_64-linux-gnu",
+ .cpu_features = "x86_64-sse-sse2-avx-cx8",
+ });
+ const target = try std.zig.system.resolveTargetQuery(query);
+
+ try std.testing.expect(target.os.tag == .linux);
+ try std.testing.expect(target.abi == .gnu);
+ try std.testing.expect(target.cpu.arch == .x86_64);
+ try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse));
+ try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .avx));
+ try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .cx8));
+ try std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .cmov));
+ try std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr));
+
+ try std.testing.expect(Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx, .cmov }));
+ try std.testing.expect(!Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx }));
+ 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 query.zigTriple(std.testing.allocator);
+ defer std.testing.allocator.free(text);
+ try std.testing.expectEqualSlices(u8, "x86_64-linux-gnu", text);
+ }
+ {
+ const query = try Query.parse(.{
+ .arch_os_abi = "arm-linux-musleabihf",
+ .cpu_features = "generic+v8a",
+ });
+ const target = try std.zig.system.resolveTargetQuery(query);
+
+ try std.testing.expect(target.os.tag == .linux);
+ try std.testing.expect(target.abi == .musleabihf);
+ try std.testing.expect(target.cpu.arch == .arm);
+ 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 query.zigTriple(std.testing.allocator);
+ defer std.testing.allocator.free(text);
+ try std.testing.expectEqualSlices(u8, "arm-linux-musleabihf", text);
+ }
+ {
+ const query = try Query.parse(.{
+ .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27",
+ .cpu_features = "generic+v8a",
+ });
+ const target = try std.zig.system.resolveTargetQuery(query);
+
+ try std.testing.expect(target.cpu.arch == .aarch64);
+ try std.testing.expect(target.os.tag == .linux);
+ try std.testing.expect(target.os.version_range.linux.range.min.major == 3);
+ try std.testing.expect(target.os.version_range.linux.range.min.minor == 10);
+ try std.testing.expect(target.os.version_range.linux.range.min.patch == 0);
+ try std.testing.expect(target.os.version_range.linux.range.max.major == 4);
+ try std.testing.expect(target.os.version_range.linux.range.max.minor == 4);
+ try std.testing.expect(target.os.version_range.linux.range.max.patch == 1);
+ try std.testing.expect(target.os.version_range.linux.glibc.major == 2);
+ try std.testing.expect(target.os.version_range.linux.glibc.minor == 27);
+ try std.testing.expect(target.os.version_range.linux.glibc.patch == 0);
+ try std.testing.expect(target.abi == .gnu);
+
+ 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
diff --git a/lib/std/target/amdgpu.zig b/lib/std/Target/amdgpu.zig
diff --git a/lib/std/target/arc.zig b/lib/std/Target/arc.zig
diff --git a/lib/std/target/arm.zig b/lib/std/Target/arm.zig
diff --git a/lib/std/target/avr.zig b/lib/std/Target/avr.zig
diff --git a/lib/std/target/bpf.zig b/lib/std/Target/bpf.zig
diff --git a/lib/std/target/csky.zig b/lib/std/Target/csky.zig
diff --git a/lib/std/target/hexagon.zig b/lib/std/Target/hexagon.zig
diff --git a/lib/std/target/loongarch.zig b/lib/std/Target/loongarch.zig
diff --git a/lib/std/target/m68k.zig b/lib/std/Target/m68k.zig
diff --git a/lib/std/target/mips.zig b/lib/std/Target/mips.zig
diff --git a/lib/std/target/msp430.zig b/lib/std/Target/msp430.zig
diff --git a/lib/std/target/nvptx.zig b/lib/std/Target/nvptx.zig
diff --git a/lib/std/target/powerpc.zig b/lib/std/Target/powerpc.zig
diff --git a/lib/std/target/riscv.zig b/lib/std/Target/riscv.zig
diff --git a/lib/std/target/s390x.zig b/lib/std/Target/s390x.zig
diff --git a/lib/std/target/sparc.zig b/lib/std/Target/sparc.zig
diff --git a/lib/std/target/spirv.zig b/lib/std/Target/spirv.zig
diff --git a/lib/std/target/ve.zig b/lib/std/Target/ve.zig
diff --git a/lib/std/target/wasm.zig b/lib/std/Target/wasm.zig
diff --git a/lib/std/target/x86.zig b/lib/std/Target/x86.zig
diff --git a/lib/std/target/xtensa.zig b/lib/std/Target/xtensa.zig
diff --git 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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/CrossTarget.zig b/lib/std/zig/CrossTarget.zig
@@ -1,895 +0,0 @@
-//! 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;
-
-/// `null` means native.
-cpu_arch: ?Target.Cpu.Arch = null,
-
-cpu_model: CpuModel = CpuModel.determined_by_cpu_arch,
-
-/// Sparse set of CPU features to add to the set from `cpu_model`.
-cpu_features_add: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
-
-/// Sparse set of CPU features to remove from the set from `cpu_model`.
-cpu_features_sub: Target.Cpu.Feature.Set = Target.Cpu.Feature.Set.empty,
-
-/// `null` means native.
-os_tag: ?Target.Os.Tag = null,
-
-/// `null` means the default version range for `os_tag`. If `os_tag` is `null` (native)
-/// then `null` for this field means native.
-os_version_min: ?OsVersion = null,
-
-/// When cross compiling, `null` means default (latest known OS version).
-/// When `os_tag` is native, `null` means equal to the native OS version.
-os_version_max: ?OsVersion = null,
-
-/// `null` means default when cross compiling, or native when os_tag is native.
-/// If `isGnuLibC()` is `false`, this must be `null` and is ignored.
-glibc_version: ?SemanticVersion = null,
-
-/// `null` means the native C ABI, if `os_tag` is native, otherwise it means the default C ABI.
-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{},
-
-/// `null` means default for the cpu/arch/os combo.
-ofmt: ?Target.ObjectFormat = null,
-
-pub const CpuModel = union(enum) {
- /// Always native
- native,
-
- /// Always baseline
- baseline,
-
- /// If CPU Architecture is native, then the CPU model will be native. Otherwise,
- /// it will be baseline.
- determined_by_cpu_arch,
-
- explicit: *const Target.Cpu.Model,
-};
-
-pub const OsVersion = union(enum) {
- none: void,
- semver: SemanticVersion,
- windows: Target.Os.WindowsVersion,
-};
-
-pub const SemanticVersion = std.SemanticVersion;
-
-pub const DynamicLinker = Target.DynamicLinker;
-
-pub fn fromTarget(target: Target) CrossTarget {
- var result: CrossTarget = .{
- .cpu_arch = target.cpu.arch,
- .cpu_model = .{ .explicit = target.cpu.model },
- .os_tag = target.os.tag,
- .os_version_min = undefined,
- .os_version_max = undefined,
- .abi = target.abi,
- .glibc_version = if (target.isGnuLibC())
- target.os.version_range.linux.glibc
- else
- null,
- };
- result.updateOsVersionRange(target.os);
-
- const all_features = target.cpu.arch.allFeaturesList();
- var cpu_model_set = target.cpu.model.features;
- cpu_model_set.populateDependencies(all_features);
- {
- // The "add" set is the full set with the CPU Model set removed.
- const add_set = &result.cpu_features_add;
- add_set.* = target.cpu.features;
- add_set.removeFeatureSet(cpu_model_set);
- }
- {
- // The "sub" set is the features that are on in CPU Model set and off in the full set.
- const sub_set = &result.cpu_features_sub;
- sub_set.* = cpu_model_set;
- sub_set.removeFeatureSet(target.cpu.features);
- }
- return result;
-}
-
-fn updateOsVersionRange(self: *CrossTarget, os: Target.Os) void {
- switch (os.tag) {
- .freestanding,
- .ananas,
- .cloudabi,
- .fuchsia,
- .kfreebsd,
- .lv2,
- .solaris,
- .illumos,
- .zos,
- .haiku,
- .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,
- => {
- self.os_version_min = .{ .none = {} };
- self.os_version_max = .{ .none = {} };
- },
-
- .freebsd,
- .macos,
- .ios,
- .tvos,
- .watchos,
- .netbsd,
- .openbsd,
- .dragonfly,
- => {
- self.os_version_min = .{ .semver = os.version_range.semver.min };
- self.os_version_max = .{ .semver = os.version_range.semver.max };
- },
-
- .linux => {
- self.os_version_min = .{ .semver = os.version_range.linux.range.min };
- self.os_version_max = .{ .semver = os.version_range.linux.range.max };
- },
-
- .windows => {
- self.os_version_min = .{ .windows = os.version_range.windows.min };
- self.os_version_max = .{ .windows = os.version_range.windows.max };
- },
- }
-}
-
-/// 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
- /// The fields are, respectively:
- /// * CPU Architecture
- /// * Operating System (and optional version range)
- /// * C ABI (optional, with optional glibc version)
- /// The string "native" can be used for CPU architecture as well as Operating System.
- /// If the CPU Architecture is specified as "native", then the Operating System and C ABI may be omitted.
- arch_os_abi: []const u8 = "native",
-
- /// Looks like "name+a+b-c-d+e", where "name" is a CPU Model name, "a", "b", and "e"
- /// are examples of CPU features to add to the set, and "c" and "d" are examples of CPU features
- /// to remove from the set.
- /// The following special strings are recognized for CPU Model name:
- /// * "baseline" - The "default" set of CPU features for cross-compiling. A conservative set
- /// of features that is expected to be supported on most available hardware.
- /// * "native" - The native CPU model is to be detected when compiling.
- /// If this field is not provided (`null`), then the value will depend on the
- /// parsed CPU Architecture. If native, then this will be "native". Otherwise, it will be "baseline".
- cpu_features: ?[]const u8 = null,
-
- /// Absolute path to dynamic linker, to override the default, which is either a natively
- /// detected path, or a standard path.
- dynamic_linker: ?[]const u8 = null,
-
- object_format: ?[]const u8 = null,
-
- /// If this is provided, the function will populate some information about parsing failures,
- /// so that user-friendly error messages can be delivered.
- diagnostics: ?*Diagnostics = null,
-
- pub const Diagnostics = struct {
- /// If the architecture was determined, this will be populated.
- arch: ?Target.Cpu.Arch = null,
-
- /// If the OS name was determined, this will be populated.
- os_name: ?[]const u8 = null,
-
- /// If the OS tag was determined, this will be populated.
- os_tag: ?Target.Os.Tag = null,
-
- /// If the ABI was determined, this will be populated.
- abi: ?Target.Abi = null,
-
- /// If the CPU name was determined, this will be populated.
- cpu_name: ?[]const u8 = null,
-
- /// If error.UnknownCpuFeature is returned, this will be populated.
- unknown_feature_name: ?[]const u8 = null,
- };
-};
-
-pub fn parse(args: ParseOptions) !CrossTarget {
- var dummy_diags: ParseOptions.Diagnostics = undefined;
- const diags = args.diagnostics orelse &dummy_diags;
-
- var result: CrossTarget = .{
- .dynamic_linker = DynamicLinker.init(args.dynamic_linker),
- };
-
- var it = mem.splitScalar(u8, args.arch_os_abi, '-');
- const arch_name = it.first();
- const arch_is_native = mem.eql(u8, arch_name, "native");
- if (!arch_is_native) {
- result.cpu_arch = std.meta.stringToEnum(Target.Cpu.Arch, arch_name) orelse
- return error.UnknownArchitecture;
- }
- const arch = result.getCpuArch();
- diags.arch = arch;
-
- if (it.next()) |os_text| {
- try parseOs(&result, diags, os_text);
- } else if (!arch_is_native) {
- return error.MissingOperatingSystem;
- }
-
- const opt_abi_text = it.next();
- if (opt_abi_text) |abi_text| {
- var abi_it = mem.splitScalar(u8, abi_text, '.');
- const abi = std.meta.stringToEnum(Target.Abi, abi_it.first()) orelse
- return error.UnknownApplicationBinaryInterface;
- result.abi = abi;
- diags.abi = abi;
-
- const abi_ver_text = abi_it.rest();
- if (abi_it.next() != null) {
- if (result.isGnuLibC()) {
- result.glibc_version = parseVersion(abi_ver_text) catch |err| switch (err) {
- error.Overflow => return error.InvalidAbiVersion,
- error.InvalidVersion => return error.InvalidAbiVersion,
- };
- } else {
- return error.InvalidAbiVersion;
- }
- }
- }
-
- if (it.next() != null) return error.UnexpectedExtraField;
-
- if (args.cpu_features) |cpu_features| {
- const all_features = arch.allFeaturesList();
- var index: usize = 0;
- while (index < cpu_features.len and
- cpu_features[index] != '+' and
- cpu_features[index] != '-')
- {
- index += 1;
- }
- const cpu_name = cpu_features[0..index];
- diags.cpu_name = cpu_name;
-
- const add_set = &result.cpu_features_add;
- const sub_set = &result.cpu_features_sub;
- if (mem.eql(u8, cpu_name, "native")) {
- result.cpu_model = .native;
- } else if (mem.eql(u8, cpu_name, "baseline")) {
- result.cpu_model = .baseline;
- } else {
- result.cpu_model = .{ .explicit = try arch.parseCpuModel(cpu_name) };
- }
-
- while (index < cpu_features.len) {
- const op = cpu_features[index];
- const set = switch (op) {
- '+' => add_set,
- '-' => sub_set,
- else => unreachable,
- };
- index += 1;
- const start = index;
- while (index < cpu_features.len and
- cpu_features[index] != '+' and
- cpu_features[index] != '-')
- {
- index += 1;
- }
- const feature_name = cpu_features[start..index];
- for (all_features, 0..) |feature, feat_index_usize| {
- const feat_index = @as(Target.Cpu.Feature.Set.Index, @intCast(feat_index_usize));
- if (mem.eql(u8, feature_name, feature.name)) {
- set.addFeature(feat_index);
- break;
- }
- } else {
- diags.unknown_feature_name = feature_name;
- return error.UnknownCpuFeature;
- }
- }
- }
-
- if (args.object_format) |ofmt_name| {
- result.ofmt = std.meta.stringToEnum(Target.ObjectFormat, ofmt_name) orelse
- return error.UnknownObjectFormat;
- }
-
- return result;
-}
-
-/// 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
-/// 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, '-');
- const arch_name = it.first();
- const arch_is_native = mem.eql(u8, arch_name, "native");
- if (arch_is_native) {
- return builtin.cpu.arch;
- } else {
- return std.meta.stringToEnum(Target.Cpu.Arch, arch_name);
- }
-}
-
-/// Similar to `SemanticVersion.parse`, but with following changes:
-/// * Leading zeroes are allowed.
-/// * Supports only 2 or 3 version components (major, minor, [patch]). If 3-rd component is omitted, it will be 0.
-pub fn parseVersion(ver: []const u8) error{ InvalidVersion, Overflow }!SemanticVersion {
- const parseVersionComponentFn = (struct {
- fn parseVersionComponentInner(component: []const u8) error{ InvalidVersion, Overflow }!usize {
- return std.fmt.parseUnsigned(usize, component, 10) catch |err| switch (err) {
- error.InvalidCharacter => return error.InvalidVersion,
- error.Overflow => return error.Overflow,
- };
- }
- }).parseVersionComponentInner;
- var version_components = mem.splitScalar(u8, ver, '.');
- const major = version_components.first();
- const minor = version_components.next() orelse return error.InvalidVersion;
- const patch = version_components.next() orelse "0";
- if (version_components.next() != null) return error.InvalidVersion;
- return .{
- .major = try parseVersionComponentFn(major),
- .minor = try parseVersionComponentFn(minor),
- .patch = try parseVersionComponentFn(patch),
- };
-}
-
-test parseVersion {
- try std.testing.expectError(error.InvalidVersion, parseVersion("1"));
- try std.testing.expectEqual(SemanticVersion{ .major = 1, .minor = 2, .patch = 0 }, try parseVersion("1.2"));
- try std.testing.expectEqual(SemanticVersion{ .major = 1, .minor = 2, .patch = 3 }, try parseVersion("1.2.3"));
- 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 {
- 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 {
- 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 {
- return self.os_tag == null and self.abi == null;
-}
-
-pub fn isNative(self: CrossTarget) bool {
- return self.isNativeCpu() and self.isNativeOs() and self.isNativeAbi();
-}
-
-/// Formats a version with the patch component omitted if it is zero,
-/// unlike SemanticVersion.format which formats all its version components regardless.
-fn formatVersion(version: SemanticVersion, writer: anytype) !void {
- if (version.patch == 0) {
- try writer.print("{d}.{d}", .{ version.major, version.minor });
- } else {
- try writer.print("{d}.{d}.{d}", .{ version.major, version.minor, version.patch });
- }
-}
-
-pub fn zigTriple(self: CrossTarget, allocator: mem.Allocator) error{OutOfMemory}![]u8 {
- if (self.isNative()) {
- return allocator.dupe(u8, "native");
- }
-
- const arch_name = if (self.cpu_arch) |arch| @tagName(arch) else "native";
- const os_name = if (self.os_tag) |os_tag| @tagName(os_tag) else "native";
-
- var result = std.ArrayList(u8).init(allocator);
- defer result.deinit();
-
- try result.writer().print("{s}-{s}", .{ arch_name, os_name });
-
- // 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()) {
- .none => {},
- .semver => |v| {
- try result.writer().writeAll(".");
- try formatVersion(v, result.writer());
- },
- .windows => |v| try result.writer().print("{s}", .{v}),
- }
- }
- if (self.os_version_max) |max| {
- switch (max) {
- .none => {},
- .semver => |v| {
- try result.writer().writeAll("...");
- try formatVersion(v, result.writer());
- },
- .windows => |v| try result.writer().print("..{s}", .{v}),
- }
- }
-
- if (self.glibc_version) |v| {
- try result.writer().print("-{s}.", .{@tagName(self.getAbi())});
- try formatVersion(v, result.writer());
- } else if (self.abi) |abi| {
- try result.writer().print("-{s}", .{@tagName(abi)});
- }
-
- 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);
-}
-
-pub fn linuxTriple(self: CrossTarget, allocator: mem.Allocator) ![]u8 {
- return Target.linuxTripleSimple(allocator, self.getCpuArch(), self.getOsTag(), self.getAbi());
-}
-
-pub fn wantSharedLibSymLinks(self: CrossTarget) bool {
- return self.getOsTag() != .windows;
-}
-
-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 isGnuLibC(self: CrossTarget) bool {
- return Target.isGnuLibC_os_tag_abi(self.getOsTag(), self.getAbi());
-}
-
-pub fn setGnuLibCVersion(self: *CrossTarget, major: u32, minor: u32, patch: u32) void {
- assert(self.isGnuLibC());
- 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 {
- var it = mem.splitScalar(u8, text, '.');
- const os_name = it.first();
- diags.os_name = os_name;
- const os_is_native = mem.eql(u8, os_name, "native");
- if (!os_is_native) {
- result.os_tag = std.meta.stringToEnum(Target.Os.Tag, os_name) orelse
- return error.UnknownOperatingSystem;
- }
- const tag = result.getOsTag();
- diags.os_tag = tag;
-
- const version_text = it.rest();
- if (it.next() == null) return;
-
- switch (tag) {
- .freestanding,
- .ananas,
- .cloudabi,
- .fuchsia,
- .kfreebsd,
- .lv2,
- .solaris,
- .illumos,
- .zos,
- .haiku,
- .minix,
- .rtems,
- .nacl,
- .aix,
- .cuda,
- .nvcl,
- .amdhsa,
- .ps4,
- .ps5,
- .elfiamcu,
- .mesa3d,
- .contiki,
- .amdpal,
- .hermit,
- .hurd,
- .wasi,
- .emscripten,
- .uefi,
- .opencl,
- .glsl450,
- .vulkan,
- .plan9,
- .driverkit,
- .shadermodel,
- .liteos,
- .other,
- => return error.InvalidOperatingSystemVersion,
-
- .freebsd,
- .macos,
- .ios,
- .tvos,
- .watchos,
- .netbsd,
- .openbsd,
- .linux,
- .dragonfly,
- => {
- var range_it = mem.splitSequence(u8, version_text, "...");
-
- const min_text = range_it.next().?;
- const min_ver = parseVersion(min_text) catch |err| switch (err) {
- error.Overflow => return error.InvalidOperatingSystemVersion,
- error.InvalidVersion => return error.InvalidOperatingSystemVersion,
- };
- result.os_version_min = .{ .semver = min_ver };
-
- const max_text = range_it.next() orelse return;
- const max_ver = parseVersion(max_text) catch |err| switch (err) {
- error.Overflow => return error.InvalidOperatingSystemVersion,
- error.InvalidVersion => return error.InvalidOperatingSystemVersion,
- };
- result.os_version_max = .{ .semver = max_ver };
- },
-
- .windows => {
- var range_it = mem.splitSequence(u8, version_text, "...");
-
- const min_text = range_it.first();
- const min_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, min_text) orelse
- return error.InvalidOperatingSystemVersion;
- result.os_version_min = .{ .windows = min_ver };
-
- const max_text = range_it.next() orelse return;
- const max_ver = std.meta.stringToEnum(Target.Os.WindowsVersion, max_text) orelse
- return error.InvalidOperatingSystemVersion;
- result.os_version_max = .{ .windows = max_ver };
- },
- }
-}
-
-test "CrossTarget.parse" {
- if (builtin.target.isGnuLibC()) {
- var cross_target = try CrossTarget.parse(.{});
- cross_target.setGnuLibCVersion(2, 1, 1);
-
- const text = try cross_target.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)},
- ) catch unreachable;
-
- try std.testing.expectEqualSlices(u8, triple, text);
- }
- {
- const cross_target = try CrossTarget.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);
- }
- {
- const cross_target = try CrossTarget.parse(.{ .arch_os_abi = "native" });
-
- try std.testing.expect(cross_target.cpu_arch == null);
- try std.testing.expect(cross_target.isNative());
-
- const text = try cross_target.zigTriple(std.testing.allocator);
- defer std.testing.allocator.free(text);
- try std.testing.expectEqualSlices(u8, "native", text);
- }
- {
- const cross_target = try CrossTarget.parse(.{
- .arch_os_abi = "x86_64-linux-gnu",
- .cpu_features = "x86_64-sse-sse2-avx-cx8",
- });
- const target = cross_target.toTarget();
-
- try std.testing.expect(target.os.tag == .linux);
- try std.testing.expect(target.abi == .gnu);
- try std.testing.expect(target.cpu.arch == .x86_64);
- try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .sse));
- try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .avx));
- try std.testing.expect(!Target.x86.featureSetHas(target.cpu.features, .cx8));
- try std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .cmov));
- try std.testing.expect(Target.x86.featureSetHas(target.cpu.features, .fxsr));
-
- try std.testing.expect(Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx, .cmov }));
- try std.testing.expect(!Target.x86.featureSetHasAny(target.cpu.features, .{ .sse, .avx }));
- 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);
- defer std.testing.allocator.free(text);
- try std.testing.expectEqualSlices(u8, "x86_64-linux-gnu", text);
- }
- {
- const cross_target = try CrossTarget.parse(.{
- .arch_os_abi = "arm-linux-musleabihf",
- .cpu_features = "generic+v8a",
- });
- const target = cross_target.toTarget();
-
- try std.testing.expect(target.os.tag == .linux);
- try std.testing.expect(target.abi == .musleabihf);
- try std.testing.expect(target.cpu.arch == .arm);
- 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);
- defer std.testing.allocator.free(text);
- try std.testing.expectEqualSlices(u8, "arm-linux-musleabihf", text);
- }
- {
- const cross_target = try CrossTarget.parse(.{
- .arch_os_abi = "aarch64-linux.3.10...4.4.1-gnu.2.27",
- .cpu_features = "generic+v8a",
- });
- const target = cross_target.toTarget();
-
- try std.testing.expect(target.cpu.arch == .aarch64);
- try std.testing.expect(target.os.tag == .linux);
- try std.testing.expect(target.os.version_range.linux.range.min.major == 3);
- try std.testing.expect(target.os.version_range.linux.range.min.minor == 10);
- try std.testing.expect(target.os.version_range.linux.range.min.patch == 0);
- try std.testing.expect(target.os.version_range.linux.range.max.major == 4);
- try std.testing.expect(target.os.version_range.linux.range.max.minor == 4);
- try std.testing.expect(target.os.version_range.linux.range.max.patch == 1);
- try std.testing.expect(target.os.version_range.linux.glibc.major == 2);
- try std.testing.expect(target.os.version_range.linux.glibc.minor == 27);
- 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);
- 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/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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 <symbol>` for ELF/MachO and `/include:<symbol>` 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 <lang>".
-// Loosely based on llvm-project/clang/include/clang/Driver/Types.def
+/// Supported languages for "zig clang -x <lang>".
+/// 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 <path>`.
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
}
@@ -34520,7 +34545,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");
}
@@ -36692,7 +36719,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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 <symbol>` for ELF/MachO and `/include:<symbol>` 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, <NONE> }.
- 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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, <NONE> }.
+ 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 <language>
+ \\ --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] <arch><sub>-<os>-<abi> 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 <language>
\\ -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:<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
@@ -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
@@ -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/<arch>/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
@@ -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
@@ -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
@@ -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
Binary files differ.
diff --git 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 <signal.h>
\\#include <stdio.h>
\\#include <stdlib.h>
@@ -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 <stdio.h>
\\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 <assert.h>
\\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 <stdio.h>
\\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<stdio.h>
\\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<stdio.h>
\\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 <stdio.h>
\\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<stdio.h>
\\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<stdio.h>
\\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 <stdio.h>
\\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 <assert.h>
\\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 <stdio.h>
\\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 <assert.h>
\\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 <dlfcn.h>
\\#include <assert.h>
@@ -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<stdio.h>
\\__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 <stdio.h>
\\__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 <stdio.h>
\\__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 <stdio.h>
\\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 <stdio.h>
\\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 <stdio.h>
\\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 <stdio.h>
\\__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 <stdio.h>
\\__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 <stdio.h>
\\__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 <stdio.h>
\\__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 <stdio.h>
\\__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 <stdio.h>
\\__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 <stdio.h>
\\__attribute__((destructor)) void fini1() { printf("7"); }
- , &.{});
+ ,
+ });
g_o.linkLibC();
- const h_o = addObject(b, "h", opts);
- addCSourceBytes(h_o,
- \\#include <stdio.h>
- \\__attribute__((destructor)) void fini2() { printf("8"); }
- , &.{});
+ const h_o = addObject(b, opts, .{ .name = "h", .c_source_bytes =
+ \\#include <stdio.h>
+ \\__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 <stdio.h>
\\#include <stdint.h>
@@ -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 <stdio.h>
\\#include <stdint.h>
@@ -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 <stdio.h>
\\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 <iostream>
\\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 <stdio.h>
\\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 <stdio.h>
\\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 <stdexcept>
\\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 <iostream>
\\#include <stdexcept>
@@ -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 <stdexcept>
\\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 <stdio.h>
\\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 <stdio.h>
\\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 <stdio.h>
\\_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 <stdio.h>
\\__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 <stdio.h>
\\__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 <stdio.h>
\\__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 <stdio.h>
\\__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 <stdio.h>
\\_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 <stdio.h>
\\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 <stdio.h>
@@ -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 <stdio.h>
\\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 <stdio.h>
\\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 <stdio.h>
\\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 <stdio.h>
\\__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 <assert.h>
\\#include <stdlib.h>
@@ -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 <pthread.h>
\\#include <dlfcn.h>
@@ -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 <stdio.h>
\\__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 <stdio.h>
\\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 <stdio.h>
\\_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 <stdio.h>
\\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 <stdio.h>
\\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 <stdio.h>
\\__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 <stdio.h>
\\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 <stdio.h>
\\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 <stdio.h>
\\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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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.
@@ -90,10 +90,6 @@ pub const build_cases = [_]BuildCase{
// .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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 => {},