src/doc/rustc/src/linker-plugin-lto.md MARKDOWN 262 lines View on github.com → Search inside
1# Linker-plugin-based LTO23The `-C linker-plugin-lto` flag allows for deferring the LTO optimization4to the actual linking step, which in turn allows for performing5interprocedural optimizations across programming language boundaries if6all the object files being linked were created by LLVM-based toolchains7using the **same** LTO mode: either thin LTO or fat LTO.8The examples would be linking Rust code together with9Clang-compiled C/C++ code and LLVM Flang-compiled Fortran code.1011## Usage1213There are two main cases how linker-plugin-based LTO can be used:1415 - compiling a Rust `staticlib` that is used as a C ABI dependency16 - compiling a Rust binary where `rustc` invokes the linker1718In both cases, the Rust code must be compiled with `-C linker-plugin-lto`.19By default, this enables thin LTO, so any interoperable language code must20also be compiled in thin LTO mode. To use fat LTO with linker-plugin-based LTO,21the `rustc` compiler requires the additional `-C lto=fat` flag, and the22interoperable language must likewise be compiled in fat LTO mode. Note that23interoperable language must be compiled using the LLVM infrastructure24(see more details in [toolchain compability](#toolchain-compatibility)).2526The following table summarizes how to enable thin LTO and fat LTO in27different compilers:2829| Compiler | Thin LTO         | Fat LTO        |30|:---------|-----------------:|---------------:|31| rustc    | -Clto=thin       | -Clto=fat      |32| clang    | -flto=thin       | -flto=full     |33| clang++  | -flto=thin       | -flto=full     |34| flang    | -flto=thin (WIP) | -flto=full     |3536### Rust `staticlib` as dependency in C/C++ program3738In this case the Rust compiler just has to make sure that the object files in39the `staticlib` are in the right format. For linking, a linker with the40LLVM plugin must be used (e.g. LLD).4142Using `rustc` directly:4344```bash45# Compile the Rust staticlib46rustc --crate-type=staticlib -Clinker-plugin-lto -Copt-level=2 ./lib.rs47# Compile the C code with `-flto=thin`48clang -c -O2 -flto=thin -o cmain.o ./cmain.c49# Link everything, making sure that we use an appropriate linker50clang -flto=thin -fuse-ld=lld -L . -l"name-of-your-rust-lib" -o main -O2 ./cmain.o51```5253Using `cargo`:5455```bash56# Compile the Rust staticlib57RUSTFLAGS="-Clinker-plugin-lto" cargo build --release58# Compile the C code with `-flto=thin`59clang -c -O2 -flto=thin -o cmain.o ./cmain.c60# Link everything, making sure that we use an appropriate linker61clang -flto=thin -fuse-ld=lld -L . -l"name-of-your-rust-lib" -o main -O2 ./cmain.o62```6364### C/C++ code as a dependency in Rust6566In this case the linker will be invoked by `rustc`. We again have to make sure67that an appropriate linker is used.6869Using `rustc` directly:7071```bash72# Compile C code with `-flto=thin`73clang ./clib.c -flto=thin -c -o ./clib.o -O274# Create a static library from the C code75ar crus ./libxyz.a ./clib.o7677# Invoke `rustc` with the additional arguments78rustc -Clinker-plugin-lto -L. -Copt-level=2 -Clinker=clang -Clink-arg=-fuse-ld=lld ./main.rs79```8081Using `cargo` directly:8283```bash84# Compile C code with `-flto=thin`85clang ./clib.c -flto=thin -c -o ./clib.o -O286# Create a static library from the C code87ar crus ./libxyz.a ./clib.o8889# Set the linking arguments via RUSTFLAGS90RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo build --release91```9293### Fortran code as a dependency in Rust9495Rust code can also be linked together with Fortran code compiled by LLVM `flang`.96The following examples demonstrate fat LTO usage, as LLVM `flang` has WIP status for97thin LTO. The same approach can be applied to compile C and C++ code with fat LTO.9899Using `rustc` directly:100101```bash102# Compile Fortran code with `-flto=full`103flang ./ftnlib.f90 -flto=full -c -o ./ftnlib.f90.o -O3104# Create a static library from the Fortran code105ar crus ./libftn.a ./ftnlib.f90.o106107# Invoke `rustc` with the additional arguments, `-Clto=fat` is mandatory108rustc -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.rs109```110111Using `cargo` directly:112113```bash114# Compile Fortran code with `-flto=full`115flang ./ftnlib.f90 -flto=full -c -o ./ftnlib.f90.o -O3116# Create a static library from the Fortran code117ar crus ./libftn.a ./ftnlib.f90.o118119# Set the linking arguments via RUSTFLAGS, `-Clto=fat` is mandatory120RUSTFLAGS="-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 --release121```122123Note, LLVM `flang` can be used as a linker driver starting from flang 21.1.8.124The `-C default-linker-libraries=yes` option may be omitted if the Fortran125runtime is not required; however, most Fortran code depends on the runtime,126so enabling default linker libraries is usually necessary.127128### Explicitly specifying the linker plugin to be used by `rustc`129130If one wants to use a linker other than LLD, the LLVM linker plugin has to be131specified explicitly. Otherwise the linker cannot read the object files. The132path to the plugin is passed as an argument to the `-Clinker-plugin-lto`133option:134135```bash136rustc -Clinker-plugin-lto="/path/to/LLVMgold.so" -L. -Copt-level=2 ./main.rs137```138139### Usage with clang-cl and x86_64-pc-windows-msvc140141Cross language LTO can be used with the x86_64-pc-windows-msvc target, but this requires using the142clang-cl compiler instead of the MSVC cl.exe included with Visual Studio Build Tools, and linking143with lld-link. Both clang-cl and lld-link can be downloaded from [LLVM's download page](https://releases.llvm.org/download.html).144Note that most crates in the ecosystem are likely to assume you are using cl.exe if using this target145and that some things, like for example vcpkg, [don't work very well with clang-cl](https://github.com/microsoft/vcpkg/issues/2087).146147You will want to make sure your rust major LLVM version matches your installed LLVM tooling version,148otherwise it is likely you will get linker errors:149150```bat151rustc -V --verbose152clang-cl --version153```154155If you are compiling any proc-macros, you will get this error:156157```bash158error: Linker plugin based LTO is not supported together with `-C prefer-dynamic` when159targeting Windows-like targets160```161162This is fixed if you explicitly set the target, for example163`cargo build --target x86_64-pc-windows-msvc`164Without an explicit --target the flags will be passed to all compiler invocations (including build165scripts and proc macros), see [cargo docs on rustflags](../cargo/reference/config.html#buildrustflags)166167If you have dependencies using the `cc` crate, you will need to set these168environment variables:169```bat170set CC=clang-cl171set CXX=clang-cl172set CFLAGS=/clang:-flto=thin /clang:-fuse-ld=lld-link173set CXXFLAGS=/clang:-flto=thin /clang:-fuse-ld=lld-link174REM Needed because msvc's lib.exe crashes on LLVM LTO .obj files175set AR=llvm-lib176```177178If you are specifying lld-link as your linker by setting `linker = "lld-link.exe"` in your cargo config,179you may run into issues with some crates that compile code with separate cargo invocations. You should be180able to get around this problem by setting `-Clinker=lld-link` in RUSTFLAGS181182## Toolchain Compatibility183184<!-- NOTE: to update the below table, you can use this Python script:185186```python187from collections import defaultdict188import subprocess189import sys190191def minor_version(version):192    return int(version.split('.')[1])193194INSTALL_TOOLCHAIN = ["rustup", "toolchain", "install", "--profile", "minimal"]195subprocess.run(INSTALL_TOOLCHAIN + ["nightly"])196197LOWER_BOUND = 91198NIGHTLY_VERSION = minor_version(subprocess.run(199    ["rustc", "+nightly", "--version"],200    capture_output=True,201    text=True).stdout)202203def llvm_version(toolchain):204    version_text = subprocess.run(205        ["rustc", "+{}".format(toolchain), "-Vv"],206        capture_output=True,207        text=True).stdout208    return int(version_text.split("LLVM")[1].split(':')[1].split('.')[0])209210version_map = defaultdict(lambda: [])211for version in range(LOWER_BOUND, NIGHTLY_VERSION - 1):212    toolchain = "1.{}.0".format(version)213    print("Checking", toolchain, file=sys.stderr)214    subprocess.run(215        INSTALL_TOOLCHAIN + ["--no-self-update", toolchain],216        capture_output=True)217    version_map[llvm_version(toolchain)].append(version)218219print("| Rust Version | Clang Version |")220print("|--------------|---------------|")221for clang, rust in sorted(version_map.items()):222    if len(rust) > 1:223        rust_range = "1.{} - 1.{}".format(rust[0], rust[-1])224    else:225        rust_range = "1.{}       ".format(rust[0])226    print("| {}  |      {}       |".format(rust_range, clang))227```228229-->230231In order for this kind of LTO to work, the LLVM linker plugin must be able to232handle the LLVM bitcode produced by `rustc` and by compilers of all233interoperable languages. A good rule of thumb is to use an LLVM linker plugin234whose version is at least as new as the newest compiler involved.235236Best results are achieved by using a `rustc` and LLVM compilers that are based237on the exact same version of LLVM. One can use `rustc -vV` in order to view238the LLVM used by a given `rustc` version. Note that the version number given239here is only an approximation as Rust sometimes uses unstable revisions of240LLVM. However, the approximation is usually reliable.241242The following table shows known good combinations of toolchain versions.243244| Rust Version | Clang Version |245|--------------|---------------|246| 1.34 - 1.37  |       8       |247| 1.38 - 1.44  |       9       |248| 1.45 - 1.46  |      10       |249| 1.47 - 1.51  |      11       |250| 1.52 - 1.55  |      12       |251| 1.56 - 1.59  |      13       |252| 1.60 - 1.64  |      14       |253| 1.65 - 1.69  |      15       |254| 1.70 - 1.72  |      16       |255| 1.73 - 1.77  |      17       |256| 1.78 - 1.81  |      18       |257| 1.82 - 1.86  |      19       |258| 1.87 - 1.90  |      20       |259| 1.91 - 1.94  |      21       |260261Note that the compatibility policy for this feature might change in the future.

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.