基于链接器插件的 LTO
-C linker-plugin-lto
标志允许将 LTO 优化推迟到实际链接步骤,这反过来允许在所有被链接的目标文件都是由基于 LLVM 的工具链创建的情况下,跨编程语言边界执行过程间优化。这里的主要例子是将 Rust 代码与用 Clang 编译的 C/C++ 代码链接在一起。
用法
基于链接器插件的 LTO 可以使用两种主要情况
- 编译用作 C ABI 依赖项的 Rust
staticlib
- 编译 Rust 二进制文件,其中
rustc
调用链接器
在这两种情况下,Rust 代码都必须使用 -C linker-plugin-lto
编译,而 C/C++ 代码则使用 -flto
或 -flto=thin
编译,以便目标文件以 LLVM 位码的形式发出。
Rust staticlib
作为 C/C++ 程序中的依赖项
在这种情况下,Rust 编译器只需要确保 staticlib
中的目标文件格式正确。对于链接,必须使用带有 LLVM 插件的链接器(例如 LLD)。
直接使用 rustc
# Compile the Rust staticlib
rustc --crate-type=staticlib -Clinker-plugin-lto -Copt-level=2 ./lib.rs
# Compile the C code with `-flto=thin`
clang -c -O2 -flto=thin -o cmain.o ./cmain.c
# Link everything, making sure that we use an appropriate linker
clang -flto=thin -fuse-ld=lld -L . -l"name-of-your-rust-lib" -o main -O2 ./cmain.o
使用 cargo
# Compile the Rust staticlib
RUSTFLAGS="-Clinker-plugin-lto" cargo build --release
# Compile the C code with `-flto=thin`
clang -c -O2 -flto=thin -o cmain.o ./cmain.c
# Link everything, making sure that we use an appropriate linker
clang -flto=thin -fuse-ld=lld -L . -l"name-of-your-rust-lib" -o main -O2 ./cmain.o
C/C++ 代码作为 Rust 中的依赖项
在这种情况下,链接器将由 rustc
调用。我们再次需要确保使用适当的链接器。
直接使用 rustc
# Compile C code with `-flto`
clang ./clib.c -flto=thin -c -o ./clib.o -O2
# Create a static library from the C code
ar crus ./libxyz.a ./clib.o
# Invoke `rustc` with the additional arguments
rustc -Clinker-plugin-lto -L. -Copt-level=2 -Clinker=clang -Clink-arg=-fuse-ld=lld ./main.rs
直接使用 cargo
# Compile C code with `-flto`
clang ./clib.c -flto=thin -c -o ./clib.o -O2
# Create a static library from the C code
ar crus ./libxyz.a ./clib.o
# Set the linking arguments via RUSTFLAGS
RUSTFLAGS="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld" cargo build --release
显式指定 rustc
要使用的链接器插件
如果想要使用除 LLD 之外的链接器,则必须显式指定 LLVM 链接器插件。否则,链接器将无法读取目标文件。插件的路径作为参数传递给 -Clinker-plugin-lto
选项
rustc -Clinker-plugin-lto="/path/to/LLVMgold.so" -L. -Copt-level=2 ./main.rs
与 clang-cl 和 x86_64-pc-windows-msvc 一起使用
跨语言 LTO 可以与 x86_64-pc-windows-msvc 目标一起使用,但这需要使用 clang-cl 编译器而不是 Visual Studio Build Tools 附带的 MSVC cl.exe,并使用 lld-link 进行链接。clang-cl 和 lld-link 都可以从 LLVM 的下载页面 下载。请注意,生态系统中的大多数板条箱可能会假设您在使用此目标时使用 cl.exe,并且某些东西,例如 vcpkg,与 clang-cl 的配合不是很好。
您需要确保您的 rust 主要 LLVM 版本与您安装的 LLVM 工具版本匹配,否则您很可能会遇到链接器错误
rustc -V --verbose
clang-cl --version
如果您正在编译任何 proc-macros,您将收到此错误
error: Linker plugin based LTO is not supported together with `-C prefer-dynamic` when
targeting Windows-like targets
如果您显式设置目标,例如 cargo build --target x86_64-pc-windows-msvc
,则可以解决此问题。如果没有显式的 --target,这些标志将传递给所有编译器调用(包括构建脚本和 proc-macros),请参阅 cargo 文档中的 rustflags
如果您有使用 cc
板条箱的依赖项,则需要设置这些环境变量
set CC=clang-cl
set CXX=clang-cl
set CFLAGS=/clang:-flto=thin /clang:-fuse-ld=lld-link
set CXXFLAGS=/clang:-flto=thin /clang:-fuse-ld=lld-link
REM Needed because msvc's lib.exe crashes on LLVM LTO .obj files
set AR=llvm-lib
如果您在 cargo 配置中设置 linker = "lld-link.exe"
来指定 lld-link 作为您的链接器,您可能会遇到使用单独的 cargo 调用编译代码的一些板条箱的问题。您可以通过在 RUSTFLAGS 中设置 -Clinker=lld-link
来解决此问题
工具链兼容性
为了使这种 LTO 能够正常工作,LLVM 链接器插件必须能够处理 rustc
和 clang
生成的 LLVM 位码。
使用基于完全相同 LLVM 版本的 rustc
和 clang
可以获得最佳效果。可以使用 rustc -vV
来查看给定 rustc
版本使用的 LLVM。请注意,这里给出的版本号只是一个近似值,因为 Rust 有时会使用 LLVM 的不稳定版本。但是,近似值通常是可靠的。
下表显示了已知的良好工具链版本组合。
Rust 版本 | Clang 版本 |
---|---|
1.34 - 1.37 | 8 |
1.38 - 1.44 | 9 |
1.45 - 1.46 | 10 |
1.47 - 1.51 | 11 |
1.52 - 1.55 | 12 |
1.56 - 1.59 | 13 |
1.60 - 1.64 | 14 |
1.65 - 1.69 | 15 |
1.70 - 1.72 | 16 |
1.73 - 1.74 | 17 |
请注意,此功能的兼容性策略将来可能会更改。