Linker-plugin-LTO

-C linker-plugin-lto 标签允许将 LTO (译者注:Linking Technology and Order)优化推迟到实际链接步骤中,如果要链接的所有目标文件(object files) 都是基于 LLVM 工具链优化的,则此标签又允许跨编程语言边界执行过程间优化,本文示例主要展示如何将 Rust 代码与使用 Clang 编译的 C/C++ 代码链接在一起。

用法

链接基于LTO的插件有两种主要的情况:

  • 编译 Rust staticlib 用于 C ABI 的依赖项。
  • rustc 调用链接器的地方编译 Rust 二进制文件。

在这两种情况下,Rust代码需要用 -C linker-plugin-lto 编译,并且 c/c++ 代码使用 -flto-flto=thin 以便将目标文件生成 LLVM 位码。

Rust staticlib 作为 C/C++ 程序中的依赖项

在这种情况下,Rust 编译器只需要确保 staticlib 中的目标文件格式正确即可。 要想进行链接,必须使用带有 LLVM 插件的链接器 (例如 LLD)。

直接使用 rustc

# 编译 Rust staticlib
rustc --crate-type=staticlib -Clinker-plugin-lto -Copt-level=2 ./lib.rs
# 用 `-flto=thin` 编译 C代码
clang -c -O2 -flto=thin -o main.o ./main.c
# 链接起来,得先确保我们使用的是合适的链接器。
clang -flto=thin -fuse-ld=lld -L . -l"name-of-your-rust-lib" -o main -O2 ./cmain.o

使用 cargo:

# 编译 Rust staticlib
RUSTFLAGS="-Clinker-plugin-lto" cargo build --release
# 用 `-flto=thin` 编译 C代码
clang -c -O2 -flto=thin -o main.o ./main.c
# 链接起来,得先确保我们使用的是合适的链接器。
clang -flto=thin -fuse-ld=lld -L . -l"name-of-your-rust-lib" -o main -O2 ./cmain.o

C/C++ 代码作为 Rust 中的依赖项

在这种情况下,将由 rustc 调用链接器。我们再次必须确保使用合适的链接器。

直接使用 rustc

# 用 `-flto=thin` 编译 C代码
clang ./clib.c -flto=thin -c -o ./clib.o -O2
# 从 C代码中创建静态库
ar crus ./libxyz.a ./clib.o

# 使用其他参数一起调用 `rustc`
rustc -Clinker-plugin-lto -L. -Copt-level=2 -Clinker=clang -Clink-arg=-fuse-ld=lld ./main.rs

直接使用 cargo

# 用 `-flto` 编译 C代码
clang ./clib.c -flto=thin -c -o ./clib.o -O2
# 从 C代码中创建静态库
ar crus ./libxyz.a ./clib.o

# 通过 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

工具链的兼容性

为了使这种 LTO 生效,LLVM 链接器插件必须能够处理由 rustcclang 产生的 LLVM 位码。

通过使用基于相同版本的 LLVM 的 rustcclang 实现可获得最佳结果。可以使用 rustc -vV 来查看给定 rustc 版本所使用的 LLVM。注意因为Rust有时会使用 LLVM 的不稳定版本,所以此处所给出的版本号只是一个近似值,但是这种近似通常是可靠的。

下表展示了已知工具链的良好组合。

Rust 版本Clang 版本
Rust 1.34Clang 8
Rust 1.35Clang 8
Rust 1.36Clang 8
Rust 1.37Clang 8
Rust 1.38Clang 9
Rust 1.39Clang 9
Rust 1.40Clang 9
Rust 1.41Clang 9
Rust 1.42Clang 9
Rust 1.43Clang 9
Rust 1.44Clang 9
Rust 1.45Clang 10
Rust 1.46Clang 10

注意,此处的兼容性策略将来可能会更改。