Skip to content

Commit 62fe055

Browse files
committed
Auto merge of #76986 - jonas-schievink:ret-in-reg, r=nagisa
Return values up to 128 bits in registers This fixes #26494 (comment) by making Rust's default ABI pass return values up to 128 bits in size in registers, just like the System V ABI. The result is that these methods from the comment linked above now generate the same code, making the Rust ABI as efficient as the `"C"` ABI: ```rust pub struct Stats { x: u32, y: u32, z: u32, } pub extern "C" fn sum_c(a: &Stats, b: &Stats) -> Stats { return Stats {x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }; } pub fn sum_rust(a: &Stats, b: &Stats) -> Stats { return Stats {x: a.x + b.x, y: a.y + b.y, z: a.z + b.z }; } ``` ```asm sum_rust: movl (%rsi), %eax addl (%rdi), %eax movl 4(%rsi), %ecx addl 4(%rdi), %ecx movl 8(%rsi), %edx addl 8(%rdi), %edx shlq $32, %rcx orq %rcx, %rax retq ```
2 parents 1ec980d + cc2ba3b commit 62fe055

File tree

3 files changed

+52
-4
lines changed

3 files changed

+52
-4
lines changed

compiler/rustc_middle/src/ty/layout.rs

+8-4
Original file line numberDiff line numberDiff line change
@@ -2735,6 +2735,7 @@ where
27352735
can_unwind: fn_can_unwind(cx.tcx().sess.panic_strategy(), codegen_fn_attr_flags, conv),
27362736
};
27372737
fn_abi.adjust_for_abi(cx, sig.abi);
2738+
debug!("FnAbi::new_internal = {:?}", fn_abi);
27382739
fn_abi
27392740
}
27402741

@@ -2748,7 +2749,7 @@ where
27482749
|| abi == SpecAbi::RustIntrinsic
27492750
|| abi == SpecAbi::PlatformIntrinsic
27502751
{
2751-
let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>| {
2752+
let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>, is_ret: bool| {
27522753
if arg.is_ignore() {
27532754
return;
27542755
}
@@ -2786,8 +2787,11 @@ where
27862787
_ => return,
27872788
}
27882789

2790+
let max_by_val_size =
2791+
if is_ret { call::max_ret_by_val(cx) } else { Pointer.size(cx) };
27892792
let size = arg.layout.size;
2790-
if arg.layout.is_unsized() || size > Pointer.size(cx) {
2793+
2794+
if arg.layout.is_unsized() || size > max_by_val_size {
27912795
arg.make_indirect();
27922796
} else {
27932797
// We want to pass small aggregates as immediates, but using
@@ -2796,9 +2800,9 @@ where
27962800
arg.cast_to(Reg { kind: RegKind::Integer, size });
27972801
}
27982802
};
2799-
fixup(&mut self.ret);
2803+
fixup(&mut self.ret, true);
28002804
for arg in &mut self.args {
2801-
fixup(arg);
2805+
fixup(arg, false);
28022806
}
28032807
if let PassMode::Indirect(ref mut attrs, _) = self.ret.mode {
28042808
attrs.set(ArgAttribute::StructRet);

compiler/rustc_target/src/abi/call/mod.rs

+12
Original file line numberDiff line numberDiff line change
@@ -610,3 +610,15 @@ impl<'a, Ty> FnAbi<'a, Ty> {
610610
Ok(())
611611
}
612612
}
613+
614+
/// Returns the maximum size of return values to be passed by value in the Rust ABI.
615+
///
616+
/// Return values beyond this size will use an implicit out-pointer instead.
617+
pub fn max_ret_by_val<C: HasTargetSpec + HasDataLayout>(spec: &C) -> Size {
618+
match spec.target_spec().arch.as_str() {
619+
// System-V will pass return values up to 128 bits in RAX/RDX.
620+
"x86_64" => Size::from_bits(128),
621+
622+
_ => spec.data_layout().pointer_size,
623+
}
624+
}
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//! This test checks that types of up to 128 bits are returned by-value instead of via out-pointer.
2+
3+
// compile-flags: -C no-prepopulate-passes -O
4+
// only-x86_64
5+
6+
#![crate_type = "lib"]
7+
8+
pub struct S {
9+
a: u64,
10+
b: u32,
11+
c: u32,
12+
}
13+
14+
// CHECK: define i128 @modify(%S* noalias nocapture dereferenceable(16) %s)
15+
#[no_mangle]
16+
pub fn modify(s: S) -> S {
17+
S { a: s.a + s.a, b: s.b + s.b, c: s.c + s.c }
18+
}
19+
20+
#[repr(packed)]
21+
pub struct TooBig {
22+
a: u64,
23+
b: u32,
24+
c: u32,
25+
d: u8,
26+
}
27+
28+
// CHECK: define void @m_big(%TooBig* [[ATTRS:.*sret.*]], %TooBig* [[ATTRS2:.*]] %s)
29+
#[no_mangle]
30+
pub fn m_big(s: TooBig) -> TooBig {
31+
TooBig { a: s.a + s.a, b: s.b + s.b, c: s.c + s.c, d: s.d + s.d }
32+
}

0 commit comments

Comments
 (0)