The following example of pointer aliasing:
pub unsafe fn f(a: *mut i32, b: *mut i32, x: *const i32) {
*a = *x;
*b = *x;
}
compiles into the following assembly (with -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
Notice that x
is being dereferenced twice. LLVM is not treating it as noalias
. My first thought was to avoid using pointers in the assignments and instead use safe references (since those "follow LLVM’s scoped noalias
model") to give a hint to the optimizer:
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;
}
But alas, this produces the exact same result. safe_x
is still dereferenced twice.
I know this example code is dumb. The parameters could easily be changed to &i32
/&mut i32
, or I could just dereference x
once and store it in a temporary that is used for the assignment. The code here is just meant to be a super simple aliasing test, and I'm interested in the broader picture my question is asking.
There is, wrap the safe reference in a function or closure:
This produces the wanted non-aliasing behavior: