Skip to content

Commit daa7185

Browse files
author
James Duley
committed
Implement va_arg for AArch64 Linux
1 parent 254f201 commit daa7185

File tree

3 files changed

+152
-17
lines changed

3 files changed

+152
-17
lines changed

Diff for: src/libcore/ffi.rs

+114-17
Original file line numberDiff line numberDiff line change
@@ -268,35 +268,73 @@ mod sealed_trait {
268268
reason = "the `c_variadic` feature has not been properly tested on \
269269
all supported platforms",
270270
issue = "44930")]
271-
pub trait VaArgSafe {}
271+
pub trait VaArgSafe {
272+
#[doc(hidden)]
273+
unsafe fn va_arg(ap: &mut super::VaListImpl<'_>) -> Self;
274+
}
272275
}
273276

274-
macro_rules! impl_va_arg_safe {
277+
macro_rules! impl_va_arg_safe_integer {
275278
($($t:ty),+) => {
276279
$(
277280
#[unstable(feature = "c_variadic",
278281
reason = "the `c_variadic` feature has not been properly tested on \
279282
all supported platforms",
280283
issue = "44930")]
281-
impl sealed_trait::VaArgSafe for $t {}
284+
impl sealed_trait::VaArgSafe for $t {
285+
unsafe fn va_arg(ap: &mut VaListImpl<'_>) -> Self {
286+
#[cfg(not(all(target_arch = "aarch64", not(target_os = "ios"), not(windows))))]
287+
return va_arg(ap);
288+
#[cfg(all(target_arch = "aarch64", not(target_os = "ios"), not(windows)))]
289+
return ap.va_arg_gr();
290+
}
291+
}
282292
)+
283293
}
284294
}
285295

286-
impl_va_arg_safe!{i8, i16, i32, i64, usize}
287-
impl_va_arg_safe!{u8, u16, u32, u64, isize}
288-
impl_va_arg_safe!{f64}
296+
macro_rules! impl_va_arg_safe_float {
297+
($($t:ty),+) => {
298+
$(
299+
#[unstable(feature = "c_variadic",
300+
reason = "the `c_variadic` feature has not been properly tested on \
301+
all supported platforms",
302+
issue = "44930")]
303+
impl sealed_trait::VaArgSafe for $t {
304+
unsafe fn va_arg(ap: &mut VaListImpl<'_>) -> Self {
305+
#[cfg(not(all(target_arch = "aarch64", not(target_os = "ios"), not(windows))))]
306+
return va_arg(ap);
307+
#[cfg(all(target_arch = "aarch64", not(target_os = "ios"), not(windows)))]
308+
return ap.va_arg_vr();
309+
}
310+
}
311+
)+
312+
}
313+
}
289314

290-
#[unstable(feature = "c_variadic",
291-
reason = "the `c_variadic` feature has not been properly tested on \
292-
all supported platforms",
293-
issue = "44930")]
294-
impl<T> sealed_trait::VaArgSafe for *mut T {}
295-
#[unstable(feature = "c_variadic",
296-
reason = "the `c_variadic` feature has not been properly tested on \
297-
all supported platforms",
298-
issue = "44930")]
299-
impl<T> sealed_trait::VaArgSafe for *const T {}
315+
macro_rules! impl_va_arg_safe_pointer {
316+
($($t:ident),+) => {
317+
$(
318+
#[unstable(feature = "c_variadic",
319+
reason = "the `c_variadic` feature has not been properly tested on \
320+
all supported platforms",
321+
issue = "44930")]
322+
impl<T> sealed_trait::VaArgSafe for *$t T {
323+
unsafe fn va_arg(ap: &mut VaListImpl<'_>) -> Self {
324+
#[cfg(not(all(target_arch = "aarch64", not(target_os = "ios"), not(windows))))]
325+
return va_arg(ap);
326+
#[cfg(all(target_arch = "aarch64", not(target_os = "ios"), not(windows)))]
327+
return ap.va_arg_gr();
328+
}
329+
}
330+
)+
331+
}
332+
}
333+
334+
impl_va_arg_safe_integer! {i8, i16, i32, i64, usize}
335+
impl_va_arg_safe_integer! {u8, u16, u32, u64, isize}
336+
impl_va_arg_safe_float! {f64}
337+
impl_va_arg_safe_pointer! {mut, const}
300338

301339
#[unstable(feature = "c_variadic",
302340
reason = "the `c_variadic` feature has not been properly tested on \
@@ -306,7 +344,7 @@ impl<'f> VaListImpl<'f> {
306344
/// Advance to the next arg.
307345
#[inline]
308346
pub unsafe fn arg<T: sealed_trait::VaArgSafe>(&mut self) -> T {
309-
va_arg(self)
347+
T::va_arg(self)
310348
}
311349

312350
/// Copies the `va_list` at the current location.
@@ -363,5 +401,64 @@ extern "rust-intrinsic" {
363401

364402
/// Loads an argument of type `T` from the `va_list` `ap` and increment the
365403
/// argument `ap` points to.
404+
#[cfg(not(all(target_arch = "aarch64", not(target_os = "ios"), not(windows))))]
366405
fn va_arg<T: sealed_trait::VaArgSafe>(ap: &mut VaListImpl<'_>) -> T;
367406
}
407+
408+
#[cfg(all(target_arch = "aarch64", not(target_os = "ios"), not(windows)))]
409+
use crate::{mem, ptr};
410+
411+
// Implemenation for AArch64 linux (and others that follow the standard PCS)
412+
// based on https://developer.arm.com/docs/ihi0055/d/procedure-call-standard-for-the-arm-64-bit-architecture
413+
// APPENDIX Variable argument Lists -> The va_start() macro
414+
#[cfg(all(target_arch = "aarch64", not(target_os = "ios"), not(windows)))]
415+
impl<'f> VaListImpl<'f> {
416+
unsafe fn get_begin_and_end<T>(current_pos: usize, reg_size: usize) -> (usize, usize) {
417+
let mut begin = current_pos;
418+
if mem::align_of::<T>() > 8 && reg_size == 8 {
419+
begin = begin.wrapping_add(15) & !15; // round up
420+
}
421+
let nreg = (mem::size_of::<T>() + reg_size - 1) / reg_size;
422+
let end = begin.wrapping_add(nreg * reg_size);
423+
#[cfg(target_endian = "big")]
424+
{
425+
if
426+
/* classof(type) != "aggregate" && */
427+
mem::size_of::<T>() < reg_size {
428+
begin = begin.wrapping_add(reg_size - mem::size_of::<T>());
429+
}
430+
}
431+
(begin, end)
432+
}
433+
434+
unsafe fn va_arg_on_stack<T: sealed_trait::VaArgSafe>(stack: &mut *mut c_void) -> T {
435+
let (begin, end) = Self::get_begin_and_end::<T>(*stack as usize, 8);
436+
*stack = end as *mut c_void;
437+
ptr::read(begin as *const T)
438+
}
439+
440+
unsafe fn va_arg_gr_or_vr<T: sealed_trait::VaArgSafe>(
441+
r_top: *mut c_void,
442+
r_offs: &mut i32,
443+
stack: &mut *mut c_void,
444+
reg_size: usize,
445+
) -> T {
446+
if *r_offs >= 0 {
447+
return Self::va_arg_on_stack(stack); // reg save area empty
448+
}
449+
let (begin, end) = Self::get_begin_and_end::<T>(*r_offs as usize, reg_size);
450+
*r_offs = end as i32;
451+
if *r_offs > 0 {
452+
return Self::va_arg_on_stack(stack); // overflowed reg save area
453+
}
454+
ptr::read((r_top as usize).wrapping_add(begin) as *const T)
455+
}
456+
457+
unsafe fn va_arg_gr<T: sealed_trait::VaArgSafe>(&mut self) -> T {
458+
Self::va_arg_gr_or_vr(self.gr_top, &mut self.gr_offs, &mut self.stack, 8)
459+
}
460+
461+
unsafe fn va_arg_vr<T: sealed_trait::VaArgSafe>(&mut self) -> T {
462+
Self::va_arg_gr_or_vr(self.vr_top, &mut self.vr_offs, &mut self.stack, 16)
463+
}
464+
}

Diff for: src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/checkrust.rs

+30
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,33 @@ pub unsafe extern "C" fn check_varargs_1(_: c_int, mut ap: ...) -> usize {
9191
pub unsafe extern "C" fn check_varargs_2(_: c_int, _ap: ...) -> usize {
9292
0
9393
}
94+
95+
#[no_mangle]
96+
pub unsafe extern "C" fn check_varargs_3(_: c_int, mut ap: ...) -> usize {
97+
continue_if!(ap.arg::<c_int>() == 1);
98+
continue_if!(ap.arg::<c_int>() == 2);
99+
continue_if!(ap.arg::<c_int>() == 3);
100+
continue_if!(ap.arg::<c_int>() == 4);
101+
continue_if!(ap.arg::<c_int>() == 5);
102+
continue_if!(ap.arg::<c_int>() == 6);
103+
continue_if!(ap.arg::<c_int>() == 7);
104+
continue_if!(ap.arg::<c_int>() == 8);
105+
continue_if!(ap.arg::<c_int>() == 9);
106+
continue_if!(ap.arg::<c_int>() == 10);
107+
0
108+
}
109+
110+
#[no_mangle]
111+
pub unsafe extern "C" fn check_varargs_4(_: c_double, mut ap: ...) -> usize {
112+
continue_if!(ap.arg::<c_double>() == 1.0);
113+
continue_if!(ap.arg::<c_double>() == 2.0);
114+
continue_if!(ap.arg::<c_double>() == 3.0);
115+
continue_if!(ap.arg::<c_double>() == 4.0);
116+
continue_if!(ap.arg::<c_double>() == 5.0);
117+
continue_if!(ap.arg::<c_double>() == 6.0);
118+
continue_if!(ap.arg::<c_double>() == 7.0);
119+
continue_if!(ap.arg::<c_double>() == 8.0);
120+
continue_if!(ap.arg::<c_double>() == 9.0);
121+
continue_if!(ap.arg::<c_double>() == 10.0);
122+
0
123+
}

Diff for: src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c

+8
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ extern size_t check_list_copy_0(va_list ap);
1111
extern size_t check_varargs_0(int fixed, ...);
1212
extern size_t check_varargs_1(int fixed, ...);
1313
extern size_t check_varargs_2(int fixed, ...);
14+
extern size_t check_varargs_3(int fixed, ...);
15+
extern size_t check_varargs_4(double fixed, ...);
1416

1517
int test_rust(size_t (*fn)(va_list), ...) {
1618
size_t ret = 0;
@@ -36,5 +38,11 @@ int main(int argc, char* argv[]) {
3638

3739
assert(check_varargs_2(0, "All", "of", "these", "are", "ignored", ".") == 0);
3840

41+
// make sure we overflow the argument registers on AArch64
42+
assert(check_varargs_3(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) == 0);
43+
44+
// make sure we overflow the argument registers on AArch64
45+
assert(check_varargs_4(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0) == 0);
46+
3947
return 0;
4048
}

0 commit comments

Comments
 (0)