为什么Rust编译器不优化代码假设两个可变引用不能别名?

据我所知,引用/指针别名会阻碍编译器生成优化代码的能力,因为它们必须确保在两个引用/指针确实别名的情况下生成的二进制行为正确。例如,在下面的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  # load a into EAX
2:    03 06                    add    (%rsi),%eax  # load-and-add b
4:    89 07                    mov    %eax,(%rdi)  # store into a
6:    03 06                    add    (%rsi),%eax  # load-and-add b again
8:    89 07                    mov    %eax,(%rdi)  # store into a again
a:    c3                       retq

在这里,代码存储回(%rdi)两次,以防int *aint *b别名。

当显式地告诉编译器这两个指针不能用restrict关键字作为别名时:

void adds(int *restrict a, int *restrict b) {
*a += *b;
*a += *b;
}

然后Clang将发出一个更优化的版本,有效地执行*a += 2 * (*b),如果(正如restrict所承诺的那样)*b没有通过赋值给*a而被修改,则等效:

0000000000000000 <adds>:
0:    8b 06                    mov    (%rsi),%eax   # load b once
2:    01 c0                    add    %eax,%eax     # double it
4:    01 07                    add    %eax,(%rdi)   # *a += 2 * (*b)
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

这没有利用ab不能别名的保证。

这是因为当前Rust编译器仍在开发中,还没有结合别名分析来进行优化吗?

这是因为仍然有机会ab可以别名,即使在安全的Rust?

49399 次浏览

Rust最初的做了启用了LLVM的noalias属性,但是这个导致错误编译的代码。当所有受支持的LLVM版本不再错误编译代码时,它将被重新启用. xml

如果你将-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

这已经发生了多次

  • Rust 1.0到1.7 - noalias启用
  • Rust 1.8到1.27 - noalias被禁用
  • Rust 1.28到1.29 - noalias启用
  • Rust 1.30到1.54 - noalias禁用
  • Rust 1.54通过??-根据编译器使用的LLVM版本,有条件地启用noalias

相关Rust问题