Is there a way to get Rust to treat pointers as non-aliasing, so it can mark them as `noalias` for LLVM's optimizer?

748 views Asked by At

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.

1

There are 1 answers

4
orlp On

There is, wrap the safe reference in a function or closure:

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)
}

This produces the wanted non-aliasing behavior:

example::f:
        movl    (%rdx), %eax
        movl    %eax, (%rdi)
        movl    %eax, (%rsi)
        retq