假设两个可变引用都不能别名,Rust编译器为什么不优化代码? [英] Why does the Rust compiler not optimize code assuming that two mutable references cannot alias?
问题描述
据我所知,引用/指针别名会阻碍编译器生成优化代码的能力,因为它们必须确保在两个引用/指针确实为别名的情况下,生成的二进制文件的行为正确。例如,在以下C代码中,
void add(int * a,int * b ){
* a + = * b;
* a + = * b;
}
由 clang版本6.0.0-1ubuntu2编译时(标签/ RELEASE_600 / final)
带有 -O3
标志,它会发出
0000000000000000< adds> ;:
0:8b 07 mov(%rdi),%eax
2:03 06 add(%rsi ),%eax
4:89 07 mov%eax,(%rdi)#第一次
6:03 06加(%rsi),%eax
8:89 07 mov% eax,(%rdi)#第二次
a:c3 retq
这里代码存储返回(%rdi)
两次,以防 int * a
和 int * b
别名。
当我们明确告诉编译器这两个指针不能使用 restrict
进行别名时关键字:
无效添加(int *限制a,int *限制b){
* a + = * b;
* a + = * b;
}
然后Clang将发出二进制代码的更优化版本:
0000000000000000< adds> ;:
0:8b 06 mov(%rsi),%eax
2:01 c0添加%eax,%eax
4:01 07添加%eax,(%rdi)
6:c3 retq
由于Rust确保(可变代码除外)两个可变引用不能互为别名,因此我认为编译器应该能够发出更优化的版本。
当我测试以下代码并使用 rustc 1.35.0
和<$ c进行编译时$ c> -C opt-level = 3 --emit obj ,
#![crate_type = staticlib]
#[no_mangle]
fn增加(a:& mut i32,b:& mut i32){
* a + = * b;
* a + = * b;
}
它会生成:
< pre class = lang-none prettyprint-override>
0000000000000000< adds> ;:
0:8b 07 mov(%rdi),%eax
2:03 06添加( %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编译器仍在开发中,还没有合并别名分析来进行优化吗?
这是因为仍有机会 a
和 b
即使在安全的Rust中也可以使用别名?
原始 did 启用LLVM的 noalias
属性,但是此导致了错误编译的代码。当所有受支持的LLVM版本不再重新编译代码时,它将重新启用
如果在编译器选项中添加 -Zmutable-noalias = yes
,则会得到预期的汇编: / p>
添加:
mov eax,dword ptr [rsi]
add eax ,eax
添加dword ptr [rdi],eax
ret
,Rust将C的 restrict
关键字 Everywhere 等同于其他任何C程序。这对LLVM的极端情况产生了超出其正确处理能力的影响。事实证明,C和C ++程序员根本不像在Rust中使用& mut
那样频繁地使用 restrict
。
这已经发生了多次。
- Rust 1.0到1.7 —启用
noalias
- Rust 1.8到1.27 —
noalias
已禁用 - 铁锈1.28至1.29-启用
noalias
- 铁锈1.30至??? —
noalias
已禁用
相关的Rust问题
-
当前情况
-
以前的情况
- 不通过将& mut指针标记为noalias#31545 $ b来解决LLVM优化器错误$ b
- 一旦LLVM不再对它们进行编译,将& mut指针标记为noalias#31681
-
其他
As far as I know, reference/pointer aliasing can hinder the compiler's ability to generate optimized code, since they must ensure the generated binary behaves correctly in the case where the two references/pointers indeed alias. For instance, in the following C code,
void adds(int *a, int *b) {
*a += *b;
*a += *b;
}
when compiled by clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
with the -O3
flag, it emits
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
Here the code stores back to (%rdi)
twice in case int *a
and int *b
alias.
When we explicitly tell the compiler that these two pointers cannot alias with the restrict
keyword:
void adds(int * restrict a, int * restrict b) {
*a += *b;
*a += *b;
}
Then Clang will emit a more optimized version of the binary code:
0000000000000000 <adds>:
0: 8b 06 mov (%rsi),%eax
2: 01 c0 add %eax,%eax
4: 01 07 add %eax,(%rdi)
6: c3 retq
Since Rust makes sure (except in unsafe code) that two mutable references cannot alias, I would think that the compiler should be able to emit the more optimized version of the code.
When I test with the code below and compile it with rustc 1.35.0
with -C opt-level=3 --emit obj
,
#![crate_type = "staticlib"]
#[no_mangle]
fn adds(a: &mut i32, b: &mut i32) {
*a += *b;
*a += *b;
}
it generates:
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
This does not take advantage of the guarantee that a
and b
cannot alias.
Is this because the current Rust compiler is still in development and has not yet incorporated alias analysis to do the optimization?
Is this because there is still a chance that a
and b
could alias, even in safe Rust?
Rust originally did enable LLVM's noalias
attribute, but this caused miscompiled code. When all supported LLVM versions no longer miscompile the code, it will be re-enabled.
If you add -Zmutable-noalias=yes
to the compiler options, you get the expected assembly:
adds:
mov eax, dword ptr [rsi]
add eax, eax
add dword ptr [rdi], eax
ret
Simply put, Rust put the equivalent of C's restrict
keyword everywhere, far more prevalent than any usual C program. This exercised corner cases of LLVM more than it was able to handle correctly. It turns out that C and C++ programmers simply don't use restrict
as frequently as &mut
is used in Rust.
This has happened multiple times.
- Rust 1.0 through 1.7 —
noalias
enabled - Rust 1.8 through 1.27 —
noalias
disabled - Rust 1.28 through 1.29 —
noalias
enabled - Rust 1.30 through ??? —
noalias
disabled
Related Rust issues
Current case
Previous case
Other
这篇关于假设两个可变引用都不能别名,Rust编译器为什么不优化代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!