rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.85k stars 12.66k forks source link

llvm noalias data gets lost when passing large structs #131905

Open sarah-quinones opened 2 days ago

sarah-quinones commented 2 days ago

https://godbolt.org/z/b8as1TxdP

#[repr(C)]
pub struct Big<'a>(&'a mut u64, &'a mut u64, usize);

#[inline(never)]
pub unsafe fn bad(a: Big) -> u64 {
    *a.0 = 0;
    *a.1 = 1;
    *a.0
}

#[repr(C)]
pub struct Good<'a>(&'a mut u64, &'a mut u64);

#[inline(never)]
pub unsafe fn good(a: Good) -> u64 {
    *a.0 = 0;
    *a.1 = 1;
    *a.0
}
example::bad::h15eeefb1851d2a2e:
        mov     rax, qword ptr [rdi]
        mov     qword ptr [rax], 0
        mov     rcx, qword ptr [rdi + 8]
        mov     qword ptr [rcx], 1
        mov     rax, qword ptr [rax]
        ret

example::good::h034657cbabb42f00:
        mov     qword ptr [rdi], 0
        mov     qword ptr [rsi], 1
        xor     eax, eax
        ret

i expected the functions to emit similar codegen, but the bad one doesn't get the noalias on the references themselves because llvm thinks they're passed by pointer since the struct is large.

maybe alias.scope can help with this?

Meta

rustc --version --verbose:

rustc 1.84.0-nightly (3ed6e3cc6 2024-10-17)
binary: rustc
commit-hash: 3ed6e3cc69857129c1d314daec00119ff47986ed
commit-date: 2024-10-17
host: x86_64-unknown-linux-gnu
release: 1.84.0-nightly
LLVM version: 19.1.1
Compiler returned: 0
saethlin commented 2 days ago

because llvm thinks they're passed by pointer since the struct is large.

LLVM doesn't just think this, it knows that the struct has been passed by pointer. The rustc ABI (at least for cg_ssa) has Scalar, ScalarPair, and Aggregate ABIs (this link will probably rot but here's the ABI enum currently: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_abi/enum.Abi.html). Your type Good is a ScalarPair and your Big is an Aggregate, and that ABI is always by-pointer.

So there might be another solution here: Figure out how to splat types with more than 2 fields.

sarah-quinones commented 1 day ago

is there room for an opt-in attribute that tells rustc that i want to pass a struct as scalars (in the rust abi)? for example

#[repr(scalar)]
struct Matrix {
  ptr: *mut f64,
  nrows: usize,
  ncols: usize,
  row_stride: isize,
  col_stride: isize,
}
saethlin commented 1 day ago

Sure. But the hard part here is implementing that, not coming up with what the attribute looks like.

bjorn3 commented 1 day ago

For arguments it would be possible to use PassMode::Cast (this is something internal to the abi computation code in rustc), but this would cause worse performance in unoptimized builds as our PassMode::Cast handling is very naive. It basically writes your entire value to the stack and then reloads the values for the individual registers to pass as arguments and does the opposite on the callee side.