26

次のポインターのエイリアシングの例:

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

xが 2 回逆参照されていることに注意してください。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して、割り当てに使用される一時変数に格納することもできます。ここのコードは、非常に単純なエイリアシング テストを意図したものであり、私の質問が求めているより広い視野に興味があります。

4

1 に答える 1

3

関数またはクロージャーで安全な参照をラップします。

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
于 2018-09-20T16:54:55.193 に答える