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