次にポインタエイリアスの例を示します。
pub unsafe fn f(a: *mut i32, b: *mut i32, x: *const i32) {
*a = *x;
*b = *x;
}
は次のアセンブリにコンパイルされます ( を使用-C opt-level=s
)。
example::f:
push rbp
mov rbp, rsp
mov eax, dword ptr [rdx]
mov dword ptr [rdi], eax
mov eax, dword ptr [rdx]
mov dword ptr [rsi], eax
pop rbp
ret
が 2 回参照解除されていることに注意してくださいx
。LLVM はこれを として扱いませんnoalias
。最初に考えたのは、割り当てでポインタを使用せず、代わりに安全な参照を使用することです (これらは "LLVMのスコープnoalias
モデルに従う") を使ってオプティマイザにヒントを与えます:
pub fn g(a: *mut i32, b: *mut i32, x: *const i32) {
let safe_a = unsafe { &mut *a };
let safe_b = unsafe { &mut *b };
let safe_x = unsafe { &*x };
*safe_a = *safe_x;
*safe_b = *safe_x;
}
しかし残念なことに、これはまったく同じ結果を生成します。safe_x
依然として 2 回参照解除されます。
このサンプル コードが愚かであることはわかっています。パラメーターは簡単に&i32
/に変更する&mut i32
か、x
一度逆参照して、割り当てに使用される一時的な場所に格納することができます。ここのコードは、非常に単純なエイリアシング テストを目的としており、私の質問が尋ねているより広い範囲に興味があります。
ベストアンサー1
安全な参照を関数またはクロージャでラップします。
pub unsafe fn f(a: *mut i32, b: *mut i32, x: *const i32) {
(|safe_a: &mut i32, safe_b: &mut i32, safe_x: &i32| {
*safe_a = *safe_x;
*safe_b = *safe_x;
})(&mut *a, &mut *b, &*x)
}
これにより、必要な非エイリアシング動作が生成されます。
example::f:
movl (%rdx), %eax
movl %eax, (%rdi)
movl %eax, (%rsi)
retq