Skip to content

Commit fc52b47

Browse files
committed
va_args implementation for AAPCS.
Implement the va args in codegen for AAPCS, this will be used as the default va_args implementation for AArch64 rather than the va_args llvm-ir as it currently is. Copyright (c) 2020, Arm Limited.
1 parent a1528c4 commit fc52b47

File tree

4 files changed

+151
-3
lines changed

4 files changed

+151
-3
lines changed

src/librustc_codegen_llvm/builder.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -1279,7 +1279,12 @@ impl Builder<'a, 'll, 'tcx> {
12791279
self.call(lifetime_intrinsic, &[self.cx.const_u64(size), ptr], None);
12801280
}
12811281

1282-
fn phi(&mut self, ty: &'ll Type, vals: &[&'ll Value], bbs: &[&'ll BasicBlock]) -> &'ll Value {
1282+
pub(crate) fn phi(
1283+
&mut self,
1284+
ty: &'ll Type,
1285+
vals: &[&'ll Value],
1286+
bbs: &[&'ll BasicBlock],
1287+
) -> &'ll Value {
12831288
assert_eq!(vals.len(), bbs.len());
12841289
let phi = unsafe { llvm::LLVMBuildPhi(self.llbuilder, ty, UNNAMED) };
12851290
unsafe {

src/librustc_codegen_llvm/va_arg.rs

+80-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ use crate::type_::Type;
33
use crate::type_of::LayoutLlvmExt;
44
use crate::value::Value;
55
use rustc_codegen_ssa::mir::operand::OperandRef;
6-
use rustc_codegen_ssa::traits::{
7-
BaseTypeMethods, BuilderMethods, ConstMethods, DerivedTypeMethods,
6+
use rustc_codegen_ssa::{
7+
common::IntPredicate,
8+
traits::{BaseTypeMethods, BuilderMethods, ConstMethods, DerivedTypeMethods},
89
};
910
use rustc_middle::ty::layout::HasTyCtxt;
1011
use rustc_middle::ty::Ty;
@@ -89,6 +90,82 @@ fn emit_ptr_va_arg(
8990
}
9091
}
9192

93+
fn emit_aapcs_va_arg(
94+
bx: &mut Builder<'a, 'll, 'tcx>,
95+
list: OperandRef<'tcx, &'ll Value>,
96+
target_ty: Ty<'tcx>,
97+
) -> &'ll Value {
98+
// Implementation of the AAPCS64 calling convention for va_args see
99+
// https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst
100+
let va_list_addr = list.immediate();
101+
let layout = bx.cx.layout_of(target_ty);
102+
103+
let mut maybe_reg = bx.build_sibling_block("va_arg.maybe_reg");
104+
let mut in_reg = bx.build_sibling_block("va_arg.in_reg");
105+
let mut on_stack = bx.build_sibling_block("va_arg.on_stack");
106+
let mut end = bx.build_sibling_block("va_arg.end");
107+
let zero = bx.const_i32(0);
108+
let offset_align = Align::from_bytes(4).unwrap();
109+
assert!(&*bx.tcx().sess.target.target.target_endian == "little");
110+
111+
let gr_type = target_ty.is_any_ptr() || target_ty.is_integral();
112+
let (reg_off, reg_top_index, slot_size) = if gr_type {
113+
let gr_offs = bx.struct_gep(va_list_addr, 7);
114+
let nreg = (layout.size.bytes() + 7) / 8;
115+
(gr_offs, 3, nreg * 8)
116+
} else {
117+
let vr_off = bx.struct_gep(va_list_addr, 9);
118+
let nreg = (layout.size.bytes() + 15) / 16;
119+
(vr_off, 5, nreg * 16)
120+
};
121+
122+
// if the offset >= 0 then the value will be on the stack
123+
let mut reg_off_v = bx.load(reg_off, offset_align);
124+
let use_stack = bx.icmp(IntPredicate::IntSGE, reg_off_v, zero);
125+
bx.cond_br(use_stack, &on_stack.llbb(), &maybe_reg.llbb());
126+
127+
// The value at this point might be in a register, but there is a chance that
128+
// it could be on the stack so we have to update the offset and then check
129+
// the offset again.
130+
131+
if layout.align.abi.bytes() > 8 {
132+
assert!(layout.align.abi.bytes() <= 16);
133+
reg_off_v = maybe_reg.add(reg_off_v, bx.const_i32(15));
134+
reg_off_v = maybe_reg.and(reg_off_v, bx.const_i32(-16));
135+
}
136+
let new_reg_off_v = maybe_reg.add(reg_off_v, bx.const_i32(slot_size as i32));
137+
138+
maybe_reg.store(new_reg_off_v, reg_off, offset_align);
139+
140+
// Check to see if we have overflowed the registers as a result of this.
141+
// If we have then we need to use the stack for this value
142+
let use_stack = maybe_reg.icmp(IntPredicate::IntSGT, new_reg_off_v, zero);
143+
maybe_reg.cond_br(use_stack, &on_stack.llbb(), &in_reg.llbb());
144+
145+
let top = in_reg.struct_gep(va_list_addr, reg_top_index);
146+
let top = in_reg.load(top, bx.tcx().data_layout.pointer_align.abi);
147+
148+
// reg_value = *(@top + reg_off_v);
149+
let top = in_reg.gep(top, &[reg_off_v]);
150+
let top = in_reg.bitcast(top, bx.cx.type_ptr_to(layout.llvm_type(bx)));
151+
let reg_value = in_reg.load(top, layout.align.abi);
152+
in_reg.br(&end.llbb());
153+
154+
// On Stack block
155+
let stack_value =
156+
emit_ptr_va_arg(&mut on_stack, list, target_ty, false, Align::from_bytes(8).unwrap(), true);
157+
on_stack.br(&end.llbb());
158+
159+
let val = end.phi(
160+
layout.immediate_llvm_type(bx),
161+
&[reg_value, stack_value],
162+
&[&in_reg.llbb(), &on_stack.llbb()],
163+
);
164+
165+
*bx = end;
166+
val
167+
}
168+
92169
pub(super) fn emit_va_arg(
93170
bx: &mut Builder<'a, 'll, 'tcx>,
94171
addr: OperandRef<'tcx, &'ll Value>,
@@ -115,6 +192,7 @@ pub(super) fn emit_va_arg(
115192
("aarch64", _) if target.target_os == "ios" => {
116193
emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(8).unwrap(), true)
117194
}
195+
("aarch64", _) => emit_aapcs_va_arg(bx, addr, target_ty),
118196
// Windows x86_64
119197
("x86_64", true) => {
120198
let target_ty_size = bx.cx.size_of(target_ty).bytes();

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

+55
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,58 @@ 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+
}
124+
125+
#[no_mangle]
126+
pub unsafe extern "C" fn check_varargs_5(_: c_int, mut ap: ...) -> usize {
127+
continue_if!(ap.arg::<c_double>() == 1.0);
128+
continue_if!(ap.arg::<c_int>() == 1);
129+
continue_if!(ap.arg::<c_double>() == 2.0);
130+
continue_if!(ap.arg::<c_int>() == 2);
131+
continue_if!(ap.arg::<c_double>() == 3.0);
132+
continue_if!(ap.arg::<c_int>() == 3);
133+
continue_if!(ap.arg::<c_double>() == 4.0);
134+
continue_if!(ap.arg::<c_int>() == 4);
135+
continue_if!(ap.arg::<c_int>() == 5);
136+
continue_if!(ap.arg::<c_double>() == 5.0);
137+
continue_if!(ap.arg::<c_int>() == 6);
138+
continue_if!(ap.arg::<c_double>() == 6.0);
139+
continue_if!(ap.arg::<c_int>() == 7);
140+
continue_if!(ap.arg::<c_double>() == 7.0);
141+
continue_if!(ap.arg::<c_int>() == 8);
142+
continue_if!(ap.arg::<c_double>() == 8.0);
143+
continue_if!(ap.arg::<c_int>() == 9);
144+
continue_if!(ap.arg::<c_double>() == 9.0);
145+
continue_if!(ap.arg::<c_int>() == 10);
146+
continue_if!(ap.arg::<c_double>() == 10.0);
147+
0
148+
}

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

+10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ 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, ...);
16+
extern size_t check_varargs_5(int fixed, ...);
1417

1518
int test_rust(size_t (*fn)(va_list), ...) {
1619
size_t ret = 0;
@@ -36,5 +39,12 @@ int main(int argc, char* argv[]) {
3639

3740
assert(check_varargs_2(0, "All", "of", "these", "are", "ignored", ".") == 0);
3841

42+
assert(check_varargs_3(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) == 0);
43+
44+
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);
45+
46+
assert(check_varargs_5(0, 1.0, 1, 2.0, 2, 3.0, 3, 4.0, 4, 5, 5.0, 6, 6.0, 7, 7.0, 8, 8.0,
47+
9, 9.0, 10, 10.0) == 0);
48+
3949
return 0;
4050
}

0 commit comments

Comments
 (0)