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
69fn 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 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 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}