Skip to content

Commit 8556604

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

File tree

1 file changed

+104
-31
lines changed

1 file changed

+104
-31
lines changed

src/abi/mod.rs

+104-31
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;
@@ -17,7 +18,7 @@ use rustc_middle::ty::TypeVisitableExt;
1718
use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization;
1819
use rustc_session::Session;
1920
use rustc_span::source_map::Spanned;
20-
use rustc_target::abi::call::{Conv, FnAbi};
21+
use rustc_target::abi::call::{Conv, FnAbi, PassMode};
2122
use rustc_target::spec::abi::Abi;
2223

2324
use self::pass_mode::*;
@@ -487,6 +488,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
487488
let args = args;
488489
assert_eq!(fn_abi.args.len(), args.len());
489490

491+
#[derive(Copy, Clone)]
490492
enum CallTarget {
491493
Direct(FuncRef),
492494
Indirect(SigRef, Value),
@@ -532,7 +534,7 @@ 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+
let mut call_args = return_ptr
536538
.into_iter()
537539
.chain(first_arg_override.into_iter())
538540
.chain(
@@ -545,47 +547,118 @@ pub(crate) fn codegen_terminator_call<'tcx>(
545547
)
546548
.collect::<Vec<Value>>();
547549

548-
let call_inst = match func_ref {
550+
// FIXME: Find a cleaner way to support varargs.
551+
if fn_abi.c_variadic {
552+
adjust_call_for_c_variadic(fx, &fn_abi, source_info, func_ref, &mut call_args);
553+
}
554+
555+
match func_ref {
549556
CallTarget::Direct(func_ref) => fx.bcx.ins().call(func_ref, &call_args),
550557
CallTarget::Indirect(sig, func_ptr) => {
551558
fx.bcx.ins().call_indirect(sig, func_ptr, &call_args)
552559
}
560+
}
561+
});
562+
563+
if let Some(dest) = target {
564+
let ret_block = fx.get_block(dest);
565+
fx.bcx.ins().jump(ret_block, &[]);
566+
} else {
567+
fx.bcx.ins().trap(TrapCode::UnreachableCodeReached);
568+
}
569+
570+
fn adjust_call_for_c_variadic<'tcx>(
571+
fx: &mut FunctionCx<'_, '_, 'tcx>,
572+
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
573+
source_info: mir::SourceInfo,
574+
target: CallTarget,
575+
call_args: &mut Vec<Value>,
576+
) {
577+
if fn_abi.conv != Conv::C {
578+
fx.tcx.dcx().span_fatal(
579+
source_info.span,
580+
format!("Variadic call for non-C abi {:?}", fn_abi.conv),
581+
);
582+
}
583+
let sig_ref = match target {
584+
CallTarget::Direct(func_ref) => fx.bcx.func.dfg.ext_funcs[func_ref].signature,
585+
CallTarget::Indirect(sig_ref, _) => sig_ref,
553586
};
587+
// `mem::take()` the `params` so that `fx.bcx` can be used below.
588+
let mut abi_params = mem::take(&mut fx.bcx.func.dfg.signatures[sig_ref].params);
589+
590+
// Recalculate the parameters in the signature to ensure the signature contains the variadic arguments.
591+
let has_return_arg = matches!(fn_abi.ret.mode, PassMode::Indirect { .. });
592+
// Drop everything except the return argument (if there is one).
593+
abi_params.truncate(if has_return_arg { 1 } else { 0 });
594+
// Add the fixed arguments.
595+
abi_params.extend(
596+
fn_abi.args[..fn_abi.fixed_count as usize]
597+
.iter()
598+
.flat_map(|arg_abi| arg_abi.get_abi_param(fx.tcx).into_iter()),
599+
);
600+
let fixed_arg_count = abi_params.len();
601+
// Add the variadic arguments.
602+
abi_params.extend(
603+
fn_abi.args[fn_abi.fixed_count as usize..]
604+
.iter()
605+
.flat_map(|arg_abi| arg_abi.get_abi_param(fx.tcx).into_iter()),
606+
);
554607

555-
// FIXME find a cleaner way to support varargs
556-
if fn_sig.c_variadic() {
557-
if !matches!(fn_sig.abi(), Abi::C { .. }) {
608+
if fx.tcx.sess.target.is_like_osx && fx.tcx.sess.target.arch == "aarch64" {
609+
// Add any padding arguments needed for Apple AArch64.
610+
// There's no need to pad the argument list unless variadic arguments are actually being
611+
// passed.
612+
if abi_params.len() > fixed_arg_count {
613+
// 128-bit integers take 2 registers, and everything else takes 1.
614+
// FIXME: Add support for non-integer types
615+
// This relies on the checks below to ensure all arguments are integer types and
616+
// that the ABI is "C".
617+
// The return argument isn't counted as it goes in its own dedicated register.
618+
let integer_registers_used: usize = abi_params
619+
[if has_return_arg { 1 } else { 0 }..fixed_arg_count]
620+
.iter()
621+
.map(|arg| if arg.value_type.bits() == 128 { 2 } else { 1 })
622+
.sum();
623+
// The ABI uses 8 registers before it starts pushing arguments to the stack. Pad out
624+
// the registers if needed to ensure the variadic arguments are passed on the stack.
625+
if integer_registers_used < 8 {
626+
abi_params.splice(
627+
fixed_arg_count..fixed_arg_count,
628+
(integer_registers_used..8).map(|_| AbiParam::new(types::I64)),
629+
);
630+
call_args.splice(
631+
fixed_arg_count..fixed_arg_count,
632+
(integer_registers_used..8).map(|_| fx.bcx.ins().iconst(types::I64, 0)),
633+
);
634+
}
635+
}
636+
637+
// `StructArgument` is not currently used by the `aarch64` ABI, and is therefore not
638+
// handled when calculating how many padding arguments to use. Assert that this remains
639+
// the case.
640+
assert!(abi_params.iter().all(|param| matches!(
641+
param.purpose,
642+
// The only purposes used are `Normal` and `StructReturn`.
643+
ArgumentPurpose::Normal | ArgumentPurpose::StructReturn
644+
)));
645+
}
646+
647+
// Check all parameters are integers.
648+
for param in abi_params.iter() {
649+
if !param.value_type.is_int() {
650+
// FIXME: Set %al to upperbound on float args once floats are supported.
558651
fx.tcx.dcx().span_fatal(
559652
source_info.span,
560-
format!("Variadic call for non-C abi {:?}", fn_sig.abi()),
653+
format!("Non int ty {:?} for variadic call", param.value_type),
561654
);
562655
}
563-
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;
579656
}
580657

581-
call_inst
582-
});
658+
assert_eq!(abi_params.len(), call_args.len());
583659

584-
if let Some(dest) = target {
585-
let ret_block = fx.get_block(dest);
586-
fx.bcx.ins().jump(ret_block, &[]);
587-
} else {
588-
fx.bcx.ins().trap(TrapCode::UnreachableCodeReached);
660+
// Put the `AbiParam`s back in the signature.
661+
fx.bcx.func.dfg.signatures[sig_ref].params = abi_params;
589662
}
590663
}
591664

0 commit comments

Comments
 (0)