如何使用clang-cl和Rust在Windows上进行跨语言LTO(链接时间优化)?

问题描述 投票:0回答:1

我正在使用 nightly rustc 和使用 clang-cl 的 LLVM 19.1。我也尝试了稳定的 rustc 1.81.0 和 clang-cl 18.1.8,但没有成功。

版本
> 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

#[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;
 }

如果我只使用 c:

,我还尝试了 a.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

出于某种原因,Rust 中的函数未内联到 C 中的函数。

Windows 中的反汇编代码
sub_140001010 proc near
push    rsi
push    rdi
sub     rsp, 28h
mov     esi, edx
call    sub_140001000
mov     edi, eax
mov     ecx, esi
call    sub_140001000
imul    eax, edi
add     rsp, 28h
pop     rdi
pop     rsi
retn
sub_140001010 endp

; I would expect this function to be inlined.
sub_140001000 proc near
lea     eax, [rcx+rcx]
retn
sub_140001000 endp

我还尝试使用

-Clto=thin
,将Rust代码编译到静态库并与其链接,使用
clang-cl
链接代码,所有情况都相同。

当我用

a.rs
替换
a.c
并从 C 链接 3 个目标文件时,它就可以工作了。

sub_140001000 proc near
imul    ecx, edx
lea     eax, ds:0[rcx*4]
retn
sub_140001000 endp
Linux

我也尝试使用 Linux 机器做同样的事情,一切都很完美:

命令

clang-19 -std=c17 -O3 -c -flto=thin -o b.o b.c
clang-19 -std=c17 -O3 -c -flto=thin -o main.o main.c
rustc +nightly -Copt-level=3 -Ccodegen-units=1 -Clto=fat -Clinker-plugin-lto --crate-type=staticlib --emit=obj a.rs
clang-19 -flto -fuse-ld=lld a.o b.o main.o -o c_wrap_rust -v
llvm-objdump-19 --demangle --disassemble-symbols=process_2_nums c_wrap_rust

结果:

c_wrap_rust:    file format elf64-x86-64

Disassembly of section .text:

0000000000046820 <process_2_nums>:
   46820: 0f af fe                      imull   %esi, %edi
   46823: 8d 04 bd 00 00 00 00          leal    (,%rdi,4), %eax
   4682a: c3                            retq
   4682b: cc                            int3
   4682c: cc                            int3
   4682d: cc                            int3
   4682e: cc                            int3
   4682f: cc                            int3
额外

我用参数

lld-link
查看了
/mllvm:-print-after-all
的输出,在我看来,它只有在完成为 Rust 发出机器代码后才开始处理来自 C 的目标文件(并且它确实在链接期间优化了 C 和 Rust 代码,但是单独)。

我还尝试将

/LTCG
标志(MS
link.exe
lto 标志)传递给链接器,但没有成功。

如何在 pc-windows-msvc 上进行跨语言链接时间优化?

rust llvm lto clang-cl link-time-optimization
1个回答
0
投票

我找到了问题的根源:它是目标特征。当我将

-Ctarget-cpu=znver1
添加到 rustc 并将
/clang:-march=znver1
添加到 clang 时,LLVM 成功地在语言之间内联了函数。

据我了解,LLVM 拒绝 Rust 的内联函数,因为与 C 相比,它们需要更多的 CPU 功能。

因此,在 Windows 上成功 LTO 的最终命令: 中:

clang-cl /clang:-march=znver1 /clang:-std=c17 /clang:-O3 -c /D_CRT_SECURE_NO_WARNINGS=1 /clang:-flo=thin c_filename.c

对于生锈:

rustc -Copt-level=3 -Ctarget-cpu=znver1 -Ccodegen-units=1 -Clto=fat -Clinker-plugin-lto --crate-type=staticlib --emit=obj rust_filename.rs

链接:

lld-link c_filename.obj rust_filename.o

© www.soinside.com 2019 - 2024. All rights reserved.