Skip to content

Commit 050a71b

Browse files
committed
rustc_target: avoid negative register counts in the SysV x86_64 ABI.
1 parent b43eb42 commit 050a71b

File tree

4 files changed

+129
-9
lines changed

4 files changed

+129
-9
lines changed

src/librustc_target/abi/call/x86_64.rs

+24-9
Original file line numberDiff line numberDiff line change
@@ -167,29 +167,44 @@ fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
167167
target
168168
}
169169

170+
const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9
171+
const MAX_SSE_REGS: usize = 8; // XMM0-7
172+
170173
pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>)
171174
where Ty: TyLayoutMethods<'a, C> + Copy,
172175
C: LayoutOf<Ty = Ty, TyLayout = TyLayout<'a, Ty>> + HasDataLayout
173176
{
174-
let mut int_regs = 6; // RDI, RSI, RDX, RCX, R8, R9
175-
let mut sse_regs = 8; // XMM0-7
177+
let mut int_regs = MAX_INT_REGS;
178+
let mut sse_regs = MAX_SSE_REGS;
176179

177180
let mut x86_64_ty = |arg: &mut ArgType<'a, Ty>, is_arg: bool| {
178181
let mut cls_or_mem = classify_arg(cx, arg);
179182

180-
let mut needed_int = 0;
181-
let mut needed_sse = 0;
182183
if is_arg {
183184
if let Ok(cls) = cls_or_mem {
185+
let mut needed_int = 0;
186+
let mut needed_sse = 0;
184187
for &c in &cls {
185188
match c {
186189
Some(Class::Int) => needed_int += 1,
187190
Some(Class::Sse) => needed_sse += 1,
188191
_ => {}
189192
}
190193
}
191-
if arg.layout.is_aggregate() && (int_regs < needed_int || sse_regs < needed_sse) {
192-
cls_or_mem = Err(Memory);
194+
match (int_regs.checked_sub(needed_int), sse_regs.checked_sub(needed_sse)) {
195+
(Some(left_int), Some(left_sse)) => {
196+
int_regs = left_int;
197+
sse_regs = left_sse;
198+
}
199+
_ => {
200+
// Not enough registers for this argument, so it will be
201+
// passed on the stack, but we only mark aggregates
202+
// explicitly as indirect `byval` arguments, as LLVM will
203+
// automatically put immediates on the stack itself.
204+
if arg.layout.is_aggregate() {
205+
cls_or_mem = Err(Memory);
206+
}
207+
}
193208
}
194209
}
195210
}
@@ -201,14 +216,14 @@ pub fn compute_abi_info<'a, Ty, C>(cx: &C, fty: &mut FnType<'a, Ty>)
201216
} else {
202217
// `sret` parameter thus one less integer register available
203218
arg.make_indirect();
219+
// NOTE(eddyb) return is handled first, so no registers
220+
// should've been used yet.
221+
assert_eq!(int_regs, MAX_INT_REGS);
204222
int_regs -= 1;
205223
}
206224
}
207225
Ok(ref cls) => {
208226
// split into sized chunks passed individually
209-
int_regs -= needed_int;
210-
sse_regs -= needed_sse;
211-
212227
if arg.layout.is_aggregate() {
213228
let size = arg.layout.size;
214229
arg.cast_to(cast_target(cls, size))

src/test/auxiliary/rust_test_helpers.c

+23
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,29 @@ uint64_t get_c_many_params(void *a, void *b, void *c, void *d, struct quad f) {
215215
return f.c;
216216
}
217217

218+
struct quad_floats {
219+
float a;
220+
float b;
221+
float c;
222+
float d;
223+
};
224+
225+
float get_c_exhaust_sysv64_ints(
226+
void *a,
227+
void *b,
228+
void *c,
229+
void *d,
230+
void *e,
231+
void *f,
232+
// `f` used the last integer register, so `g` goes on the stack.
233+
// It also used to bring the "count of available integer registers" down to
234+
// `-1` which broke the next SSE-only aggregate argument (`h`) - see #62350.
235+
void *g,
236+
struct quad_floats h
237+
) {
238+
return h.c;
239+
}
240+
218241
// Calculates the average of `(x + y) / n` where x: i64, y: f64. There must be exactly n pairs
219242
// passed as variadic arguments. There are two versions of this function: the
220243
// variadic one, and the one that takes a `va_list`.

src/test/run-pass/abi-sysv64-arg-passing.rs

+36
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
// extern-return-TwoU64s
2121
// foreign-fn-with-byval
2222
// issue-28676
23+
// issue-62350-sysv-neg-reg-counts
2324
// struct-return
2425

2526
// ignore-android
@@ -83,6 +84,9 @@ mod tests {
8384
#[derive(Copy, Clone)]
8485
pub struct Quad { a: u64, b: u64, c: u64, d: u64 }
8586

87+
#[derive(Copy, Clone)]
88+
pub struct QuadFloats { a: f32, b: f32, c: f32, d: f32 }
89+
8690
#[repr(C)]
8791
#[derive(Copy, Clone)]
8892
pub struct Floats { a: f64, b: u8, c: f64 }
@@ -108,6 +112,16 @@ mod tests {
108112
pub fn get_z(x: S) -> u64;
109113
pub fn get_c_many_params(_: *const (), _: *const (),
110114
_: *const (), _: *const (), f: Quad) -> u64;
115+
pub fn get_c_exhaust_sysv64_ints(
116+
_: *const (),
117+
_: *const (),
118+
_: *const (),
119+
_: *const (),
120+
_: *const (),
121+
_: *const (),
122+
_: *const (),
123+
h: QuadFloats,
124+
) -> f32;
111125
pub fn rust_dbg_abi_1(q: Quad) -> Quad;
112126
pub fn rust_dbg_abi_2(f: Floats) -> Floats;
113127
}
@@ -263,6 +277,27 @@ mod tests {
263277
test();
264278
}
265279

280+
fn test_62350() {
281+
use std::ptr;
282+
unsafe {
283+
let null = ptr::null();
284+
let q = QuadFloats {
285+
a: 10.2,
286+
b: 20.3,
287+
c: 30.4,
288+
d: 40.5
289+
};
290+
assert_eq!(
291+
get_c_exhaust_sysv64_ints(null, null, null, null, null, null, null, q),
292+
q.c,
293+
);
294+
}
295+
}
296+
297+
pub fn issue_62350() {
298+
test_62350();
299+
}
300+
266301
fn test1() {
267302
unsafe {
268303
let q = Quad { a: 0xaaaa_aaaa_aaaa_aaaa,
@@ -321,6 +356,7 @@ fn main() {
321356
extern_return_twou64s();
322357
foreign_fn_with_byval();
323358
issue_28676();
359+
issue_62350();
324360
struct_return();
325361
}
326362

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// run-pass
2+
#![allow(dead_code)]
3+
#![allow(improper_ctypes)]
4+
5+
// ignore-wasm32-bare no libc to test ffi with
6+
7+
#[derive(Copy, Clone)]
8+
pub struct QuadFloats { a: f32, b: f32, c: f32, d: f32 }
9+
10+
mod rustrt {
11+
use super::QuadFloats;
12+
13+
#[link(name = "rust_test_helpers", kind = "static")]
14+
extern {
15+
pub fn get_c_exhaust_sysv64_ints(
16+
_: *const (),
17+
_: *const (),
18+
_: *const (),
19+
_: *const (),
20+
_: *const (),
21+
_: *const (),
22+
_: *const (),
23+
h: QuadFloats,
24+
) -> f32;
25+
}
26+
}
27+
28+
fn test() {
29+
unsafe {
30+
let null = std::ptr::null();
31+
let q = QuadFloats {
32+
a: 10.2,
33+
b: 20.3,
34+
c: 30.4,
35+
d: 40.5
36+
};
37+
assert_eq!(
38+
rustrt::get_c_exhaust_sysv64_ints(null, null, null, null, null, null, null, q),
39+
q.c,
40+
);
41+
}
42+
}
43+
44+
pub fn main() {
45+
test();
46+
}

0 commit comments

Comments
 (0)