Missed optimization: unnecessary copies returning 3-tuple of u32
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
This function provides a safe wrapper for the (Linux-specific) system call getresuid.
use libc::uid_t;
use std::mem::MaybeUninit;
pub fn getresuid() -> (uid_t, uid_t, uid_t) {
let mut ruid = MaybeUninit::uninit();
let mut euid = MaybeUninit::uninit();
let mut suid = MaybeUninit::uninit();
unsafe {
let _ = libc::getresuid(ruid.as_mut_ptr(), euid.as_mut_ptr(), suid.as_mut_ptr());
(ruid.assume_init(), euid.assume_init(), suid.assume_init())
}
}
With rustc 1.87.0-nightly (2025-02-22 46420c96070b4c4bd824), on x86-64, suboptimal assembly code is generated:
playground::getresuid:
pushq %rbx
subq $16, %rsp
movq %rdi, %rbx
leaq 8(%rsp), %rsi
leaq 12(%rsp), %rdx
callq *getresuid@GOTPCREL(%rip)
movl 8(%rsp), %eax
movl 12(%rsp), %ecx
movl %eax, 4(%rbx)
movl %ecx, 8(%rbx)
movq %rbx, %rax
addq $16, %rsp
popq %rbx
retq
The 3-tuple return value is being returned by "invisible reference", i.e. caller supplied a pointer to space for 3 uid_t (== u32) quantities. The compiler could have had libc::getresuid write all three of its outputs directly into that space, but instead it only had it write one of its outputs directly into that space, and supplied scratch stack locations for the other two, which then had to be copied into place.
Better code would be like this:
playground::getresuid:
pushq %rbx
movq %rdi, %rbx
leaq 4(%rdi), %rsi
leaq 8(%rdi), %rdx
callq *getresuid@GOTPCREL(%rip)
movq %rbx, %rax
popq %rbx
retq
This does not seem to be a regression, at least not from current stable.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the getresuid entry point and compare the current x86-64 assembly with the desired assembly shown in the issue. Trace how Rust lowers the three-u32 tuple return and libc call, then verify completion by reproducing the Playground example and confirming that the unnecessary stack copies are gone.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100