| //! The Xtensa ABI implementation | |
| //! | |
| //! This ABI implementation is based on the following sources: | |
| //! | |
| //! Section 8.1.4 & 8.1.5 of the Xtensa ISA reference manual, as well as snippets from | |
| //! Section 2.3 from the Xtensa programmers guide. | |
| use rustc_abi::{BackendRepr, HasDataLayout, Size, TyAbiInterface}; | |
| use crate::callconv::{ArgAbi, FnAbi, Reg, Uniform}; | |
| use crate::spec::HasTargetSpec; | |
| const NUM_ARG_GPRS: u64 = 6; | |
| const NUM_RET_GPRS: u64 = 4; | |
| const MAX_ARG_IN_REGS_SIZE: u64 = NUM_ARG_GPRS * 32; | |
| const MAX_RET_IN_REGS_SIZE: u64 = NUM_RET_GPRS * 32; | |
| fn classify_ret_ty<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) | |
| where | |
| Ty: TyAbiInterface<'a, C> + Copy, | |
| { | |
| if arg.is_ignore() { | |
| return; | |
| } | |
| // The rules for return and argument types are the same, | |
| // so defer to `classify_arg_ty`. | |
| let mut arg_gprs_left = NUM_RET_GPRS; | |
| classify_arg_ty(cx, arg, &mut arg_gprs_left, true); | |
| // Ret args cannot be passed via stack, we lower to indirect and let the backend handle the invisible reference | |
| match arg.mode { | |
| super::PassMode::Indirect { attrs: _, meta_attrs: _, ref mut on_stack } => { | |
| *on_stack = false; | |
| } | |
| _ => {} | |
| } | |
| } | |
| fn classify_arg_ty<'a, Ty, C>( | |
| cx: &C, | |
| arg: &mut ArgAbi<'a, Ty>, | |
| arg_gprs_left: &mut u64, | |
| is_ret: bool, | |
| ) where | |
| Ty: TyAbiInterface<'a, C> + Copy, | |
| { | |
| assert!(*arg_gprs_left <= NUM_ARG_GPRS, "Arg GPR tracking underflow"); | |
| let max_size = if is_ret { MAX_RET_IN_REGS_SIZE } else { MAX_ARG_IN_REGS_SIZE }; | |
| if !is_ret && arg.layout.pass_indirectly_in_non_rustic_abis(cx) { | |
| *arg_gprs_left = arg_gprs_left.saturating_sub(1); | |
| arg.make_indirect(); | |
| return; | |
| } | |
| // Ignore empty structs/unions. | |
| if arg.layout.is_zst() { | |
| return; | |
| } | |
| let size = arg.layout.size.bits(); | |
| let needed_align = arg.layout.align.bits(); | |
| let mut must_use_stack = false; | |
| // Determine the number of GPRs needed to pass the current argument | |
| // according to the ABI. 2*XLen-aligned varargs are passed in "aligned" | |
| // register pairs, so may consume 3 registers. | |
| let mut needed_arg_gprs = size.div_ceil(32); | |
| if needed_align == 64 { | |
| needed_arg_gprs += *arg_gprs_left % 2; | |
| } | |
| if needed_arg_gprs > *arg_gprs_left | |
| || needed_align > 128 | |
| || (*arg_gprs_left < (max_size / 32) && needed_align == 128) | |
| { | |
| must_use_stack = true; | |
| needed_arg_gprs = *arg_gprs_left; | |
| } | |
| *arg_gprs_left -= needed_arg_gprs; | |
| if must_use_stack { | |
| arg.pass_by_stack_offset(None); | |
| } else if is_xtensa_aggregate(arg) { | |
| // Aggregates which are <= max_size will be passed in | |
| // registers if possible, so coerce to integers. | |
| // Use a single `xlen` int if possible, 2 * `xlen` if 2 * `xlen` alignment | |
| // is required, and a 2-element `xlen` array if only `xlen` alignment is | |
| // required. | |
| if size <= 32 { | |
| arg.cast_to(Reg::i32()); | |
| } else { | |
| let reg = if needed_align == 2 * 32 { Reg::i64() } else { Reg::i32() }; | |
| let total = Size::from_bits(((size + 32 - 1) / 32) * 32); | |
| arg.cast_to(Uniform::new(reg, total)); | |
| } | |
| } else { | |
| // All integral types are promoted to `xlen` | |
| // width. | |
| // | |
| // We let the LLVM backend handle integral types >= xlen. | |
| if size < 32 { | |
| arg.extend_integer_width_to(32); | |
| } | |
| } | |
| } | |
| pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>) | |
| where | |
| Ty: TyAbiInterface<'a, C> + Copy, | |
| C: HasDataLayout + HasTargetSpec, | |
| { | |
| if !fn_abi.ret.is_ignore() { | |
| classify_ret_ty(cx, &mut fn_abi.ret); | |
| } | |
| let mut arg_gprs_left = NUM_ARG_GPRS; | |
| for arg in fn_abi.args.iter_mut() { | |
| if arg.is_ignore() { | |
| continue; | |
| } | |
| classify_arg_ty(cx, arg, &mut arg_gprs_left, false); | |
| } | |
| } | |
| fn is_xtensa_aggregate<'a, Ty>(arg: &ArgAbi<'a, Ty>) -> bool { | |
| match arg.layout.backend_repr { | |
| BackendRepr::SimdVector { .. } => true, | |
| _ => arg.layout.is_aggregate(), | |
| } | |
| } |