私が知る限り、参照/ポインタのエイリアスは、最適化されたコードを生成するコンパイラの機能を妨げる可能性があります。これは、2つの参照/ポインタが実際にエイリアスである場合に、生成されたバイナリが正しく動作することを保証する必要があるためです。たとえば、次の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)
はケースint *a
とint *b
エイリアスで2倍に保存します。
これらの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
これは、その保証を活用していないa
とb
エイリアスすることはできません。
これは、現在のRustコンパイラがまだ開発中で、最適化を行うためのエイリアス分析をまだ組み込んでいないためですか?
そこにチャンスがまだあることからですa
とb
、エイリアスができたとしても、安全ルーストに、?
unsafe
コードの中でさえ、可変参照のエイリアスは許可されておらず、未定義の動作を引き起こします。生のポインタにエイリアスを設定することはできますが、unsafe
コードでは実際にはRustの標準ルールを無視できません。これはよくある誤解であり、指摘する価値があります。
+=
の本体の2つの操作をadds
として再解釈できるかどうか*a = *a + *b + *b
です。ポインターがエイリアスしない場合、エイリアスが可能ですb* + *b
。2番目のasmリストで何になるかを確認することもできます2: 01 c0 add %eax,%eax
。しかし、エイリアスを作成する場合、エイリアスを追加することはできません。これ*b
は、2回目に追加するときまでに、初回(4:
最初のasmリストの行に保存する値)とは異なる値が含まれるためです。