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