Rollup merge of #62380 - eddyb:x64-sysv-regs, r=nagisa
rustc_target: avoid negative register counts in the SysV x86_64 ABI. Because `needed_{int,sse}` and `{int,sse}_regs` were only used with integer literals, they were inferred to `i32` and `{int,sse}_regs` could therefore be negative. There was a check which prevented that, but *only* for aggregate arguments, not scalars. Fixes #62350. r? @nagisa or @rkruppe
This commit is contained in:
commit
58f402aab7
|
@ -167,20 +167,23 @@ fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
|
||||||
target
|
target
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9
|
||||||
|
const MAX_SSE_REGS: usize = 8; // XMM0-7
|
||||||
|
|
||||||
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>)
|
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>)
|
||||||
where Ty: TyLayoutMethods<'a, C> + Copy,
|
where Ty: TyLayoutMethods<'a, C> + Copy,
|
||||||
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
|
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
|
||||||
{
|
{
|
||||||
let mut int_regs = 6; // RDI, RSI, RDX, RCX, R8, R9
|
let mut int_regs = MAX_INT_REGS;
|
||||||
let mut sse_regs = 8; // XMM0-7
|
let mut sse_regs = MAX_SSE_REGS;
|
||||||
|
|
||||||
let mut x86_64_ty = |arg: &mut ArgType<'a, Ty>, is_arg: bool| {
|
let mut x86_64_ty = |arg: &mut ArgType<'a, Ty>, is_arg: bool| {
|
||||||
let mut cls_or_mem = classify_arg(cx, arg);
|
let mut cls_or_mem = classify_arg(cx, arg);
|
||||||
|
|
||||||
let mut needed_int = 0;
|
|
||||||
let mut needed_sse = 0;
|
|
||||||
if is_arg {
|
if is_arg {
|
||||||
if let Ok(cls) = cls_or_mem {
|
if let Ok(cls) = cls_or_mem {
|
||||||
|
let mut needed_int = 0;
|
||||||
|
let mut needed_sse = 0;
|
||||||
for &c in &cls {
|
for &c in &cls {
|
||||||
match c {
|
match c {
|
||||||
Some(Class::Int) => needed_int += 1,
|
Some(Class::Int) => needed_int += 1,
|
||||||
|
@ -188,8 +191,20 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>)
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if arg.layout.is_aggregate() && (int_regs < needed_int || sse_regs < needed_sse) {
|
match (int_regs.checked_sub(needed_int), sse_regs.checked_sub(needed_sse)) {
|
||||||
cls_or_mem = Err(Memory);
|
(Some(left_int), Some(left_sse)) => {
|
||||||
|
int_regs = left_int;
|
||||||
|
sse_regs = left_sse;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Not enough registers for this argument, so it will be
|
||||||
|
// passed on the stack, but we only mark aggregates
|
||||||
|
// explicitly as indirect `byval` arguments, as LLVM will
|
||||||
|
// automatically put immediates on the stack itself.
|
||||||
|
if arg.layout.is_aggregate() {
|
||||||
|
cls_or_mem = Err(Memory);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,14 +216,14 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>)
|
||||||
} else {
|
} else {
|
||||||
// `sret` parameter thus one less integer register available
|
// `sret` parameter thus one less integer register available
|
||||||
arg.make_indirect();
|
arg.make_indirect();
|
||||||
|
// NOTE(eddyb) return is handled first, so no registers
|
||||||
|
// should've been used yet.
|
||||||
|
assert_eq!(int_regs, MAX_INT_REGS);
|
||||||
int_regs -= 1;
|
int_regs -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(ref cls) => {
|
Ok(ref cls) => {
|
||||||
// split into sized chunks passed individually
|
// split into sized chunks passed individually
|
||||||
int_regs -= needed_int;
|
|
||||||
sse_regs -= needed_sse;
|
|
||||||
|
|
||||||
if arg.layout.is_aggregate() {
|
if arg.layout.is_aggregate() {
|
||||||
let size = arg.layout.size;
|
let size = arg.layout.size;
|
||||||
arg.cast_to(cast_target(cls, size))
|
arg.cast_to(cast_target(cls, size))
|
||||||
|
|
|
@ -215,6 +215,29 @@ uint64_t get_c_many_params(void *a, void *b, void *c, void *d, struct quad f) {
|
||||||
return f.c;
|
return f.c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct quad_floats {
|
||||||
|
float a;
|
||||||
|
float b;
|
||||||
|
float c;
|
||||||
|
float d;
|
||||||
|
};
|
||||||
|
|
||||||
|
float get_c_exhaust_sysv64_ints(
|
||||||
|
void *a,
|
||||||
|
void *b,
|
||||||
|
void *c,
|
||||||
|
void *d,
|
||||||
|
void *e,
|
||||||
|
void *f,
|
||||||
|
// `f` used the last integer register, so `g` goes on the stack.
|
||||||
|
// It also used to bring the "count of available integer registers" down to
|
||||||
|
// `-1` which broke the next SSE-only aggregate argument (`h`) - see #62350.
|
||||||
|
void *g,
|
||||||
|
struct quad_floats h
|
||||||
|
) {
|
||||||
|
return h.c;
|
||||||
|
}
|
||||||
|
|
||||||
// Calculates the average of `(x + y) / n` where x: i64, y: f64. There must be exactly n pairs
|
// Calculates the average of `(x + y) / n` where x: i64, y: f64. There must be exactly n pairs
|
||||||
// passed as variadic arguments. There are two versions of this function: the
|
// passed as variadic arguments. There are two versions of this function: the
|
||||||
// variadic one, and the one that takes a `va_list`.
|
// variadic one, and the one that takes a `va_list`.
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
// extern-return-TwoU64s
|
// extern-return-TwoU64s
|
||||||
// foreign-fn-with-byval
|
// foreign-fn-with-byval
|
||||||
// issue-28676
|
// issue-28676
|
||||||
|
// issue-62350-sysv-neg-reg-counts
|
||||||
// struct-return
|
// struct-return
|
||||||
|
|
||||||
// ignore-android
|
// ignore-android
|
||||||
|
@ -83,6 +84,9 @@ mod tests {
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Quad { a: u64, b: u64, c: u64, d: u64 }
|
pub struct Quad { a: u64, b: u64, c: u64, d: u64 }
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct QuadFloats { a: f32, b: f32, c: f32, d: f32 }
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Floats { a: f64, b: u8, c: f64 }
|
pub struct Floats { a: f64, b: u8, c: f64 }
|
||||||
|
@ -108,6 +112,16 @@ mod tests {
|
||||||
pub fn get_z(x: S) -> u64;
|
pub fn get_z(x: S) -> u64;
|
||||||
pub fn get_c_many_params(_: *const (), _: *const (),
|
pub fn get_c_many_params(_: *const (), _: *const (),
|
||||||
_: *const (), _: *const (), f: Quad) -> u64;
|
_: *const (), _: *const (), f: Quad) -> u64;
|
||||||
|
pub fn get_c_exhaust_sysv64_ints(
|
||||||
|
_: *const (),
|
||||||
|
_: *const (),
|
||||||
|
_: *const (),
|
||||||
|
_: *const (),
|
||||||
|
_: *const (),
|
||||||
|
_: *const (),
|
||||||
|
_: *const (),
|
||||||
|
h: QuadFloats,
|
||||||
|
) -> f32;
|
||||||
pub fn rust_dbg_abi_1(q: Quad) -> Quad;
|
pub fn rust_dbg_abi_1(q: Quad) -> Quad;
|
||||||
pub fn rust_dbg_abi_2(f: Floats) -> Floats;
|
pub fn rust_dbg_abi_2(f: Floats) -> Floats;
|
||||||
}
|
}
|
||||||
|
@ -263,6 +277,27 @@ mod tests {
|
||||||
test();
|
test();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn test_62350() {
|
||||||
|
use std::ptr;
|
||||||
|
unsafe {
|
||||||
|
let null = ptr::null();
|
||||||
|
let q = QuadFloats {
|
||||||
|
a: 10.2,
|
||||||
|
b: 20.3,
|
||||||
|
c: 30.4,
|
||||||
|
d: 40.5
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
get_c_exhaust_sysv64_ints(null, null, null, null, null, null, null, q),
|
||||||
|
q.c,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn issue_62350() {
|
||||||
|
test_62350();
|
||||||
|
}
|
||||||
|
|
||||||
fn test1() {
|
fn test1() {
|
||||||
unsafe {
|
unsafe {
|
||||||
let q = Quad { a: 0xaaaa_aaaa_aaaa_aaaa,
|
let q = Quad { a: 0xaaaa_aaaa_aaaa_aaaa,
|
||||||
|
@ -321,6 +356,7 @@ fn main() {
|
||||||
extern_return_twou64s();
|
extern_return_twou64s();
|
||||||
foreign_fn_with_byval();
|
foreign_fn_with_byval();
|
||||||
issue_28676();
|
issue_28676();
|
||||||
|
issue_62350();
|
||||||
struct_return();
|
struct_return();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
// run-pass
|
||||||
|
#![allow(dead_code)]
|
||||||
|
#![allow(improper_ctypes)]
|
||||||
|
|
||||||
|
// ignore-wasm32-bare no libc to test ffi with
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct QuadFloats { a: f32, b: f32, c: f32, d: f32 }
|
||||||
|
|
||||||
|
mod rustrt {
|
||||||
|
use super::QuadFloats;
|
||||||
|
|
||||||
|
#[link(name = "rust_test_helpers", kind = "static")]
|
||||||
|
extern {
|
||||||
|
pub fn get_c_exhaust_sysv64_ints(
|
||||||
|
_: *const (),
|
||||||
|
_: *const (),
|
||||||
|
_: *const (),
|
||||||
|
_: *const (),
|
||||||
|
_: *const (),
|
||||||
|
_: *const (),
|
||||||
|
_: *const (),
|
||||||
|
h: QuadFloats,
|
||||||
|
) -> f32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test() {
|
||||||
|
unsafe {
|
||||||
|
let null = std::ptr::null();
|
||||||
|
let q = QuadFloats {
|
||||||
|
a: 10.2,
|
||||||
|
b: 20.3,
|
||||||
|
c: 30.4,
|
||||||
|
d: 40.5
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
rustrt::get_c_exhaust_sysv64_ints(null, null, null, null, null, null, null, q),
|
||||||
|
q.c,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
test();
|
||||||
|
}
|
Loading…
Reference in New Issue