-
Notifications
You must be signed in to change notification settings - Fork 13.6k
Description
Consider a function that returns Option<T>
where T is too large to be returned in registers, such as
const N: usize = 8;
pub fn fill_v(val: u64) -> Option<[u32; N]> {
let v = u32::try_from(val).ok()?;
Some(core::array::from_fn(|_| v))
}
Because [u32;8]
doesn't fit in the ABI's return value registers, instead it's returned by "invisible reference"; at the assembly level, caller provides fill_v
with two arguments, val
and also a pointer to space for the return value. Right now, when you return Option<[u32;8]>
, the Option
discriminator takes up part of that space:
playground::fill_v: # @playground::fill_v
# %bb.0:
movq %rdi, %rax
xorl %ecx, %ecx
movq %rsi, %rdx
shrq $32, %rdx
jne .LBB0_2
# %bb.1:
movd %esi, %xmm0
pshufd $0, %xmm0, %xmm0 # xmm0 = xmm0[0,0,0,0]
movdqu %xmm0, 4(%rax)
movdqu %xmm0, 20(%rax)
movl $1, %ecx
.LBB0_2:
movl %ecx, (%rax)
retq
(The invisible reference is the first argument at the assembly level, in %rdi
; the Rust-level first argument val
is in %rsi
.)
But the invisible reference argument is necessarily non-NULL, so we could apply the null pointer optimization for Option
in this case, even though we can't do it in general for Option<[T;N]>
, and generate code like this instead:
playground::fill_v: # @playground::fill_v
# %bb.0:
xorl %eax, %eax
movq %rsi, %rdx
shrq $32, %rdx
jne .LBB0_2
# %bb.1:
movd %esi, %xmm0
pshufd $0, %xmm0, %xmm0 # xmm0 = xmm0[0,0,0,0]
movdqu %xmm0, (%rdi)
movdqu %xmm0, 16(%rdi)
movq %rdi, %rax
.LBB0_2:
retq
This would save a word of stack space and would probably also be more efficient for the caller.
The example is an array, but the optimization is equally applicable to Option<LargeStruct>
, etc.
Meta
rustc --version --verbose
:
1.90.0-nightly (2025-07-21 9748d87dc70a9a6725c5)