rustc_target/asm/
arm.rs

1use std::fmt;
2
3use rustc_data_structures::fx::FxIndexSet;
4use rustc_span::{Symbol, sym};
5
6use super::{InlineAsmArch, InlineAsmType, ModifierInfo};
7use crate::spec::{RelocModel, Target};
8
9def_reg_class! {
10    Arm ArmInlineAsmRegClass {
11        reg,
12        sreg,
13        sreg_low16,
14        dreg,
15        dreg_low16,
16        dreg_low8,
17        qreg,
18        qreg_low8,
19        qreg_low4,
20    }
21}
22
23impl ArmInlineAsmRegClass {
24    pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] {
25        match self {
26            Self::qreg | Self::qreg_low8 | Self::qreg_low4 => &['e', 'f'],
27            _ => &[],
28        }
29    }
30
31    pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
32        None
33    }
34
35    pub fn suggest_modifier(
36        self,
37        _arch: InlineAsmArch,
38        _ty: InlineAsmType,
39    ) -> Option<ModifierInfo> {
40        None
41    }
42
43    pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> {
44        None
45    }
46
47    pub fn supported_types(
48        self,
49        _arch: InlineAsmArch,
50    ) -> &'static [(InlineAsmType, Option<Symbol>)] {
51        match self {
52            Self::reg => types! { _: I8, I16, I32, F16, F32; },
53            Self::sreg | Self::sreg_low16 => types! { vfp2: I32, F16, F32; },
54            Self::dreg_low16 | Self::dreg_low8 => types! {
55                vfp2: I64, F64;
56                neon: VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF16(4), VecF32(2);
57            },
58            Self::dreg => types! {
59                d32: I64, F64;
60                neon: VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF16(4), VecF32(2);
61            },
62            Self::qreg | Self::qreg_low8 | Self::qreg_low4 => types! {
63                neon: VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF16(8), VecF32(4);
64            },
65        }
66    }
67}
68
69// This uses the same logic as useR7AsFramePointer in LLVM
70fn frame_pointer_is_r7(target_features: &FxIndexSet<Symbol>, target: &Target) -> bool {
71    target.is_like_osx || (!target.is_like_windows && target_features.contains(&sym::thumb_mode))
72}
73
74fn frame_pointer_r11(
75    arch: InlineAsmArch,
76    reloc_model: RelocModel,
77    target_features: &FxIndexSet<Symbol>,
78    target: &Target,
79    is_clobber: bool,
80) -> Result<(), &'static str> {
81    not_thumb1(arch, reloc_model, target_features, target, is_clobber)?;
82
83    if !frame_pointer_is_r7(target_features, target) {
84        Err("the frame pointer (r11) cannot be used as an operand for inline asm")
85    } else {
86        Ok(())
87    }
88}
89
90fn frame_pointer_r7(
91    _arch: InlineAsmArch,
92    _reloc_model: RelocModel,
93    target_features: &FxIndexSet<Symbol>,
94    target: &Target,
95    _is_clobber: bool,
96) -> Result<(), &'static str> {
97    if frame_pointer_is_r7(target_features, target) {
98        Err("the frame pointer (r7) cannot be used as an operand for inline asm")
99    } else {
100        Ok(())
101    }
102}
103
104fn not_thumb1(
105    _arch: InlineAsmArch,
106    _reloc_model: RelocModel,
107    target_features: &FxIndexSet<Symbol>,
108    _target: &Target,
109    is_clobber: bool,
110) -> Result<(), &'static str> {
111    if !is_clobber
112        && target_features.contains(&sym::thumb_mode)
113        && !target_features.contains(&sym::thumb2)
114    {
115        Err("high registers (r8+) can only be used as clobbers in Thumb-1 code")
116    } else {
117        Ok(())
118    }
119}
120
121fn reserved_r9(
122    arch: InlineAsmArch,
123    reloc_model: RelocModel,
124    target_features: &FxIndexSet<Symbol>,
125    target: &Target,
126    is_clobber: bool,
127) -> Result<(), &'static str> {
128    not_thumb1(arch, reloc_model, target_features, target, is_clobber)?;
129
130    match reloc_model {
131        RelocModel::Rwpi | RelocModel::RopiRwpi => {
132            Err("the RWPI static base register (r9) cannot be used as an operand for inline asm")
133        }
134        _ => Ok(()),
135    }
136}
137
138def_regs! {
139    Arm ArmInlineAsmReg ArmInlineAsmRegClass {
140        r0: reg = ["r0", "a1"],
141        r1: reg = ["r1", "a2"],
142        r2: reg = ["r2", "a3"],
143        r3: reg = ["r3", "a4"],
144        r4: reg = ["r4", "v1"],
145        r5: reg = ["r5", "v2"],
146        r7: reg = ["r7", "v4"] % frame_pointer_r7,
147        r8: reg = ["r8", "v5"] % not_thumb1,
148        r9: reg = ["r9", "v6", "rfp"] % reserved_r9,
149        r10: reg = ["r10", "sl"] % not_thumb1,
150        r11: reg = ["r11", "fp"] % frame_pointer_r11,
151        r12: reg = ["r12", "ip"] % not_thumb1,
152        r14: reg = ["r14", "lr"] % not_thumb1,
153        s0: sreg_low16, sreg = ["s0"],
154        s1: sreg_low16, sreg = ["s1"],
155        s2: sreg_low16, sreg = ["s2"],
156        s3: sreg_low16, sreg = ["s3"],
157        s4: sreg_low16, sreg = ["s4"],
158        s5: sreg_low16, sreg = ["s5"],
159        s6: sreg_low16, sreg = ["s6"],
160        s7: sreg_low16, sreg = ["s7"],
161        s8: sreg_low16, sreg = ["s8"],
162        s9: sreg_low16, sreg = ["s9"],
163        s10: sreg_low16, sreg = ["s10"],
164        s11: sreg_low16, sreg = ["s11"],
165        s12: sreg_low16, sreg = ["s12"],
166        s13: sreg_low16, sreg = ["s13"],
167        s14: sreg_low16, sreg = ["s14"],
168        s15: sreg_low16, sreg = ["s15"],
169        s16: sreg = ["s16"],
170        s17: sreg = ["s17"],
171        s18: sreg = ["s18"],
172        s19: sreg = ["s19"],
173        s20: sreg = ["s20"],
174        s21: sreg = ["s21"],
175        s22: sreg = ["s22"],
176        s23: sreg = ["s23"],
177        s24: sreg = ["s24"],
178        s25: sreg = ["s25"],
179        s26: sreg = ["s26"],
180        s27: sreg = ["s27"],
181        s28: sreg = ["s28"],
182        s29: sreg = ["s29"],
183        s30: sreg = ["s30"],
184        s31: sreg = ["s31"],
185        d0: dreg_low8, dreg_low16, dreg = ["d0"],
186        d1: dreg_low8, dreg_low16, dreg = ["d1"],
187        d2: dreg_low8, dreg_low16, dreg = ["d2"],
188        d3: dreg_low8, dreg_low16, dreg = ["d3"],
189        d4: dreg_low8, dreg_low16, dreg = ["d4"],
190        d5: dreg_low8, dreg_low16, dreg = ["d5"],
191        d6: dreg_low8, dreg_low16, dreg = ["d6"],
192        d7: dreg_low8, dreg_low16, dreg = ["d7"],
193        d8: dreg_low16, dreg = ["d8"],
194        d9: dreg_low16, dreg = ["d9"],
195        d10: dreg_low16, dreg = ["d10"],
196        d11: dreg_low16, dreg = ["d11"],
197        d12: dreg_low16, dreg = ["d12"],
198        d13: dreg_low16, dreg = ["d13"],
199        d14: dreg_low16, dreg = ["d14"],
200        d15: dreg_low16, dreg = ["d15"],
201        d16: dreg = ["d16"],
202        d17: dreg = ["d17"],
203        d18: dreg = ["d18"],
204        d19: dreg = ["d19"],
205        d20: dreg = ["d20"],
206        d21: dreg = ["d21"],
207        d22: dreg = ["d22"],
208        d23: dreg = ["d23"],
209        d24: dreg = ["d24"],
210        d25: dreg = ["d25"],
211        d26: dreg = ["d26"],
212        d27: dreg = ["d27"],
213        d28: dreg = ["d28"],
214        d29: dreg = ["d29"],
215        d30: dreg = ["d30"],
216        d31: dreg = ["d31"],
217        q0: qreg_low4, qreg_low8, qreg = ["q0"],
218        q1: qreg_low4, qreg_low8, qreg = ["q1"],
219        q2: qreg_low4, qreg_low8, qreg = ["q2"],
220        q3: qreg_low4, qreg_low8, qreg = ["q3"],
221        q4: qreg_low8, qreg = ["q4"],
222        q5: qreg_low8, qreg = ["q5"],
223        q6: qreg_low8, qreg = ["q6"],
224        q7: qreg_low8, qreg = ["q7"],
225        q8: qreg = ["q8"],
226        q9: qreg = ["q9"],
227        q10: qreg = ["q10"],
228        q11: qreg = ["q11"],
229        q12: qreg = ["q12"],
230        q13: qreg = ["q13"],
231        q14: qreg = ["q14"],
232        q15: qreg = ["q15"],
233        #error = ["r6", "v3"] =>
234            "r6 is used internally by LLVM and cannot be used as an operand for inline asm",
235        #error = ["r13", "sp"] =>
236            "the stack pointer cannot be used as an operand for inline asm",
237        #error = ["r15", "pc"] =>
238            "the program pointer cannot be used as an operand for inline asm",
239    }
240}
241
242impl ArmInlineAsmReg {
243    pub fn emit(
244        self,
245        out: &mut dyn fmt::Write,
246        _arch: InlineAsmArch,
247        modifier: Option<char>,
248    ) -> fmt::Result {
249        // Only qreg is allowed to have modifiers. This should have been
250        // validated already by now.
251        if let Some(modifier) = modifier {
252            let index = self as u32 - Self::q0 as u32;
253            assert!(index < 16);
254            let index = index * 2 + (modifier == 'f') as u32;
255            write!(out, "d{index}")
256        } else {
257            out.write_str(self.name())
258        }
259    }
260
261    pub fn overlapping_regs(self, mut cb: impl FnMut(ArmInlineAsmReg)) {
262        cb(self);
263
264        macro_rules! reg_conflicts {
265            (
266                $(
267                    $q:ident : $d0:ident $d1:ident : $s0:ident $s1:ident $s2:ident $s3:ident
268                ),*;
269                $(
270                    $q_high:ident : $d0_high:ident $d1_high:ident
271                ),*;
272            ) => {
273                match self {
274                    $(
275                        Self::$q => {
276                            cb(Self::$d0);
277                            cb(Self::$d1);
278                            cb(Self::$s0);
279                            cb(Self::$s1);
280                            cb(Self::$s2);
281                            cb(Self::$s3);
282                        }
283                        Self::$d0 => {
284                            cb(Self::$q);
285                            cb(Self::$s0);
286                            cb(Self::$s1);
287                        }
288                        Self::$d1 => {
289                            cb(Self::$q);
290                            cb(Self::$s2);
291                            cb(Self::$s3);
292                        }
293                        Self::$s0 | Self::$s1 => {
294                            cb(Self::$q);
295                            cb(Self::$d0);
296                        }
297                        Self::$s2 | Self::$s3 => {
298                            cb(Self::$q);
299                            cb(Self::$d1);
300                        }
301                    )*
302                    $(
303                        Self::$q_high => {
304                            cb(Self::$d0_high);
305                            cb(Self::$d1_high);
306                        }
307                        Self::$d0_high | Self::$d1_high => {
308                            cb(Self::$q_high);
309                        }
310                    )*
311                    _ => {},
312                }
313            };
314        }
315
316        // ARM's floating-point register file is interesting in that it can be
317        // viewed as 16 128-bit registers, 32 64-bit registers or 32 32-bit
318        // registers. Because these views overlap, the registers of different
319        // widths will conflict (e.g. d0 overlaps with s0 and s1, and q1
320        // overlaps with d2 and d3).
321        //
322        // See section E1.3.1 of the ARM Architecture Reference Manual for
323        // ARMv8-A for more details.
324        reg_conflicts! {
325            q0 : d0 d1 : s0 s1 s2 s3,
326            q1 : d2 d3 : s4 s5 s6 s7,
327            q2 : d4 d5 : s8 s9 s10 s11,
328            q3 : d6 d7 : s12 s13 s14 s15,
329            q4 : d8 d9 : s16 s17 s18 s19,
330            q5 : d10 d11 : s20 s21 s22 s23,
331            q6 : d12 d13 : s24 s25 s26 s27,
332            q7 : d14 d15 : s28 s29 s30 s31;
333            q8 : d16 d17,
334            q9 : d18 d19,
335            q10 : d20 d21,
336            q11 : d22 d23,
337            q12 : d24 d25,
338            q13 : d26 d27,
339            q14 : d28 d29,
340            q15 : d30 d31;
341        }
342    }
343}