-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Description
Currently, we pass everything that's smaller than a pointer as an immediate argument (except for fiexd size arrays, but that's a different issue) and everything else as non-immediate. This seems to be not quite optimal for structs like this:
#[deriving(Show)]
struct RGBA {
r: u8,
g: u8,
b: u8,
a: u8,
}
#[inline(never)]
fn gradient(from: RGBA, to: RGBA) {
println!("Gradient from {} to {}", from, to);
}
fn main() {
let from = RGBA { r: 0, g: 0, b: 0, a: 0 };
let to = RGBA { r: 255, g: 255, b: 255, a: 255 };
gradient(from, to);
gradient(to, from);
}
The calls end up being lowered to this:
movl $255, 8(%rsp)
movl $255, (%rsp)
xorl %edi, %edi
xorl %esi, %esi
xorl %edx, %edx
xorl %ecx, %ecx
movl $255, %r8d
movl $255, %r9d
callq _ZN8gradient20h9b7e3615552adffcPcaE
movl $0, 8(%rsp)
movl $0, (%rsp)
movl $255, %edi
movl $255, %esi
movl $255, %edx
movl $255, %ecx
xorl %r8d, %r8d
xorl %r9d, %r9d
callq _ZN8gradient20h9b7e3615552adffcPcaE
So to pass effectively 8 bytes, we need 6 registers and 16 bytes of stack.
For return values, this also means that such structs in fact isn't return as an immediate values in the eax
register, but in memory. Using 4 single byte movs (maybe coalescing those into a single store is something that could be handled in LLVM, but it still seems bad that it goes through memory).
Maybe the rust ABI should be re-evaluated and we need to add casts/coercions for small structs to pass them more efficiently. Passing e.g. slices in registers instead of memory also seems like it might be a good idea. I'm far off from being an expert in this area though, so feel free to correct me if things are fine as they are.