假设两个可变引用都不能别名,Rust编译器为什么不优化代码? [英] Why does the Rust compiler not optimize code assuming that two mutable references cannot alias?

查看:106
本文介绍了假设两个可变引用都不能别名,Rust编译器为什么不优化代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

据我所知,引用/指针别名会阻碍编译器生成优化代码的能力,因为它们必须确保在两个引用/指针确实为别名的情况下,生成的二进制文件的行为正确。例如,在以下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问题




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

这篇关于假设两个可变引用都不能别名,Rust编译器为什么不优化代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆