aboutsummaryrefslogtreecommitdiff
path: root/src/link/Elf.zig
diff options
context:
space:
mode:
authormlugg <mlugg@mlugg.co.uk>2025-05-28 06:36:47 +0100
committermlugg <mlugg@mlugg.co.uk>2025-06-12 13:55:39 +0100
commit3743c3e39c6bb645db7403fd446953d43ac7c7dc (patch)
treee9223b737051f606b7eec326e71e5977f4164dfc /src/link/Elf.zig
parent424e6ac54b0f8bbfb43f24e28c71ac72169f3719 (diff)
downloadzig-3743c3e39c6bb645db7403fd446953d43ac7c7dc.tar.gz
zig-3743c3e39c6bb645db7403fd446953d43ac7c7dc.zip
compiler: slightly untangle LLVM from the linkers
The main goal of this commit is to make it easier to decouple codegen from the linkers by being able to do LLVM codegen without going through the `link.File`; however, this ended up being a nice refactor anyway. Previously, every linker stored an optional `llvm.Object`, which was populated when using LLVM for the ZCU *and* linking an output binary; and `Zcu` also stored an optional `llvm.Object`, which was used only when we needed LLVM for the ZCU (e.g. for `-femit-llvm-bc`) but were not emitting a binary. This situation was incredibly silly. It meant there were N+1 places the LLVM object might be instead of just 1, and it meant that every linker had to start a bunch of methods by checking for an LLVM object, and just dispatching to the corresponding method on *it* instead if it was not `null`. Instead, we now always store the LLVM object on the `Zcu` -- which makes sense, because it corresponds to the object emitted by, well, the Zig Compilation Unit! The linkers now mostly don't make reference to LLVM. `Compilation` makes sure to emit the LLVM object if necessary before calling `flush`, so it is ready for the linker. Also, all of the `link.File` methods which act on the ZCU -- like `updateNav` -- now check for the LLVM object in `link.zig` instead of in every single individual linker implementation. Notably, the change to LLVM emit improves this rather ludicrous call chain in the `-fllvm -flld` case: * Compilation.flush * link.File.flush * link.Elf.flush * link.Elf.linkWithLLD * link.Elf.flushModule * link.emitLlvmObject * Compilation.emitLlvmObject * llvm.Object.emit Replacing it with this one: * Compilation.flush * llvm.Object.emit ...although we do currently still end up in `link.Elf.linkWithLLD` to do the actual linking. The logic for invoking LLD should probably also be unified at least somewhat; I haven't done that in this commit.
Diffstat (limited to 'src/link/Elf.zig')
-rw-r--r--src/link/Elf.zig40
1 files changed, 10 insertions, 30 deletions
diff --git a/src/link/Elf.zig b/src/link/Elf.zig
index 1516993c74..b18fc7ce33 100644
--- a/src/link/Elf.zig
+++ b/src/link/Elf.zig
@@ -32,9 +32,6 @@ entry_name: ?[]const u8,
ptr_width: PtrWidth,
-/// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path.
-llvm_object: ?LlvmObject.Ptr = null,
-
/// A list of all input files.
/// First index is a special "null file". Order is otherwise not observed.
files: std.MultiArrayList(File.Entry) = .{},
@@ -344,9 +341,6 @@ pub fn createEmpty(
.print_map = options.print_map,
.dump_argv_list = .empty,
};
- 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)) {
@@ -457,8 +451,6 @@ pub fn open(
pub fn deinit(self: *Elf) void {
const gpa = self.base.comp.gpa;
- if (self.llvm_object) |llvm_object| llvm_object.deinit();
-
for (self.file_handles.items) |fh| {
fh.close();
}
@@ -515,7 +507,6 @@ pub fn deinit(self: *Elf) void {
}
pub fn getNavVAddr(self: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo) !u64 {
- assert(self.llvm_object == null);
return self.zigObjectPtr().?.getNavVAddr(self, pt, nav_index, reloc_info);
}
@@ -530,7 +521,6 @@ pub fn lowerUav(
}
pub fn getUavVAddr(self: *Elf, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {
- assert(self.llvm_object == null);
return self.zigObjectPtr().?.getUavVAddr(self, uav, reloc_info);
}
@@ -805,35 +795,29 @@ pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std
else => |e| return diags.fail("failed to link with LLD: {s}", .{@errorName(e)}),
};
}
- try self.flushModule(arena, tid, prog_node);
+ try self.flushZcu(arena, tid, prog_node);
}
-pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
+pub fn flushZcu(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
const tracy = trace(@src());
defer tracy.end();
const comp = self.base.comp;
const diags = &comp.link_diags;
- 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;
- }
-
if (comp.verbose_link) Compilation.dump_argv(self.dump_argv_list.items);
const sub_prog_node = prog_node.start("ELF Flush", 0);
defer sub_prog_node.end();
- return flushModuleInner(self, arena, tid) catch |err| switch (err) {
+ return flushZcuInner(self, arena, tid) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
error.LinkFailure => return error.LinkFailure,
else => |e| return diags.fail("ELF flush failed: {s}", .{@errorName(e)}),
};
}
-fn flushModuleInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
+fn flushZcuInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
const comp = self.base.comp;
const gpa = comp.gpa;
const diags = &comp.link_diags;
@@ -1523,8 +1507,12 @@ fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: s
// 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 (comp.zcu != null) blk: {
- try self.flushModule(arena, tid, prog_node);
+ const module_obj_path: ?[]const u8 = if (comp.zcu) |zcu| blk: {
+ if (zcu.llvm_object == null) {
+ try self.flushZcu(arena, tid, prog_node);
+ } else {
+ // `Compilation.flush` has already made LLVM emit this object file for us.
+ }
if (fs.path.dirname(full_out_path)) |dirname| {
break :blk try fs.path.join(arena, &.{ dirname, self.base.zcu_object_sub_path.? });
@@ -2385,7 +2373,6 @@ pub fn writeElfHeader(self: *Elf) !void {
}
pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void {
- if (self.llvm_object) |llvm_object| return llvm_object.freeNav(nav);
return self.zigObjectPtr().?.freeNav(self, nav);
}
@@ -2399,7 +2386,6 @@ pub fn updateFunc(
if (build_options.skip_non_native and builtin.object_format != .elf) {
@panic("Attempted to compile for object format that was disabled by build configuration");
}
- if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness);
return self.zigObjectPtr().?.updateFunc(self, pt, func_index, air, liveness);
}
@@ -2411,7 +2397,6 @@ pub fn updateNav(
if (build_options.skip_non_native and builtin.object_format != .elf) {
@panic("Attempted to compile for object format that was disabled by build configuration");
}
- if (self.llvm_object) |llvm_object| return llvm_object.updateNav(pt, nav);
return self.zigObjectPtr().?.updateNav(self, pt, nav);
}
@@ -2423,7 +2408,6 @@ pub fn updateContainerType(
if (build_options.skip_non_native and builtin.object_format != .elf) {
@panic("Attempted to compile for object format that was disabled by build configuration");
}
- if (self.llvm_object) |_| return;
const zcu = pt.zcu;
const gpa = zcu.gpa;
return self.zigObjectPtr().?.updateContainerType(pt, ty) catch |err| switch (err) {
@@ -2449,12 +2433,10 @@ pub fn updateExports(
if (build_options.skip_non_native and builtin.object_format != .elf) {
@panic("Attempted to compile for object format that was disabled by build configuration");
}
- if (self.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices);
return self.zigObjectPtr().?.updateExports(self, pt, exported, export_indices);
}
pub fn updateLineNumber(self: *Elf, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
- if (self.llvm_object) |_| return;
return self.zigObjectPtr().?.updateLineNumber(pt, ti_id);
}
@@ -2463,7 +2445,6 @@ pub fn deleteExport(
exported: Zcu.Exported,
name: InternPool.NullTerminatedString,
) void {
- if (self.llvm_object) |_| return;
return self.zigObjectPtr().?.deleteExport(self, exported, name);
}
@@ -5332,7 +5313,6 @@ const GotSection = synthetic_sections.GotSection;
const GotPltSection = synthetic_sections.GotPltSection;
const HashSection = synthetic_sections.HashSection;
const LinkerDefined = @import("Elf/LinkerDefined.zig");
-const LlvmObject = @import("../codegen/llvm.zig").Object;
const Zcu = @import("../Zcu.zig");
const Object = @import("Elf/Object.zig");
const InternPool = @import("../InternPool.zig");