diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index caf33fa5d213b..987573dc902ce 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -3214,9 +3214,11 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { _ => return, } - // Pass and return structures up to 2 pointers in size by value, matching `ScalarPair`. - // LLVM will usually pass these in 2 registers, which is more efficient than by-ref. - let max_by_val_size = Pointer.size(self) * 2; + // Pass and return structures up to 3 pointers in size by value, + // similar to `ScalarPair`'s 2 values, but more widely applicable. + // LLVM will usually pass these in 3 registers, which is more efficient than by-ref. + let pointer_size = Pointer.size(self); + let max_by_val_size = pointer_size * 3; let size = arg.layout.size; if arg.layout.is_unsized() || size > max_by_val_size { @@ -3224,8 +3226,24 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> { } else { // We want to pass small aggregates as immediates, but using // a LLVM aggregate type for this leads to bad optimizations, - // so we pick an appropriately sized integer type instead. - arg.cast_to(Reg { kind: RegKind::Integer, size }); + // so we pick an appropriately sized integer type instead, + // for anything small enough. + let cast_target = if size > pointer_size * 2 { + // This will end up as either a mixed aggregate, + // or `[usize; N]` if `size == pointer_size * N`. + call::Uniform { + unit: Reg { kind: RegKind::Integer, size: pointer_size }, + total: size, + } + } else { + // FIXME(eddyb) what's the impact of this combining + // several *unrelated* fields into one integer with a + // non-physical size (e.g. `i96`)? this is likely + // alleviated in common cases by `ScalarPair`, which is + // handled more directly, and before "adjustments". + Reg { kind: RegKind::Integer, size }.into() + }; + arg.cast_to(cast_target); } }; fixup(&mut fn_abi.ret); diff --git a/src/test/codegen/arg-return-value-in-reg.rs b/src/test/codegen/arg-return-value-in-reg.rs index a69291d47821a..6e2c1330c11c6 100644 --- a/src/test/codegen/arg-return-value-in-reg.rs +++ b/src/test/codegen/arg-return-value-in-reg.rs @@ -19,7 +19,7 @@ pub fn modify(s: S) -> S { #[repr(packed)] pub struct TooBig { - a: u64, + a: u128, b: u32, c: u32, d: u8, diff --git a/src/test/codegen/array-equality.rs b/src/test/codegen/array-equality.rs index fefc232b49040..1130ba5953336 100644 --- a/src/test/codegen/array-equality.rs +++ b/src/test/codegen/array-equality.rs @@ -25,11 +25,11 @@ pub fn array_eq_ref(a: &[u16; 6], b: &[u16; 6]) -> bool { // CHECK-LABEL: @array_eq_value_still_passed_by_pointer #[no_mangle] -pub fn array_eq_value_still_passed_by_pointer(a: [u16; 9], b: [u16; 9]) -> bool { +pub fn array_eq_value_still_passed_by_pointer(a: [u16; 13], b: [u16; 13]) -> bool { // CHECK-NEXT: start: // CHECK-NEXT: bitcast // CHECK-NEXT: bitcast - // CHECK-NEXT: %[[CMP:.+]] = tail call i32 @{{bcmp|memcmp}}(i8* {{.*}} dereferenceable(18) %{{.+}}, i8* {{.*}} dereferenceable(18) %{{.+}}, i64 18) + // CHECK-NEXT: %[[CMP:.+]] = tail call i32 @{{bcmp|memcmp}}(i8* {{.*}} dereferenceable(26) %{{.+}}, i8* {{.*}} dereferenceable(26) %{{.+}}, i64 26) // CHECK-NEXT: %[[EQ:.+]] = icmp eq i32 %[[CMP]], 0 // CHECK-NEXT: ret i1 %[[EQ]] a == b diff --git a/src/test/codegen/issue-15953.rs b/src/test/codegen/issue-15953.rs index 28d28428904f5..9f503240f8ead 100644 --- a/src/test/codegen/issue-15953.rs +++ b/src/test/codegen/issue-15953.rs @@ -1,14 +1,17 @@ // Test that llvm generates `memcpy` for moving a value // inside a function and moving an argument. +// NOTE(eddyb) this has to be large enough to never be passed in registers. +type BigWithDrop = [String; 2]; + struct Foo { - x: Vec, + x: BigWithDrop, } #[inline(never)] #[no_mangle] // CHECK: memcpy -fn interior(x: Vec) -> Vec { +fn interior(x: BigWithDrop) -> BigWithDrop { let Foo { x } = Foo { x: x }; x } @@ -16,14 +19,14 @@ fn interior(x: Vec) -> Vec { #[inline(never)] #[no_mangle] // CHECK: memcpy -fn exterior(x: Vec) -> Vec { +fn exterior(x: BigWithDrop) -> BigWithDrop { x } fn main() { - let x = interior(Vec::new()); + let x = interior(BigWithDrop::default()); println!("{:?}", x); - let x = exterior(Vec::new()); + let x = exterior(BigWithDrop::default()); println!("{:?}", x); }