[英]Why does the Rust compiler error with "cannot borrow as immutable because it is also borrowed as mutable" after moving the variable into a scope?
[英]Why does the Rust compiler not optimize code assuming that two mutable references cannot alias?
据我所知,引用/指针别名会阻碍编译器生成优化代码的能力,因为它们必须确保生成的二进制文件在两个引用/指针确实别名的情况下正确运行。 例如,在下面的 C 代码中,
void adds(int *a, int *b) {
*a += *b;
*a += *b;
}
当使用-O3
标志由clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
编译时,它发出
0000000000000000 <adds>:
0: 8b 07 mov (%rdi),%eax
2: 03 06 add (%rsi),%eax
4: 89 07 mov %eax,(%rdi) # The first time
6: 03 06 add (%rsi),%eax
8: 89 07 mov %eax,(%rdi) # The second time
a: c3 retq
这里代码存储回(%rdi)
两次,以防int *a
和int *b
别名。
当我们明确告诉编译器这两个指针不能用restrict
关键字做别名时:
void adds(int * restrict a, int * restrict b) {
*a += *b;
*a += *b;
}
然后 Clang 将发出二进制代码的更优化版本:
0000000000000000 <adds>:
0: 8b 06 mov (%rsi),%eax
2: 01 c0 add %eax,%eax
4: 01 07 add %eax,(%rdi)
6: c3 retq
由于 Rust 确保(除了在不安全代码中)两个可变引用不能别名,我认为编译器应该能够发出更优化的代码版本。
当我使用下面的代码进行测试并使用rustc 1.35.0
和-C opt-level=3 --emit obj
编译它时,
#![crate_type = "staticlib"]
#[no_mangle]
fn adds(a: &mut i32, b: &mut i32) {
*a += *b;
*a += *b;
}
它产生:
0000000000000000 <adds>:
0: 8b 07 mov (%rdi),%eax
2: 03 06 add (%rsi),%eax
4: 89 07 mov %eax,(%rdi)
6: 03 06 add (%rsi),%eax
8: 89 07 mov %eax,(%rdi)
a: c3 retq
这没有利用a
和b
不能别名的保证。
这是因为当前的 Rust 编译器仍在开发中,还没有结合别名分析来进行优化吗?
这是因为即使在安全的 Rust 中, a
和b
仍然有可能别名吗?
Rust 最初确实启用了 LLVM 的noalias
属性,但这会导致错误编译的代码。 当所有支持的 LLVM 版本不再错误编译代码时,它将重新启用。
如果将-Zmutable-noalias=yes
添加到编译器选项,您将获得预期的程序集:
adds:
mov eax, dword ptr [rsi]
add eax, eax
add dword ptr [rdi], eax
ret
简而言之,Rust 在任何地方都放置了相当于 C 的restrict
关键字,比任何普通的 C 程序都流行得多。 这对 LLVM 的极端情况进行了超出其正确处理的能力。 事实证明,C 和 C++ 程序员根本不会像 Rust 中使用&mut
那样频繁地使用restrict
。
这已经发生了多次。
noalias
noalias
noalias
noalias
noalias
当前案例
上一案例
其他
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.