Change:
Rust should pass more structs in registers. Consider these examples: ideally, both functions would execute entirely in registers and wouldn't touch memory: Rust, as of a recent 1.2.0-dev nightly, is unable to pass either of these in registers (see LLVM IR and ASM below). It would be pretty safe to pass and return small structs (ones that fit into <=2 registers) in registers, and is likely to improve performance on average. This is what the System V ABI does. It would also be nice to exploit Rust's control over aliasing, and where possible also promote reference arguments to registers, i.e. put the values in registers for the following functions: In the case, this would mean passing two values in registers as function parameters, and returning two values in registers as the return values (ideally we'd arrange for the parameter registers to match the return registers). Uniqueness of makes this optimization valid, although we may have to give up on this optimization in cases such as when there are raw pointers present. Here's a more realistic example where I've wanted Rust to do this: This function is too large to justify inlining. I'd like the begin and end pointers of to be kept in registers across the function call. Probably not surprising to the compiler team, but for completeness here is the LLVM IR of the above snippets, as of today's Rust (1.2.0-dev), compiled in release mode / opt-level=3: and here is the ASM:
For fat pointers this has been fixed in Am 22.06.2015 07:58 schrieb "Reiner Pope"
On etc. this is currently impossible because the address of references is significant (you can e.g. print it with ).
- yes, the ability to take addresses prevents doing this for all functions. But you could see doing this as an optimization: make the compiler check whether a function uses the address of a reference, and if not then change its calling convention. The compiler can use the worker/wrapper technique (as in GHC) to support call sites which aren't aware of this change in calling convention, by splitting the function into two:
Don't do this. You will break FastISel. Fix LLVM if it's not doing the optimizations you want.
what exactly would break FastISel?
Pretty sure that since work on niche-filling optimisation, and use a pair of registers for the argument and the return value, respectively. Cc
didn't solve this?
Yes, this appears to be fixed. The following Rust code (): generates and
Ah no, I see that this issue also suggests: We don't do that yet: . FWIW Maybe it is worth to split this issue into two. One part has been closed already AFAICT. The other part (passing as a register containing a ) is... a suggestion at most. This issue does not suggest how to do this. already raised the issue that this breaks if the address is inspected. It probably needs to handle writes through (e.g. due to ) and somehow as well, etc. So I'm skeptic that this can work.
Here's another slightly more complex example, where ABI seems to be more efficient than :
A related thread:
is this fixed? Linked code is still bigger on Rust 1.48.
my example is fixed, the return value is no longer via stack: The difference is that rust abi version now uses a vectorized add, which seems like a win?
Hi all. I've traced back the cause of 'P-high' issue to two pulls that relate to this issue, namely and . Basically, these commits break auto-vectorization post Rust version 1.48 in a way that is not easily worked around. In short, if we take: 47 yields an efficient: 48 yields an awkward: 50 yields an even more awkward cascade effect: There are other examples in . My overly simplistic analysis, I'm no expert, is . In summary, if I rebuild Rust with the above pulls neutered, auto-vectorization appears to return to normal, as per 1.47.

Discussion:
can_unwind: fn_can_unwind(cx.tcx().sess.panic_strategy(), codegen_fn_attr_flags, conv), }; fn_abi.adjust_for_abi(cx, sig.abi); <ins> debug!("FnAbi::new_internal = {:?}", fn_abi); </ins> fn_abi }

|| abi == SpecAbi::RustIntrinsic || abi == SpecAbi::PlatformIntrinsic { <del> let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>| { </del> <ins> let fixup = |arg: &mut ArgAbi<'tcx, Ty<'tcx>>, is_ret: bool| { </ins> if arg.is_ignore() { return; }

_ => return, } <ins> let max_by_val_size = if is_ret { call::max_ret_by_val(cx) } else { Pointer.size(cx) }; </ins> let size = arg.layout.size; <del> if arg.layout.is_unsized() || size > Pointer.size(cx) { </del> <ins> if arg.layout.is_unsized() || size > max_by_val_size { </ins> arg.make_indirect(); } else { // We want to pass small aggregates as immediates, but using

arg.cast_to(Reg { kind: RegKind::Integer, size }); } }; <del> fixup(&mut self.ret); </del> <ins> fixup(&mut self.ret, true); </ins> for arg in &mut self.args { <del> fixup(arg); </del> <ins> fixup(arg, false); </ins> } if let PassMode::Indirect(ref mut attrs, _) = self.ret.mode { attrs.set(ArgAttribute::StructRet);

Ok(()) } } <ins> /// Returns the maximum size of return values to be passed by value in the Rust ABI. /// /// Return values beyond this size will use an implicit out-pointer instead. pub fn max_ret_by_val<C: HasTargetSpec + HasDataLayout>(spec: &C) -> Size { match spec.target_spec().arch.as_str() { // System-V will pass return values up to 128 bits in RAX/RDX. "x86_64" => Size::from_bits(128), _ => spec.data_layout().pointer_size, } } </ins>

<ins> //! This test checks that types of up to 128 bits are returned by-value instead of via out-pointer. // compile-flags: -C no-prepopulate-passes -O // only-x86_64 #![crate_type = "lib"] pub struct S { a: u64, b: u32, c: u32, } // CHECK: define i128 @modify(%S* noalias nocapture dereferenceable(16) %s) #[no_mangle] pub fn modify(s: S) -> S { S { a: s.a + s.a, b: s.b + s.b, c: s.c + s.c } } #[repr(packed)] pub struct TooBig { a: u64, b: u32, c: u32, d: u8, } // CHECK: define void @m_big(%TooBig* [[ATTRS:.*sret.*]], %TooBig* [[ATTRS2:.*]] %s) #[no_mangle] pub fn m_big(s: TooBig) -> TooBig { TooBig { a: s.a + s.a, b: s.b + s.b, c: s.c + s.c, d: s.d + s.d } } </ins>