1. ホーム
  2. rust

[解決済み] なぜRustコンパイラは、2つのミュータブル参照がエイリアスできないと仮定してコードを最適化しないのですか?

2022-03-15 20:48:04

質問

私の知る限り、参照/ポインタのエイリアシングはコンパイラの最適化コード生成の妨げになることがあります。例えば、次のような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
   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) の場合、2回 int *aint *b のエイリアスです。

この2つのポインタがエイリアスできないことをコンパイラに明示的に伝えると、そのポインタは 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は(安全でないコードを除いて)2つのミュータブル参照をエイリアスできないようにしているので、コンパイラはより最適化されたバージョンのコードを出力できるはずだと思うのですが、いかがでしょうか。

以下のコードでテストして、コンパイル時に 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でもエイリアスできるのでしょうか?

どのように解決するのですか?

元々錆は した。 は、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のコーナーケースは、LLVMが正しく処理できる以上の力を発揮しました。CとC++のプログラマは、単に restrict のように頻繁に &mut はRustで使われています。

このようなことがありました 複数回 .

  • 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関連問題