rust-lang / rust-lang/rust

Missed optimization: unnecessary copies returning 3-tuple of u32

Open
#137,488 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

C-optimization T-compiler
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

Playground link.

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.