Skip to content

Commit fc742a7

Browse files
committed
Fix varargs support on aarch64-apple-darwin
1 parent 1cb7282 commit fc742a7

File tree

1 file changed

+81
-19
lines changed

1 file changed

+81
-19
lines changed

src/abi/mod.rs

Lines changed: 81 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ mod returning;
66

77
use std::borrow::Cow;
88

9-
use cranelift_codegen::ir::SigRef;
9+
use cranelift_codegen::ir::{ArgumentPurpose, SigRef};
1010
use cranelift_codegen::isa::CallConv;
1111
use cranelift_module::ModuleError;
1212
use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall;
@@ -532,18 +532,64 @@ pub(crate) fn codegen_terminator_call<'tcx>(
532532
};
533533

534534
self::returning::codegen_with_call_return_arg(fx, &fn_abi.ret, ret_place, |fx, return_ptr| {
535-
let call_args = return_ptr
535+
// The majority of this block is hacky workarounds for cranelift not supporting C variadic
536+
// functions. The only bits that aren't are the `let mut call_args =` statement (excluding
537+
// the `.take()`; `(&mut args_iter)` can be replace with `args.into_iter().enumerate()`)
538+
// and the `let call_inst = ` statement.
539+
540+
let has_return_arg = return_ptr.is_some();
541+
let mut args_iter = args.into_iter().enumerate();
542+
let mut call_args = return_ptr
536543
.into_iter()
537544
.chain(first_arg_override.into_iter())
538545
.chain(
539-
args.into_iter()
540-
.enumerate()
546+
(&mut args_iter)
547+
// If the function is variadic, only add the fixed argument first so that how
548+
// many `Value`s the fixed arguments expand to can be counted.
549+
.take(fn_abi.fixed_count as usize)
541550
.skip(if first_arg_override.is_some() { 1 } else { 0 })
542551
.flat_map(|(i, arg)| {
543552
adjust_arg_for_abi(fx, arg.value, &fn_abi.args[i], arg.is_owned).into_iter()
544553
}),
545554
)
546555
.collect::<Vec<Value>>();
556+
// Count how many `Value`s represent the fixed arguments.
557+
let fixed_arg_count = call_args.len();
558+
// Add the rest of the arguments. This is a no-op unless the function is variadic.
559+
call_args.extend(args_iter.flat_map(|(i, arg)| {
560+
adjust_arg_for_abi(fx, arg.value, &fn_abi.args[i], arg.is_owned).into_iter()
561+
}));
562+
563+
let padding_arg_count = if fx.tcx.sess.target.is_like_osx
564+
&& fx.tcx.sess.target.arch == "aarch64"
565+
&& fn_sig.c_variadic()
566+
// No need to pad the argument list unless variadic arguments are actually being passed.
567+
&& call_args.len() > fixed_arg_count
568+
{
569+
// 128-bit integers take 2 registers, and everything else takes 1.
570+
// FIXME: Add support for non-integer types
571+
// This relies on the checks below to ensure all arguments are integer types and that
572+
// the ABI is "C".
573+
// The return argument isn't counted as it goes in its own dedicated register.
574+
let integer_registers_used: usize = call_args
575+
[if has_return_arg { 1 } else { 0 }..fixed_arg_count]
576+
.iter()
577+
.map(|arg| if fx.bcx.func.dfg.value_type(*arg).bits() == 128 { 2 } else { 1 })
578+
.sum();
579+
// The ABI uses 8 registers before it starts pushing arguments to the stack. Pad out
580+
// the registers if needed to ensure the variadic arguments are passed on the stack.
581+
if integer_registers_used < 8 {
582+
call_args.splice(
583+
fixed_arg_count..fixed_arg_count,
584+
(integer_registers_used..8).map(|_| fx.bcx.ins().iconst(types::I64, 0)),
585+
);
586+
8 - integer_registers_used
587+
} else {
588+
0
589+
}
590+
} else {
591+
0
592+
};
547593

548594
let call_inst = match func_ref {
549595
CallTarget::Direct(func_ref) => fx.bcx.ins().call(func_ref, &call_args),
@@ -561,21 +607,37 @@ pub(crate) fn codegen_terminator_call<'tcx>(
561607
);
562608
}
563609
let sig_ref = fx.bcx.func.dfg.call_signature(call_inst).unwrap();
564-
let abi_params = call_args
565-
.into_iter()
566-
.map(|arg| {
567-
let ty = fx.bcx.func.dfg.value_type(arg);
568-
if !ty.is_int() {
569-
// FIXME set %al to upperbound on float args once floats are supported
570-
fx.tcx.dcx().span_fatal(
571-
source_info.span,
572-
format!("Non int ty {:?} for variadic call", ty),
573-
);
574-
}
575-
AbiParam::new(ty)
576-
})
577-
.collect::<Vec<AbiParam>>();
578-
fx.bcx.func.dfg.signatures[sig_ref].params = abi_params;
610+
// Ensure the signature includes all the extra arguments.
611+
fx.bcx.func.dfg.signatures[sig_ref] =
612+
clif_sig_from_fn_abi(fx.tcx, fx.target_config.default_call_conv, &fn_abi);
613+
// Add any padding arguments needed for Apple AArch64.
614+
fx.bcx.func.dfg.signatures[sig_ref].params.splice(
615+
fixed_arg_count..fixed_arg_count,
616+
(0..padding_arg_count).map(|_| AbiParam::new(types::I64)),
617+
);
618+
// Check all parameters are integers.
619+
for param in &fx.bcx.func.dfg.signatures[sig_ref].params {
620+
if !param.value_type.is_int() {
621+
// FIXME set %al to upperbound on float args once floats are supported
622+
fx.tcx.dcx().span_fatal(
623+
source_info.span,
624+
format!("Non int ty {:?} for variadic call", param.value_type),
625+
);
626+
}
627+
}
628+
629+
if fx.tcx.sess.target.is_like_osx && fx.tcx.sess.target.arch == "aarch64" {
630+
// `StructArgument` is not currently used by the `aarch64`, and is therefore not
631+
// handled when calculating how many padding arguments to use. Assert that this
632+
// remains the case.
633+
assert!(fx.bcx.func.dfg.signatures[sig_ref].params.iter().all(|param| matches!(
634+
param.purpose,
635+
// The only purposes used are `Normal` and `StructReturn`.
636+
ArgumentPurpose::Normal | ArgumentPurpose::StructReturn
637+
)))
638+
}
639+
640+
assert_eq!(fx.bcx.func.dfg.signatures[sig_ref].params.len(), call_args.len());
579641
}
580642

581643
call_inst

0 commit comments

Comments
 (0)