]> git.lizzy.rs Git - rust.git/blob - src/doc/rustc/src/linker-plugin-lto.md
Rollup merge of #106669 - ozkanonur:helper-function-for-lint-level, r=Nilstrieb
[rust.git] / src / doc / rustc / src / linker-plugin-lto.md
1 # Linker-plugin-based LTO
2
3 The `-C linker-plugin-lto` flag allows for deferring the LTO optimization
4 to the actual linking step, which in turn allows for performing
5 interprocedural optimizations across programming language boundaries if
6 all the object files being linked were created by LLVM based toolchains.
7 The prime example here would be linking Rust code together with
8 Clang-compiled C/C++ code.
9
10 ## Usage
11
12 There are two main cases how linker plugin based LTO can be used:
13
14  - compiling a Rust `staticlib` that is used as a C ABI dependency
15  - compiling a Rust binary where `rustc` invokes the linker
16
17 In both cases the Rust code has to be compiled with `-C linker-plugin-lto` and
18 the C/C++ code with `-flto` or `-flto=thin` so that object files are emitted
19 as LLVM bitcode.
20
21 ### Rust `staticlib` as dependency in C/C++ program
22
23 In this case the Rust compiler just has to make sure that the object files in
24 the `staticlib` are in the right format. For linking, a linker with the
25 LLVM plugin must be used (e.g. LLD).
26
27 Using `rustc` directly:
28
29 ```bash
30 # Compile the Rust staticlib
31 rustc --crate-type=staticlib -Clinker-plugin-lto -Copt-level=2 ./lib.rs
32 # Compile the C code with `-flto=thin`
33 clang -c -O2 -flto=thin -o cmain.o ./cmain.c
34 # Link everything, making sure that we use an appropriate linker
35 clang -flto=thin -fuse-ld=lld -L . -l"name-of-your-rust-lib" -o main -O2 ./cmain.o
36 ```
37
38 Using `cargo`:
39
40 ```bash
41 # Compile the Rust staticlib
42 RUSTFLAGS="-Clinker-plugin-lto" cargo build --release
43 # Compile the C code with `-flto=thin`
44 clang -c -O2 -flto=thin -o cmain.o ./cmain.c
45 # Link everything, making sure that we use an appropriate linker
46 clang -flto=thin -fuse-ld=lld -L . -l"name-of-your-rust-lib" -o main -O2 ./cmain.o
47 ```
48
49 ### C/C++ code as a dependency in Rust
50
51 In this case the linker will be invoked by `rustc`. We again have to make sure
52 that an appropriate linker is used.
53
54 Using `rustc` directly:
55
56 ```bash
57 # Compile C code with `-flto`
58 clang ./clib.c -flto=thin -c -o ./clib.o -O2
59 # Create a static library from the C code
60 ar crus ./libxyz.a ./clib.o
61
62 # Invoke `rustc` with the additional arguments
63 rustc -Clinker-plugin-lto -L. -Copt-level=2 -Clinker=clang -Clink-arg=-fuse-ld=lld ./main.rs
64 ```
65
66 Using `cargo` directly:
67
68 ```bash
69 # Compile C code with `-flto`
70 clang ./clib.c -flto=thin -c -o ./clib.o -O2
71 # Create a static library from the C code
72 ar crus ./libxyz.a ./clib.o
73
74 # Set the linking arguments via RUSTFLAGS
75 RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo build --release
76 ```
77
78 ### Explicitly specifying the linker plugin to be used by `rustc`
79
80 If one wants to use a linker other than LLD, the LLVM linker plugin has to be
81 specified explicitly. Otherwise the linker cannot read the object files. The
82 path to the plugin is passed as an argument to the `-Clinker-plugin-lto`
83 option:
84
85 ```bash
86 rustc -Clinker-plugin-lto="/path/to/LLVMgold.so" -L. -Copt-level=2 ./main.rs
87 ```
88
89 ### Usage with clang-cl and x86_64-pc-windows-msvc
90
91 Cross language LTO can be used with the x86_64-pc-windows-msvc target, but this requires using the
92 clang-cl compiler instead of the MSVC cl.exe included with Visual Studio Build Tools, and linking
93 with lld-link. Both clang-cl and lld-link can be downloaded from [LLVM's download page](https://releases.llvm.org/download.html).
94 Note that most crates in the ecosystem are likely to assume you are using cl.exe if using this target
95 and that some things, like for example vcpkg, [don't work very well with clang-cl](https://github.com/microsoft/vcpkg/issues/2087).
96
97 You will want to make sure your rust major LLVM version matches your installed LLVM tooling version,
98 otherwise it is likely you will get linker errors:
99
100 ```bat
101 rustc -V --verbose
102 clang-cl --version
103 ```
104
105 If you are compiling any proc-macros, you will get this error:
106
107 ```bash
108 error: Linker plugin based LTO is not supported together with `-C prefer-dynamic` when
109 targeting Windows-like targets
110 ```
111
112 This is fixed if you explicitly set the target, for example
113 `cargo build --target x86_64-pc-windows-msvc`
114 Without an explicit --target the flags will be passed to all compiler invocations (including build
115 scripts and proc macros), see [cargo docs on rustflags](../cargo/reference/config.html#buildrustflags)
116
117 If you have dependencies using the `cc` crate, you will need to set these
118 environment variables:
119 ```bat
120 set CC=clang-cl
121 set CXX=clang-cl
122 set CFLAGS=/clang:-flto=thin /clang:-fuse-ld=lld-link
123 set CXXFLAGS=/clang:-flto=thin /clang:-fuse-ld=lld-link
124 REM Needed because msvc's lib.exe crashes on LLVM LTO .obj files
125 set AR=llvm-lib
126 ```
127
128 If you are specifying lld-link as your linker by setting `linker = "lld-link.exe"` in your cargo config,
129 you may run into issues with some crates that compile code with separate cargo invocations. You should be
130 able to get around this problem by setting `-Clinker=lld-link` in RUSTFLAGS
131
132 ## Toolchain Compatibility
133
134 <!-- NOTE: to update the below table, you can use this Python script:
135
136 ```python
137 from collections import defaultdict
138 import subprocess
139
140 def minor_version(version):
141     return int(version.split('.')[1])
142
143 INSTALL_TOOLCHAIN = ["rustup", "toolchain", "install", "--profile", "minimal"]
144 subprocess.run(INSTALL_TOOLCHAIN + ["nightly"])
145
146 LOWER_BOUND = 65
147 NIGHTLY_VERSION = minor_version(subprocess.run(
148     ["rustc", "+nightly", "--version"],
149     capture_output=True,
150     text=True).stdout)
151
152 def llvm_version(toolchain):
153     version_text = subprocess.run(
154         ["rustc", "+{}".format(toolchain), "-Vv"],
155         capture_output=True,
156         text=True).stdout
157     return int(version_text.split("LLVM")[1].split(':')[1].split('.')[0])
158
159 version_map = defaultdict(lambda: [])
160 for version in range(LOWER_BOUND, NIGHTLY_VERSION - 1):
161     toolchain = "1.{}.0".format(version)
162     subprocess.run(
163         INSTALL_TOOLCHAIN + ["--no-self-update", toolchain],
164         capture_output=True)
165     version_map[llvm_version(toolchain)].append(version)
166
167 print("| Rust Version | Clang Version |")
168 print("|--------------|---------------|")
169 for clang, rust in sorted(version_map.items()):
170     if len(rust) > 1:
171         rust_range = "1.{} - 1.{}".format(rust[0], rust[-1])
172     else:
173         rust_range = "1.{}       ".format(rust[0])
174     print("| {}  |      {}       |".format(rust_range, clang))
175 ```
176
177 -->
178
179 In order for this kind of LTO to work, the LLVM linker plugin must be able to
180 handle the LLVM bitcode produced by both `rustc` and `clang`.
181
182 Best results are achieved by using a `rustc` and `clang` that are based on the
183 exact same version of LLVM. One can use `rustc -vV` in order to view the LLVM
184 used by a given `rustc` version. Note that the version number given
185 here is only an approximation as Rust sometimes uses unstable revisions of
186 LLVM. However, the approximation is usually reliable.
187
188 The following table shows known good combinations of toolchain versions.
189
190 | Rust Version | Clang Version |
191 |--------------|---------------|
192 | 1.34 - 1.37  |       8       |
193 | 1.38 - 1.44  |       9       |
194 | 1.45 - 1.46  |      10       |
195 | 1.47 - 1.51  |      11       |
196 | 1.52 - 1.55  |      12       |
197 | 1.56 - 1.59  |      13       |
198 | 1.60 - 1.64  |      14       |
199 | 1.65         |      15       |
200
201 Note that the compatibility policy for this feature might change in the future.