Skip to main content

rustc_target/callconv/
arm.rs

1use rustc_abi::{ArmCall, CanonAbi, HasDataLayout, TyAbiInterface};
2
3use crate::callconv::{ArgAbi, FnAbi, Reg, RegKind, Uniform};
4use crate::spec::{CfgAbi, HasTargetSpec, Os};
5
6#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for ArmAbiKind { }
#[automatically_derived]
impl ::core::clone::Clone for ArmAbiKind {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for ArmAbiKind { }Copy)]
7enum ArmAbiKind {
8    Aapcs,
9    AapcsVfp,
10    Aapcs16Vfp,
11}
12
13fn is_homogeneous_aggregate<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>) -> Option<Uniform>
14where
15    Ty: TyAbiInterface<'a, C> + Copy,
16    C: HasDataLayout,
17{
18    arg.layout.homogeneous_aggregate(cx).ok().and_then(|ha| ha.unit()).and_then(|unit| {
19        let size = arg.layout.size;
20
21        // Ensure we have at most four uniquely addressable members.
22        if size > unit.size.checked_mul(4, cx).unwrap() {
23            return None;
24        }
25
26        let valid_unit = match unit.kind {
27            RegKind::Integer => false,
28            RegKind::Float => true,
29            RegKind::PpcF128 => ::core::panicking::unreachable_default_msg()unreachable!(),
30            RegKind::Vector { .. } => unit.size.bits() == 64 || unit.size.bits() == 128,
31        };
32
33        valid_unit.then_some(Uniform::consecutive(unit, size))
34    })
35}
36
37fn classify_ret<'a, Ty, C>(cx: &C, ret: &mut ArgAbi<'a, Ty>, abi_kind: ArmAbiKind, vfp: bool)
38where
39    Ty: TyAbiInterface<'a, C> + Copy,
40    C: HasDataLayout,
41{
42    if !ret.layout.is_sized() {
43        // Not touching this...
44        return;
45    }
46    if !ret.layout.is_aggregate() {
47        ret.extend_integer_width_to(32);
48        return;
49    }
50
51    if vfp {
52        if let Some(uniform) = is_homogeneous_aggregate(cx, ret) {
53            ret.cast_to(uniform);
54            return;
55        }
56    }
57
58    let size = ret.layout.size;
59    let bits = size.bits();
60
61    if bits <= 32 {
62        // Aggregates <= 4 bytes are returned in r0; other aggregates are returned indirectly.
63        ret.cast_to(Uniform::new(Reg::i32(), size));
64        return;
65    } else if #[allow(non_exhaustive_omitted_patterns)] match abi_kind {
    ArmAbiKind::Aapcs16Vfp => true,
    _ => false,
}matches!(abi_kind, ArmAbiKind::Aapcs16Vfp) && bits <= 128 {
66        // watchOS returns the remaining aggregates of up to 128 bits in GPRs.
67        ret.cast_to(Uniform::consecutive(Reg::i32(), size));
68        return;
69    }
70
71    ret.make_indirect();
72}
73
74fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, abi_kind: ArmAbiKind, vfp: bool)
75where
76    Ty: TyAbiInterface<'a, C> + Copy,
77    C: HasDataLayout,
78{
79    if !arg.layout.is_sized() {
80        // Not touching this...
81        return;
82    }
83    if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
84        arg.make_indirect();
85        return;
86    }
87    if !arg.layout.is_aggregate() {
88        arg.extend_integer_width_to(32);
89        return;
90    }
91
92    // watchOS also passes homogeneous aggregates in VFP registers, and unlike `AapcsVfp` it does
93    // so even for variadics and for `extern "aapcs"`: the backend will use GPRs if needed.
94    if vfp || #[allow(non_exhaustive_omitted_patterns)] match abi_kind {
    ArmAbiKind::Aapcs16Vfp => true,
    _ => false,
}matches!(abi_kind, ArmAbiKind::Aapcs16Vfp) {
95        if let Some(uniform) = is_homogeneous_aggregate(cx, arg) {
96            arg.cast_to(uniform);
97            return;
98        }
99    }
100
101    // For the composites that are left, watchOS adopts the 64-bit AAPCS rule: those larger than
102    // 128 bits are placed in space allocated by the caller, and a pointer is passed.
103    if #[allow(non_exhaustive_omitted_patterns)] match abi_kind {
    ArmAbiKind::Aapcs16Vfp => true,
    _ => false,
}matches!(abi_kind, ArmAbiKind::Aapcs16Vfp) && arg.layout.size.bits() > 128 {
104        arg.make_indirect();
105        return;
106    }
107
108    let align = match abi_kind {
109        ArmAbiKind::Aapcs | ArmAbiKind::AapcsVfp => arg.layout.unadjusted_abi_align.bytes(),
110        ArmAbiKind::Aapcs16Vfp => arg.layout.align.bytes(),
111    };
112
113    let total = arg.layout.size;
114    arg.cast_to(Uniform::consecutive(if align <= 4 { Reg::i32() } else { Reg::i64() }, total));
115}
116
117pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
118where
119    Ty: TyAbiInterface<'a, C> + Copy,
120    C: HasDataLayout + HasTargetSpec,
121{
122    let abi_kind = if cx.target_spec().os == Os::WatchOs {
123        ArmAbiKind::Aapcs16Vfp
124    } else if cx.target_spec().cfg_abi == CfgAbi::EabiHf {
125        ArmAbiKind::AapcsVfp
126    } else {
127        ArmAbiKind::Aapcs
128    };
129
130    // Whether we must use the VFP registers for homogeneous aggregates.
131    let is_effectively_vfp = |accept_aapcs16| {
132        // When the user requested aapcs explicitly, honor that.
133        if #[allow(non_exhaustive_omitted_patterns)] match fn_abi.conv {
    CanonAbi::Arm(ArmCall::Aapcs) => true,
    _ => false,
}matches!(fn_abi.conv, CanonAbi::Arm(ArmCall::Aapcs)) {
134            return false;
135        }
136
137        match abi_kind {
138            ArmAbiKind::AapcsVfp => true,
139            ArmAbiKind::Aapcs16Vfp => accept_aapcs16,
140            ArmAbiKind::Aapcs => false,
141        }
142    };
143
144    if !fn_abi.ret.is_ignore() {
145        classify_ret(cx, &mut fn_abi.ret, abi_kind, !fn_abi.c_variadic && is_effectively_vfp(true));
146    }
147
148    let is_arg_vfp = !fn_abi.c_variadic && is_effectively_vfp(false);
149    for arg in fn_abi.args.iter_mut() {
150        if arg.is_ignore() {
151            continue;
152        }
153        classify_arg(cx, arg, abi_kind, is_arg_vfp);
154    }
155}