summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-09-27 02:35:11 +0000
committerbors <bors@rust-lang.org>2020-09-27 02:35:11 +0000
commit62fe055aba3ddac5e5d113920cf5fd80522104e2 (patch)
tree4545619f936a4860e19494c8f68720e366b44e90
parentAuto merge of #77247 - jonas-schievink:rollup-r6ehh8h, r=jonas-schievink (diff)
parentAdd a test for 128-bit return values (diff)
downloadrust-62fe055aba3ddac5e5d113920cf5fd80522104e2.tar.gz
rust-62fe055aba3ddac5e5d113920cf5fd80522104e2.tar.bz2
rust-62fe055aba3ddac5e5d113920cf5fd80522104e2.tar.xz
Auto merge of #76986 - jonas-schievink:ret-in-reg, r=nagisa
Return values up to 128 bits in registers This fixes https://github.com/rust-lang/rust/issues/26494#issuecomment-619506345 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 ```
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs12
-rw-r--r--compiler/rustc_target/src/abi/call/mod.rs12
-rw-r--r--src/test/codegen/return-value-in-reg.rs32
3 files changed, 52 insertions, 4 deletions
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 0fda147..ee669ed 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -2735,6 +2735,7 @@ where
2735 can_unwind: fn_can_unwind(cx.tcx().sess.panic_strategy(), codegen_fn_attr_flags, conv), 2735 can_unwind: fn_can_unwind(cx.tcx().sess.panic_strategy(), codegen_fn_attr_flags, conv),
2736 }; 2736 };
2737 fn_abi.adjust_for_abi(cx, sig.abi); 2737 fn_abi.adjust_for_abi(cx, sig.abi);
2738 debug!("FnAbi::new_internal = {:?}", fn_abi);
2738 fn_abi 2739 fn_abi
2739 } 2740 }
2740 2741
@@ -2748,7 +2749,7 @@ where
2748 || abi == SpecAbi::RustIntrinsic 2749 || abi == SpecAbi::RustIntrinsic
2749 || abi == SpecAbi::PlatformIntrinsic 2750 || abi == SpecAbi::PlatformIntrinsic
2750 { 2751 {
2751 let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>| { 2752 let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>, is_ret: bool| {
2752 if arg.is_ignore() { 2753 if arg.is_ignore() {
2753 return; 2754 return;
2754 } 2755 }
@@ -2786,8 +2787,11 @@ where
2786 _ => return, 2787 _ => return,
2787 } 2788 }
2788 2789
2790 let max_by_val_size =
2791 if is_ret { call::max_ret_by_val(cx) } else { Pointer.size(cx) };
2789 let size = arg.layout.size; 2792 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 {
2791 arg.make_indirect(); 2795 arg.make_indirect();
2792 } else { 2796 } else {
2793 // We want to pass small aggregates as immediates, but using 2797 // We want to pass small aggregates as immediates, but using
@@ -2796,9 +2800,9 @@ where
2796 arg.cast_to(Reg { kind: RegKind::Integer, size }); 2800 arg.cast_to(Reg { kind: RegKind::Integer, size });
2797 } 2801 }
2798 }; 2802 };
2799 fixup(&mut self.ret); 2803 fixup(&mut self.ret, true);
2800 for arg in &mut self.args { 2804 for arg in &mut self.args {
2801 fixup(arg); 2805 fixup(arg, false);
2802 } 2806 }
2803 if let PassMode::Indirect(ref mut attrs, _) = self.ret.mode { 2807 if let PassMode::Indirect(ref mut attrs, _) = self.ret.mode {
2804 attrs.set(ArgAttribute::StructRet); 2808 attrs.set(ArgAttribute::StructRet);
diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs
index 8f7e2bb..602c424 100644
--- a/compiler/rustc_target/src/abi/call/mod.rs
+++ b/compiler/rustc_target/src/abi/call/mod.rs
@@ -610,3 +610,15 @@ impl<'a, Ty> FnAbi<'a, Ty> {
610 Ok(()) 610 Ok(())
611 } 611 }
612} 612}
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.
617pub 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}
diff --git a/src/test/codegen/return-value-in-reg.rs b/src/test/codegen/return-value-in-reg.rs
new file mode 100644
index 0000000..4bc0136
--- /dev/null
+++ b/src/test/codegen/return-value-in-reg.rs
@@ -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
8pub 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]
16pub 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)]
21pub 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]
30pub 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}