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

Why does the Rust compiler not optimize code assuming that two mutable references cannot alias?

据我所知,reference/pointer 别名会阻碍编译器生成优化代码的能力,因为它们必须确保生成的二进制文件在两个 references/pointers 确实是别名的情况下正确运行。例如,在下面的 C 代码中,

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

clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final) 使用 -O3 标志编译时,它发出

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 未通过分配给 [=] 进行修改,则等效25=]:

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 编译器还在开发中,还没有结合别名分析来做优化吗?

这是因为即使在安全的 Rust 中,ab 仍然有可能混叠吗?

Rust 最初 确实 启用了 LLVM 的 noalias 属性,但是这个 caused miscompiled code. When all supported LLVM versions no longer miscompile the code, it will be re-enabled.

如果您将 -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 通过 ??? — noalias 根据编译器使用的 LLVM 版本有条件地启用

相关的 Rust 问题