Skip to content

Commit f5a43d3

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

File tree

1 file changed

+78
-19
lines changed

1 file changed

+78
-19
lines changed

src/abi/mod.rs

Lines changed: 78 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ mod pass_mode;
55
mod returning;
66

77
use std::borrow::Cow;
8+
use std::mem;
89

9-
use cranelift_codegen::ir::SigRef;
10+
use cranelift_codegen::ir::{ArgumentPurpose, SigRef};
1011
use cranelift_codegen::isa::CallConv;
1112
use cranelift_module::ModuleError;
1213
use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall;
@@ -425,7 +426,8 @@ pub(crate) fn codegen_terminator_call<'tcx>(
425426
None
426427
};
427428

428-
let extra_args = &args[fn_sig.inputs().skip_binder().len()..];
429+
let fixed_arg_count = fn_sig.inputs().skip_binder().len();
430+
let extra_args = &args[fixed_arg_count..];
429431
let extra_args = fx.tcx.mk_type_list_from_iter(
430432
extra_args.iter().map(|op_arg| fx.monomorphize(op_arg.node.ty(fx.mir, fx.tcx))),
431433
);
@@ -532,18 +534,59 @@ pub(crate) fn codegen_terminator_call<'tcx>(
532534
};
533535

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

548591
let call_inst = match func_ref {
549592
CallTarget::Direct(func_ref) => fx.bcx.ins().call(func_ref, &call_args),
@@ -561,20 +604,36 @@ pub(crate) fn codegen_terminator_call<'tcx>(
561604
);
562605
}
563606
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>>();
607+
let mut abi_params = mem::take(&mut fx.bcx.func.dfg.signatures[sig_ref].params);
608+
assert_eq!(abi_params.len(), fixed_arg_count);
609+
if fx.tcx.sess.target.is_like_osx && fx.tcx.sess.target.arch == "aarch64" {
610+
// `StructArgument` is not currently used by the `aarch64`, and is therefore not
611+
// handled when calculating how many padding arguments to use. Assert that this
612+
// remains the case.
613+
assert!(abi_params.iter().all(|param| matches!(
614+
param.purpose,
615+
// The only purposes used are `Normal` and `StructReturn`.
616+
ArgumentPurpose::Normal | ArgumentPurpose::StructReturn
617+
)))
618+
}
619+
// Don't replace existing `AbiParam`s as they might have `ArgumentPurpose` or
620+
// `ArgumentExtension`.
621+
abi_params.extend(
622+
call_args
623+
.into_iter()
624+
.map(|arg| {
625+
let ty = fx.bcx.func.dfg.value_type(arg);
626+
if !ty.is_int() {
627+
// FIXME set %al to upperbound on float args once floats are supported
628+
fx.tcx.dcx().span_fatal(
629+
source_info.span,
630+
format!("Non int ty {:?} for variadic call", ty),
631+
);
632+
}
633+
AbiParam::new(ty)
634+
})
635+
.skip(fixed_arg_count),
636+
);
578637
fx.bcx.func.dfg.signatures[sig_ref].params = abi_params;
579638
}
580639

0 commit comments

Comments
 (0)