]>
Commit | Line | Data |
---|---|---|
9fa01778 XL |
1 | # Linker-plugin-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 main.o ./main.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 main.o ./main.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 | ||
5099ac24 FG |
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](https://doc.rust-lang.org/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 | |
9fa01778 XL |
131 | |
132 | ## Toolchain Compatibility | |
133 | ||
1b1a35ee XL |
134 | <!-- NOTE: to update the below table, you can use this shell script: |
135 | ||
136 | ```sh | |
137 | rustup toolchain install --profile minimal nightly | |
138 | MINOR_VERSION=$(rustc +nightly --version | cut -d . -f 2) | |
139 | LOWER_BOUND=44 | |
140 | ||
141 | llvm_version() { | |
142 | toolchain="$1" | |
143 | printf "Rust $toolchain | Clang " | |
144 | rustc +"$toolchain" -Vv | grep LLVM | cut -d ':' -f 2 | tr -d ' ' | |
145 | } | |
146 | ||
147 | for version in `seq $LOWER_BOUND $((MINOR_VERSION - 2))`; do | |
148 | toolchain=1.$version.0 | |
149 | rustup toolchain install --no-self-update --profile minimal $toolchain >/dev/null 2>&1 | |
150 | llvm_version $toolchain | |
151 | done | |
152 | ``` | |
153 | ||
154 | --> | |
155 | ||
9fa01778 XL |
156 | In order for this kind of LTO to work, the LLVM linker plugin must be able to |
157 | handle the LLVM bitcode produced by both `rustc` and `clang`. | |
158 | ||
159 | Best results are achieved by using a `rustc` and `clang` that are based on the | |
160 | exact same version of LLVM. One can use `rustc -vV` in order to view the LLVM | |
161 | used by a given `rustc` version. Note that the version number given | |
162 | here is only an approximation as Rust sometimes uses unstable revisions of | |
163 | LLVM. However, the approximation is usually reliable. | |
164 | ||
165 | The following table shows known good combinations of toolchain versions. | |
166 | ||
1b1a35ee XL |
167 | | Rust Version | Clang Version | |
168 | |--------------|---------------| | |
169 | | Rust 1.34 | Clang 8 | | |
170 | | Rust 1.35 | Clang 8 | | |
171 | | Rust 1.36 | Clang 8 | | |
172 | | Rust 1.37 | Clang 8 | | |
173 | | Rust 1.38 | Clang 9 | | |
174 | | Rust 1.39 | Clang 9 | | |
175 | | Rust 1.40 | Clang 9 | | |
176 | | Rust 1.41 | Clang 9 | | |
177 | | Rust 1.42 | Clang 9 | | |
178 | | Rust 1.43 | Clang 9 | | |
179 | | Rust 1.44 | Clang 9 | | |
180 | | Rust 1.45 | Clang 10 | | |
181 | | Rust 1.46 | Clang 10 | | |
9fa01778 XL |
182 | |
183 | Note that the compatibility policy for this feature might change in the future. |