如何真正禁用所有 rustc 优化?
我正在尝试通过编译 Rust 来学习汇编。我找到了一种将 Rust 代码编译为二进制机器代码的方法,并且能够通过 objdump 来查看程序集。但是,如果我写以下内容:
#![no_main]
#[link_section = ".text.entry"]
#[no_mangle]
pub extern "C" fn _start() -> ! {
let a: u64 = 4;
let b: u64 = 7;
let c: u64 = a * b;
loop {}
}
我得到的程序集是:
0000000000000000 <.data>:
0: 1101 addi sp,sp,-32
2: 4511 li a0,4
4: e42a sd a0,8(sp)
6: 451d li a0,7
8: e82a sd a0,16(sp)
a: 4571 li a0,28
c: ec2a sd a0,24(sp)
e: a009 j 0x10
10: a001 j 0x10
所以看起来 rust 正在将 mul 压缩为常数。我正在使用以下编译选项:
Cargo.toml:
[profile.dev]
opt-level = 0
mir-opt-level = 0
有没有办法阻止 Rust 对此进行优化?
发出的 LLVM 看起来像这样:
; Function Attrs: noreturn nounwind
define dso_local void @_start() unnamed_addr #0 section ".text.entry" !dbg !22 {
start:
%c.dbg.spill = alloca i64, align 8
%b.dbg.spill = alloca i64, align 8
%a.dbg.spill = alloca i64, align 8
store i64 4, i64* %a.dbg.spill, align 8, !dbg !36
call void @llvm.dbg.declare(metadata i64* %a.dbg.spill, metadata !28, metadata !DIExpression()), !dbg !37
store i64 7, i64* %b.dbg.spill, align 8, !dbg !38
call void @llvm.dbg.declare(metadata i64* %b.dbg.spill, metadata !31, metadata !DIExpression()), !dbg !39
store i64 28, i64* %c.dbg.spill, align 8, !dbg !40
call void @llvm.dbg.declare(metadata i64* %c.dbg.spill, metadata !33, metadata !DIExpression()), !dbg !41
所以看起来优化是在 LLVM 传递之前进行的。
$ rustc --version
rustc 1.60.0-nightly (c5c610aad 2022-02-14)
构建命令:
RUSTFLAGS="--emit=llvm-bc" cargo build --target riscv64imac-unknown-none-elf --no-default-features
build.rs
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rustc-link-arg=-Tlink.ld");
}
link.ld
ENTRY(_start)
SECTIONS {
.text : { *(.text); *(.text.*) }
}
I'm trying to learn assembly through compiling Rust. I have found a way to compile Rust code to binary machine code and be able to objdump
it to view the assembly. However if I write the following:
#![no_main]
#[link_section = ".text.entry"]
#[no_mangle]
pub extern "C" fn _start() -> ! {
let a: u64 = 4;
let b: u64 = 7;
let c: u64 = a * b;
loop {}
}
The assembly I get is:
0000000000000000 <.data>:
0: 1101 addi sp,sp,-32
2: 4511 li a0,4
4: e42a sd a0,8(sp)
6: 451d li a0,7
8: e82a sd a0,16(sp)
a: 4571 li a0,28
c: ec2a sd a0,24(sp)
e: a009 j 0x10
10: a001 j 0x10
So it looks like rust is collapsing the mul to a constant. I'm using the following compile options:
Cargo.toml:
[profile.dev]
opt-level = 0
mir-opt-level = 0
Is there a way to stop Rust from optimizing this?
The LLVM emitted looks like this:
; Function Attrs: noreturn nounwind
define dso_local void @_start() unnamed_addr #0 section ".text.entry" !dbg !22 {
start:
%c.dbg.spill = alloca i64, align 8
%b.dbg.spill = alloca i64, align 8
%a.dbg.spill = alloca i64, align 8
store i64 4, i64* %a.dbg.spill, align 8, !dbg !36
call void @llvm.dbg.declare(metadata i64* %a.dbg.spill, metadata !28, metadata !DIExpression()), !dbg !37
store i64 7, i64* %b.dbg.spill, align 8, !dbg !38
call void @llvm.dbg.declare(metadata i64* %b.dbg.spill, metadata !31, metadata !DIExpression()), !dbg !39
store i64 28, i64* %c.dbg.spill, align 8, !dbg !40
call void @llvm.dbg.declare(metadata i64* %c.dbg.spill, metadata !33, metadata !DIExpression()), !dbg !41
So it looks like the optimization is before the LLVM pass.
$ rustc --version
rustc 1.60.0-nightly (c5c610aad 2022-02-14)
Command to build:
RUSTFLAGS="--emit=llvm-bc" cargo build --target riscv64imac-unknown-none-elf --no-default-features
build.rs
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rustc-link-arg=-Tlink.ld");
}
link.ld
ENTRY(_start)
SECTIONS {
.text : { *(.text); *(.text.*) }
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在生成 LLVM-IR 之前有一个编译器通道,即生成 MIR,Rust 中间表示。如果您使用如下命令为给定代码发出此命令:
您将在生成的 .mir 文件中看到优化已经发生。
发生这种情况是因为
mir-opt-level
选项当前仅作为不稳定的编译器选项存在。它不能在 Cargo 中作为配置文件属性使用。在直接调用编译器时手动设置它:并且这种优化将会消失:
这可能是您在不直接接触 LLVM 的情况下可以做到的最大程度。代码特定部分的某些优化也可以通过诸如
black_box
。另请参阅:
There is one compiler pass before the generation of LLVM-IR, which is the generation of MIR, the Rust intermediate representation. If you emit this for the given code with a command such as this one:
You will see in the .mir file generated that the optimization already took place there.
This is happening because the
mir-opt-level
option currently only exists as an unstable compiler option. It is not available as a profile property in Cargo. Set it manually on a direct call to the compiler:And this optimization will disappear:
And this is probably as far as you can go without touching LLVM directly. Some optimisations in specific parts of the code can also be prevented through constructs such as
black_box
.See also: