Linker-plugin-based LTO: Give explanation how to use linker-plugin-lto with full LTO

This commit is contained in:
Igor S. Gerasimov 2026-02-01 03:13:55 +01:00
parent 905b926967
commit cd50e62277

View file

@ -3,20 +3,35 @@
The `-C linker-plugin-lto` flag allows for deferring the LTO optimization
to the actual linking step, which in turn allows for performing
interprocedural optimizations across programming language boundaries if
all the object files being linked were created by LLVM based toolchains.
The prime example here would be linking Rust code together with
Clang-compiled C/C++ code.
all the object files being linked were created by LLVM-based toolchains
using the **same** LTO mode: either thin LTO or fat LTO.
The examples would be linking Rust code together with
Clang-compiled C/C++ code and LLVM Flang-compiled Fortran code.
## Usage
There are two main cases how linker plugin based LTO can be used:
There are two main cases how linker-plugin-based LTO can be used:
- compiling a Rust `staticlib` that is used as a C ABI dependency
- compiling a Rust binary where `rustc` invokes the linker
In both cases the Rust code has to be compiled with `-C linker-plugin-lto` and
the C/C++ code with `-flto` or `-flto=thin` so that object files are emitted
as LLVM bitcode.
In both cases, the Rust code must be compiled with `-C linker-plugin-lto`.
By default, this enables thin LTO, so any interoperable language code must
also be compiled in thin LTO mode. To use fat LTO with linker-plugin-based LTO,
the `rustc` compiler requires the additional `-C lto=fat` flag, and the
interoperable language must likewise be compiled in fat LTO mode. Note that
interoperable language must be compiled using the LLVM infrastructure
(see more details in [toolchain compability](#toolchain-compatibility)).
The following table summarizes how to enable thin LTO and fat LTO in
different compilers:
| Compiler | Thin LTO | Fat LTO |
|:---------|-----------------:|---------------:|
| rustc | -Clto=thin | -Clto=fat |
| clang | -flto=thin | -flto=full |
| clang++ | -flto=thin | -flto=full |
| flang | -flto=thin (WIP) | -flto=full |
### Rust `staticlib` as dependency in C/C++ program
@ -54,7 +69,7 @@ that an appropriate linker is used.
Using `rustc` directly:
```bash
# Compile C code with `-flto`
# Compile C code with `-flto=thin`
clang ./clib.c -flto=thin -c -o ./clib.o -O2
# Create a static library from the C code
ar crus ./libxyz.a ./clib.o
@ -66,7 +81,7 @@ rustc -Clinker-plugin-lto -L. -Copt-level=2 -Clinker=clang -Clink-arg=-fuse-ld=l
Using `cargo` directly:
```bash
# Compile C code with `-flto`
# Compile C code with `-flto=thin`
clang ./clib.c -flto=thin -c -o ./clib.o -O2
# Create a static library from the C code
ar crus ./libxyz.a ./clib.o
@ -75,6 +90,41 @@ ar crus ./libxyz.a ./clib.o
RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo build --release
```
### Fortran code as a dependency in Rust
Rust code can also be linked together with Fortran code compiled by LLVM `flang`.
The following examples demonstrate fat LTO usage, as LLVM `flang` has WIP status for
thin LTO. The same approach can be applied to compile C and C++ code with fat LTO.
Using `rustc` directly:
```bash
# Compile Fortran code with `-flto=full`
flang ./ftnlib.f90 -flto=full -c -o ./ftnlib.f90.o -O3
# Create a static library from the Fortran code
ar crus ./libftn.a ./ftnlib.f90.o
# Invoke `rustc` with the additional arguments, `-Clto=fat` is mandatory
rustc -Clinker-plugin-lto -Clto=fat -Clink-arg=path/to/libftn.a -Copt-level=3 -Clinker=flang -C default-linker-libraries=yes -Clink-arg=-fuse-ld=lld ./main.rs
```
Using `cargo` directly:
```bash
# Compile Fortran code with `-flto=full`
flang ./ftnlib.f90 -flto=full -c -o ./ftnlib.f90.o -O3
# Create a static library from the Fortran code
ar crus ./libftn.a ./ftnlib.f90.o
# Set the linking arguments via RUSTFLAGS, `-Clto=fat` is mandatory
RUSTFLAGS="-Clinker-plugin-lto -Clto=fat -Clink-arg=path/to/libftn.a -Clinker=flang -C default-linker-libraries=yes -Clink-arg=-fuse-ld=lld" cargo build --release
```
Note, LLVM `flang` can be used as a linker driver starting from flang 21.1.8.
The `-C default-linker-libraries=yes` option may be omitted if the Fortran
runtime is not required; however, most Fortran code depends on the runtime,
so enabling default linker libraries is usually necessary.
### Explicitly specifying the linker plugin to be used by `rustc`
If one wants to use a linker other than LLD, the LLVM linker plugin has to be
@ -179,11 +229,13 @@ for clang, rust in sorted(version_map.items()):
-->
In order for this kind of LTO to work, the LLVM linker plugin must be able to
handle the LLVM bitcode produced by both `rustc` and `clang`.
handle the LLVM bitcode produced by `rustc` and by compilers of all
interoperable languages. A good rule of thumb is to use an LLVM linker plugin
whose version is at least as new as the newest compiler involved.
Best results are achieved by using a `rustc` and `clang` that are based on the
exact same version of LLVM. One can use `rustc -vV` in order to view the LLVM
used by a given `rustc` version. Note that the version number given
Best results are achieved by using a `rustc` and LLVM compilers that are based
on the exact same version of LLVM. One can use `rustc -vV` in order to view
the LLVM used by a given `rustc` version. Note that the version number given
here is only an approximation as Rust sometimes uses unstable revisions of
LLVM. However, the approximation is usually reliable.
@ -204,6 +256,6 @@ The following table shows known good combinations of toolchain versions.
| 1.78 - 1.81 | 18 |
| 1.82 - 1.86 | 19 |
| 1.87 - 1.90 | 20 |
| 1.91 - 1.92 | 21 |
| 1.91 - 1.93 | 21 |
Note that the compatibility policy for this feature might change in the future.