aboutsummaryrefslogtreecommitdiff
path: root/src/link.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2024-01-02 14:11:27 -0800
committerGitHub <noreply@github.com>2024-01-02 14:11:27 -0800
commit289ae45c1b58e952867c4fa1e246d0ef7bc2ff64 (patch)
tree5dd034143a2354b7b44496e684f1c764e2f9664c /src/link.zig
parentc89bb3e141ee215add0b52930d48bffd8dae8342 (diff)
parentc546ddb3edc557fae4b932e5239b9dcb66117832 (diff)
downloadzig-289ae45c1b58e952867c4fa1e246d0ef7bc2ff64.tar.gz
zig-289ae45c1b58e952867c4fa1e246d0ef7bc2ff64.zip
Merge pull request #18160 from ziglang/std-build-module
Move many settings from being per-Compilation to being per-Module
Diffstat (limited to 'src/link.zig')
-rw-r--r--src/link.zig1050
1 files changed, 397 insertions, 653 deletions
diff --git a/src/link.zig b/src/link.zig
index 12b9c58d9b..5c296b49ab 100644
--- a/src/link.zig
+++ b/src/link.zig
@@ -10,7 +10,6 @@ const wasi_libc = @import("wasi_libc.zig");
const Air = @import("Air.zig");
const Allocator = std.mem.Allocator;
-const BuildId = std.Build.CompileStep.BuildId;
const Cache = std.Build.Cache;
const Compilation = @import("Compilation.zig");
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
@@ -19,6 +18,7 @@ const Module = @import("Module.zig");
const InternPool = @import("InternPool.zig");
const Type = @import("type.zig").Type;
const TypedValue = @import("TypedValue.zig");
+const LlvmObject = @import("codegen/llvm.zig").Object;
/// When adding a new field, remember to update `hashAddSystemLibs`.
/// These are *always* dynamically linked. Static libraries will be
@@ -33,17 +33,6 @@ pub const SystemLib = struct {
path: ?[]const u8,
};
-/// When adding a new field, remember to update `hashAddFrameworks`.
-pub const Framework = struct {
- needed: bool = false,
- weak: bool = false,
- path: []const u8,
-};
-
-pub const SortSection = enum { name, alignment };
-
-pub const CacheMode = enum { incremental, whole };
-
pub fn hashAddSystemLibs(
man: *Cache.Manifest,
hm: std.StringArrayHashMapUnmanaged(SystemLib),
@@ -57,355 +46,172 @@ pub fn hashAddSystemLibs(
}
}
-pub fn hashAddFrameworks(man: *Cache.Manifest, hm: []const Framework) !void {
- for (hm) |value| {
- man.hash.add(value.needed);
- man.hash.add(value.weak);
- _ = try man.addFile(value.path, null);
- }
-}
-
pub const producer_string = if (builtin.is_test) "zig test" else "zig " ++ build_options.version;
-pub const Emit = struct {
- /// Where the output will go.
- directory: Compilation.Directory,
- /// Path to the output file, relative to `directory`.
- sub_path: []const u8,
-
- /// Returns the full path to `basename` if it were in the same directory as the
- /// `Emit` sub_path.
- pub fn basenamePath(emit: Emit, arena: Allocator, basename: [:0]const u8) ![:0]const u8 {
- const full_path = if (emit.directory.path) |p|
- try fs.path.join(arena, &[_][]const u8{ p, emit.sub_path })
- else
- emit.sub_path;
-
- if (fs.path.dirname(full_path)) |dirname| {
- return try fs.path.joinZ(arena, &.{ dirname, basename });
- } else {
- return basename;
- }
- }
-};
-
-pub const Options = struct {
- /// This is `null` when `-fno-emit-bin` is used.
- emit: ?Emit,
- /// This is `null` when not building a Windows DLL, or when `-fno-emit-implib` is used.
- implib_emit: ?Emit,
- /// This is non-null when `-femit-docs` is provided.
- docs_emit: ?Emit,
- target: std.Target,
- output_mode: std.builtin.OutputMode,
- link_mode: std.builtin.LinkMode,
- optimize_mode: std.builtin.OptimizeMode,
- machine_code_model: std.builtin.CodeModel,
- root_name: [:0]const u8,
- /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`.
- module: ?*Module,
- dynamic_linker: ?[]const u8,
- /// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin)
- sysroot: ?[]const u8,
- /// Used for calculating how much space to reserve for symbols in case the binary file
- /// does not already have a symbol table.
- symbol_count_hint: u64 = 32,
- /// Used for calculating how much space to reserve for executable program code in case
- /// the binary file does not already have such a section.
- program_code_size_hint: u64 = 256 * 1024,
- entry_addr: ?u64 = null,
- entry: ?[]const u8,
- stack_size_override: ?u64,
- image_base_override: ?u64,
- /// 0 means no stack protector
- /// other value means stack protector with that buffer size.
- stack_protector: u32,
- cache_mode: CacheMode,
- include_compiler_rt: bool,
- /// Set to `true` to omit debug info.
- strip: bool,
- /// If this is true then this link code is responsible for outputting an object
- /// file and then using LLD to link it together with the link options and other objects.
- /// Otherwise (depending on `use_llvm`) this link code directly outputs and updates the final binary.
- use_lld: bool,
- /// If this is true then this link code is responsible for making an LLVM IR Module,
- /// outputting it to an object file, and then linking that together with link options and
- /// other objects.
- /// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary.
- use_llvm: bool,
- use_lib_llvm: bool,
- link_libc: bool,
- link_libcpp: bool,
- link_libunwind: bool,
- darwin_sdk_layout: ?DarwinSdkLayout,
- function_sections: bool,
- data_sections: bool,
- no_builtin: bool,
- eh_frame_hdr: bool,
- emit_relocs: bool,
- rdynamic: bool,
- z_nodelete: bool,
- z_notext: bool,
- z_defs: bool,
- z_origin: bool,
- z_nocopyreloc: bool,
- z_now: bool,
- z_relro: bool,
- z_common_page_size: ?u64,
- z_max_page_size: ?u64,
- tsaware: bool,
- nxcompat: bool,
- dynamicbase: bool,
- linker_optimization: u8,
- compress_debug_sections: CompressDebugSections,
- bind_global_refs_locally: bool,
- import_memory: bool,
- export_memory: bool,
- import_symbols: bool,
- import_table: bool,
- export_table: bool,
- initial_memory: ?u64,
- max_memory: ?u64,
- shared_memory: bool,
- export_symbol_names: []const []const u8,
- global_base: ?u64,
- is_native_os: bool,
- is_native_abi: bool,
- pic: bool,
- pie: bool,
- lto: bool,
- valgrind: bool,
- tsan: bool,
- stack_check: bool,
- red_zone: bool,
- omit_frame_pointer: bool,
- single_threaded: bool,
- verbose_link: bool,
- dll_export_fns: bool,
- error_return_tracing: bool,
- skip_linker_dependencies: bool,
- each_lib_rpath: bool,
- build_id: BuildId,
- disable_lld_caching: bool,
- is_test: bool,
- hash_style: HashStyle,
- sort_section: ?SortSection,
- major_subsystem_version: ?u32,
- minor_subsystem_version: ?u32,
- gc_sections: ?bool = null,
- allow_shlib_undefined: ?bool,
- subsystem: ?std.Target.SubSystem,
- linker_script: ?[]const u8,
- version_script: ?[]const u8,
- soname: ?[]const u8,
- llvm_cpu_features: ?[*:0]const u8,
- print_gc_sections: bool,
- print_icf_sections: bool,
- print_map: bool,
- opt_bisect_limit: i32,
-
- objects: []Compilation.LinkObject,
- framework_dirs: []const []const u8,
- frameworks: []const Framework,
- /// These are *always* dynamically linked. Static libraries will be
- /// provided as positional arguments.
- system_libs: std.StringArrayHashMapUnmanaged(SystemLib),
- wasi_emulated_libs: []const wasi_libc.CRTFile,
- // TODO: remove this. libraries are resolved by the frontend.
- lib_dirs: []const []const u8,
- rpath_list: []const []const u8,
-
- /// List of symbols forced as undefined in the symbol table
- /// thus forcing their resolution by the linker.
- /// Corresponds to `-u <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,
- }
-}