Skip to content

Commit 3967fc6

Browse files
committed
Increase the Rust ABI by-value arg/return size limit from 2*usize to 3*usize.
1 parent d2dc425 commit 3967fc6

File tree

4 files changed

+34
-13
lines changed

4 files changed

+34
-13
lines changed

compiler/rustc_middle/src/ty/layout.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3214,18 +3214,36 @@ impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
32143214
_ => return,
32153215
}
32163216

3217-
// Pass and return structures up to 2 pointers in size by value, matching `ScalarPair`.
3218-
// LLVM will usually pass these in 2 registers, which is more efficient than by-ref.
3219-
let max_by_val_size = Pointer.size(self) * 2;
3217+
// Pass and return structures up to 3 pointers in size by value,
3218+
// similar to `ScalarPair`'s 2 values, but more widely applicable.
3219+
// LLVM will usually pass these in 3 registers, which is more efficient than by-ref.
3220+
let pointer_size = Pointer.size(self);
3221+
let max_by_val_size = pointer_size * 3;
32203222
let size = arg.layout.size;
32213223

32223224
if arg.layout.is_unsized() || size > max_by_val_size {
32233225
arg.make_indirect();
32243226
} else {
32253227
// We want to pass small aggregates as immediates, but using
32263228
// a LLVM aggregate type for this leads to bad optimizations,
3227-
// so we pick an appropriately sized integer type instead.
3228-
arg.cast_to(Reg { kind: RegKind::Integer, size });
3229+
// so we pick an appropriately sized integer type instead,
3230+
// for anything small enough.
3231+
let cast_target = if size > pointer_size * 2 {
3232+
// This will end up as either a mixed aggregate,
3233+
// or `[usize; N]` if `size == pointer_size * N`.
3234+
call::Uniform {
3235+
unit: Reg { kind: RegKind::Integer, size: pointer_size },
3236+
total: size,
3237+
}
3238+
} else {
3239+
// FIXME(eddyb) what's the impact of this combining
3240+
// several *unrelated* fields into one integer with a
3241+
// non-physical size (e.g. `i96`)? this is likely
3242+
// alleviated in common cases by `ScalarPair`, which is
3243+
// handled more directly, and before "adjustments".
3244+
Reg { kind: RegKind::Integer, size }.into()
3245+
};
3246+
arg.cast_to(cast_target);
32293247
}
32303248
};
32313249
fixup(&mut fn_abi.ret);

src/test/codegen/arg-return-value-in-reg.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub fn modify(s: S) -> S {
1919

2020
#[repr(packed)]
2121
pub struct TooBig {
22-
a: u64,
22+
a: u128,
2323
b: u32,
2424
c: u32,
2525
d: u8,

src/test/codegen/array-equality.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ pub fn array_eq_ref(a: &[u16; 6], b: &[u16; 6]) -> bool {
2525

2626
// CHECK-LABEL: @array_eq_value_still_passed_by_pointer
2727
#[no_mangle]
28-
pub fn array_eq_value_still_passed_by_pointer(a: [u16; 9], b: [u16; 9]) -> bool {
28+
pub fn array_eq_value_still_passed_by_pointer(a: [u16; 13], b: [u16; 13]) -> bool {
2929
// CHECK-NEXT: start:
3030
// CHECK-NEXT: bitcast
3131
// CHECK-NEXT: bitcast
32-
// CHECK-NEXT: %[[CMP:.+]] = tail call i32 @{{bcmp|memcmp}}(i8* {{.*}} dereferenceable(18) %{{.+}}, i8* {{.*}} dereferenceable(18) %{{.+}}, i64 18)
32+
// CHECK-NEXT: %[[CMP:.+]] = tail call i32 @{{bcmp|memcmp}}(i8* {{.*}} dereferenceable(26) %{{.+}}, i8* {{.*}} dereferenceable(26) %{{.+}}, i64 26)
3333
// CHECK-NEXT: %[[EQ:.+]] = icmp eq i32 %[[CMP]], 0
3434
// CHECK-NEXT: ret i1 %[[EQ]]
3535
a == b

src/test/codegen/issue-15953.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,32 @@
11
// Test that llvm generates `memcpy` for moving a value
22
// inside a function and moving an argument.
33

4+
// NOTE(eddyb) this has to be large enough to never be passed in registers.
5+
type BigWithDrop = [String; 2];
6+
47
struct Foo {
5-
x: Vec<i32>,
8+
x: BigWithDrop,
69
}
710

811
#[inline(never)]
912
#[no_mangle]
1013
// CHECK: memcpy
11-
fn interior(x: Vec<i32>) -> Vec<i32> {
14+
fn interior(x: BigWithDrop) -> BigWithDrop {
1215
let Foo { x } = Foo { x: x };
1316
x
1417
}
1518

1619
#[inline(never)]
1720
#[no_mangle]
1821
// CHECK: memcpy
19-
fn exterior(x: Vec<i32>) -> Vec<i32> {
22+
fn exterior(x: BigWithDrop) -> BigWithDrop {
2023
x
2124
}
2225

2326
fn main() {
24-
let x = interior(Vec::new());
27+
let x = interior(BigWithDrop::default());
2528
println!("{:?}", x);
2629

27-
let x = exterior(Vec::new());
30+
let x = exterior(BigWithDrop::default());
2831
println!("{:?}", x);
2932
}

0 commit comments

Comments
 (0)