diff options
| author | mlugg <mlugg@mlugg.co.uk> | 2025-05-28 09:30:31 +0100 |
|---|---|---|
| committer | mlugg <mlugg@mlugg.co.uk> | 2025-06-12 13:55:39 +0100 |
| commit | 2fb6f5c1adcd764372ad28ed4014fdaf558da778 (patch) | |
| tree | c38d1e129c3ee15c29198aacab1c2d76df857cf5 /src/codegen | |
| parent | 3743c3e39c6bb645db7403fd446953d43ac7c7dc (diff) | |
| download | zig-2fb6f5c1adcd764372ad28ed4014fdaf558da778.tar.gz zig-2fb6f5c1adcd764372ad28ed4014fdaf558da778.zip | |
link: divorce LLD from the self-hosted linkers
Similar to the previous commit, this commit untangles LLD integration
from the self-hosted linkers. Despite the big network of functions which
were involved, it turns out what was going on here is quite simple. The
LLD linking logic is actually very self-contained; it requires a few
flags from the `link.File.OpenOptions`, but that's really about it. We
don't need any of the mutable state on `Elf`/`Coff`/`Wasm`, for
instance. There was some legacy code trying to handle support for using
self-hosted codegen with LLD, but that's not a supported use case, so
I've just stripped it out.
For now, I've just pasted the logic for linking the 3 targets we
currently support using LLD for into this new linker implementation,
`link.Lld`; however, it's almost certainly possible to combine some of
the logic and simplify this file a bit. But to be honest, it's not
actually that bad right now.
This commit ends up eliminating the distinction between `flush` and
`flushZcu` (formerly `flushModule`) in linkers, where the latter
previously meant something along the lines of "flush, but if you're
going to be linking with LLD, just flush the ZCU object file, don't
actually link"?. The distinction here doesn't seem like it was properly
defined, and most linkers seem to treat them as essentially identical
anyway. Regardless, all calls to `flushZcu` are gone now, so it's
deleted -- one `flush` to rule them all!
The end result of this commit and the preceding one is that LLVM and LLD
fit into the pipeline much more sanely:
* If we're using LLVM for the ZCU, that state is on `zcu.llvm_object`
* If we're using LLD to link, then the `link.File` is a `link.Lld`
* Calls to "ZCU link functions" (e.g. `updateNav`) lower to calls to the
LLVM object if it's available, or otherwise to the `link.File` if it's
available (neither is available under `-fno-emit-bin`)
* After everything is done, linking is finalized by calling `flush` on
the `link.File`; for `link.Lld` this invokes LLD, for other linkers it
flushes self-hosted linker state
There's one messy thing remaining, and that's how self-hosted function
codegen in a ZCU works; right now, we process AIR with a call sequence
something like this:
* `link.doTask`
* `Zcu.PerThread.linkerUpdateFunc`
* `link.File.updateFunc`
* `link.Elf.updateFunc`
* `link.Elf.ZigObject.updateFunc`
* `codegen.generateFunction`
* `arch.x86_64.CodeGen.generate`
So, we start in the linker, take a scenic detour through `Zcu`, go back
to the linker, into its implementation, and then... right back out, into
code which is generic over the linker implementation, and then dispatch
on the *backend* instead! Of course, within `arch.x86_64.CodeGen`, there
are some more places which switch on the `link` implementation being
used. This is all pretty silly... so it shall be my next target.
Diffstat (limited to 'src/codegen')
| -rw-r--r-- | src/codegen/llvm.zig | 15 |
1 files changed, 9 insertions, 6 deletions
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 2b39396c38..37c13c7211 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1587,12 +1587,15 @@ pub const Object = struct { const comp = zcu.comp; // If we're on COFF and linking with LLD, the linker cares about our exports to determine the subsystem in use. - if (comp.bin_file != null and - comp.bin_file.?.tag == .coff and - zcu.comp.config.use_lld and - ip.isFunctionType(ip.getNav(nav_index).typeOf(ip))) - { - const flags = &comp.bin_file.?.cast(.coff).?.lld_export_flags; + coff_export_flags: { + const lf = comp.bin_file orelse break :coff_export_flags; + const lld = lf.cast(.lld) orelse break :coff_export_flags; + const coff = switch (lld.ofmt) { + .elf, .wasm => break :coff_export_flags, + .coff => |*coff| coff, + }; + if (!ip.isFunctionType(ip.getNav(nav_index).typeOf(ip))) break :coff_export_flags; + const flags = &coff.lld_export_flags; for (export_indices) |export_index| { const name = export_index.ptr(zcu).opts.name; if (name.eqlSlice("main", ip)) flags.c_main = true; |
