如何解决Windows平台clang-cl与Rust跨语言LTO内联失效问题
我正在使用nightly版rustc(LLVM 19.1)与clang-cl,也曾尝试稳定版rustc 1.81.0和clang-cl 18.1.8,但均未成功实现跨语言LTO。
版本信息
> rustc +nightly --version --verbose rustc 1.83.0-nightly (9e394f551 2024-09-25) binary: rustc commit-hash: 9e394f551c050ff03c6fc57f190e0761cf0be6e8 commit-date: 2024-09-25 host: x86_64-pc-windows-msvc release: 1.83.0-nightly LLVM version: 19.1.0 > clang --version clang version 19.1.0 Target: x86_64-pc-windows-msvc Thread model: posix InstalledDir: C:\Program Files\LLVM\bin
测试代码
用于验证LTO效果的最小代码:
a.rs(Rust实现)
#[no_mangle] extern "C" fn add_2_numbers(arg: i32)->i32{ arg + arg }
b.c
#include <stdint.h> int32_t add_2_numbers(int32_t arg); [[clang::noinline]] int32_t process_2_nums(int32_t x, int32_t y) { return add_2_numbers(x) * add_2_numbers(y); }
main.c
#include <stdint.h> #include <stdio.h> int32_t process_2_nums(int32_t x, int32_t y); int32_t main() { int32_t x = 0; int32_t y = 0; scanf("%d", &x); scanf("%d", &y); const int32_t value = process_2_nums(x, y); printf("Result is %d", value); return 0; }
a.c(纯C实现,用于验证LTO基础功能)
#include <stdint.h> int32_t add_2_numbers(int32_t arg) { return arg + arg; }
原编译链接命令
rustc +nightly -Copt-level=3 -Ccodegen-units=1 -Clto=fat -Clinker-plugin-lto --crate-type=staticlib --emit=obj a.rs clang-cl /clang:-std=c17 /clang:-O3 -c /D_CRT_SECURE_NO_WARNINGS=1 /clang:-flto=thin /clang:-fuse-ld=lld-link b.c clang-cl /clang:-std=c17 /clang:-O3 -c /D_CRT_SECURE_NO_WARNINGS=1 /clang:-flto=thin /clang:-fuse-ld=lld-link main.c lld-link /out:c_wrap_rust.exe a.o b.obj main.obj -verbose /threads:1
问题现象
在Windows平台,Rust编写的add_2_numbers函数未被内联到C的process_2_nums函数中,反汇编显示仍存在函数调用。尝试过-Clto=thin、将Rust代码编译为静态库后链接、直接用clang-cl链接等方式,结果均相同。但将a.rs替换为a.c(纯C)后,LTO正常工作,反汇编显示函数已被内联。
Linux平台对比
在Linux平台执行相同操作时,跨语言LTO完全正常,反汇编显示add_2_numbers已被成功内联。
额外尝试
通过/mllvm:-print-after-all参数查看lld-link输出,发现链接器先处理Rust目标文件并生成机器码,再处理C目标文件,两者优化分开进行。尝试向链接器传递MS link.exe的LTO标志/LTCG,无效。
解决方案
要在Windows平台实现Rust与C的跨语言LTO,需调整编译和链接流程,确保LLVM LTO插件能协同处理Rust和C的中间代码:
1. 统一LTO类型
Rust和Clang需使用相同的LTO类型(thin或fat),避免类型不匹配导致优化失效。这里统一使用thin LTO:
2. 编译Rust代码
添加-Clinker=lld-link指定链接器,并确保-Clinker-plugin-lto启用:
rustc +nightly -Copt-level=3 -Ccodegen-units=1 -Clto=thin -Clinker-plugin-lto -Clinker=lld-link --crate-type=staticlib --emit=obj a.rs
3. 编译C代码
保持-flto=thin参数,无需指定链接器(编译阶段仅生成目标文件):
clang-cl /clang:-std=c17 /clang:-O3 /c /D_CRT_SECURE_NO_WARNINGS=1 /clang:-flto=thin b.c clang-cl /clang:-std=c17 /clang:-O3 /c /D_CRT_SECURE_NO_WARNINGS=1 /clang:-flto=thin main.c
4. 用clang-cl驱动链接
直接调用lld-link无法自动加载Rust的LTO插件,需通过clang-cl驱动完成链接,让它自动传递必要的LTO参数:
clang-cl /O3 /clang:-flto=thin /clang:-fuse-ld=lld-link a.o b.obj main.obj /Fe:c_wrap_rust.exe
原理说明
通过clang-cl驱动链接时,它会自动加载LLVM LTO插件,并将Rust和C的LTO中间代码合并处理,实现跨语言的函数内联等优化。直接调用lld-link无法触发这一联合优化流程,导致Rust和C的代码被分开处理。
内容的提问来源于stack exchange,提问作者Angelicos Phosphoros

