]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_target/src/asm/arm.rs
New upstream version 1.60.0+dfsg1
[rustc.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 is_clobber: bool,
73 ) -> Result<(), &'static str> {
74 not_thumb1(arch, target_features, target, is_clobber)?;
75
76 if !frame_pointer_is_r7(target_features, target) {
77 Err("the frame pointer (r11) cannot be used as an operand for inline asm")
78 } else {
79 Ok(())
80 }
81 }
82
83 fn frame_pointer_r7(
84 _arch: InlineAsmArch,
85 target_features: &FxHashSet<Symbol>,
86 target: &Target,
87 _is_clobber: bool,
88 ) -> Result<(), &'static str> {
89 if frame_pointer_is_r7(target_features, target) {
90 Err("the frame pointer (r7) cannot be used as an operand for inline asm")
91 } else {
92 Ok(())
93 }
94 }
95
96 fn not_thumb1(
97 _arch: InlineAsmArch,
98 target_features: &FxHashSet<Symbol>,
99 _target: &Target,
100 is_clobber: bool,
101 ) -> Result<(), &'static str> {
102 if !is_clobber
103 && target_features.contains(&sym::thumb_mode)
104 && !target_features.contains(&sym::thumb2)
105 {
106 Err("high registers (r8+) can only be used as clobbers in Thumb-1 code")
107 } else {
108 Ok(())
109 }
110 }
111
112 fn reserved_r9(
113 arch: InlineAsmArch,
114 target_features: &FxHashSet<Symbol>,
115 target: &Target,
116 is_clobber: bool,
117 ) -> Result<(), &'static str> {
118 not_thumb1(arch, target_features, target, is_clobber)?;
119
120 // We detect this using the reserved-r9 feature instead of using the target
121 // because the relocation model can be changed with compiler options.
122 if target_features.contains(&sym::reserved_r9) {
123 Err("the RWPI static base register (r9) cannot be used as an operand for inline asm")
124 } else {
125 Ok(())
126 }
127 }
128
129 def_regs! {
130 Arm ArmInlineAsmReg ArmInlineAsmRegClass {
131 r0: reg = ["r0", "a1"],
132 r1: reg = ["r1", "a2"],
133 r2: reg = ["r2", "a3"],
134 r3: reg = ["r3", "a4"],
135 r4: reg = ["r4", "v1"],
136 r5: reg = ["r5", "v2"],
137 r7: reg = ["r7", "v4"] % frame_pointer_r7,
138 r8: reg = ["r8", "v5"] % not_thumb1,
139 r9: reg = ["r9", "v6", "rfp"] % reserved_r9,
140 r10: reg = ["r10", "sl"] % not_thumb1,
141 r11: reg = ["r11", "fp"] % frame_pointer_r11,
142 r12: reg = ["r12", "ip"] % not_thumb1,
143 r14: reg = ["r14", "lr"] % not_thumb1,
144 s0: sreg, sreg_low16 = ["s0"],
145 s1: sreg, sreg_low16 = ["s1"],
146 s2: sreg, sreg_low16 = ["s2"],
147 s3: sreg, sreg_low16 = ["s3"],
148 s4: sreg, sreg_low16 = ["s4"],
149 s5: sreg, sreg_low16 = ["s5"],
150 s6: sreg, sreg_low16 = ["s6"],
151 s7: sreg, sreg_low16 = ["s7"],
152 s8: sreg, sreg_low16 = ["s8"],
153 s9: sreg, sreg_low16 = ["s9"],
154 s10: sreg, sreg_low16 = ["s10"],
155 s11: sreg, sreg_low16 = ["s11"],
156 s12: sreg, sreg_low16 = ["s12"],
157 s13: sreg, sreg_low16 = ["s13"],
158 s14: sreg, sreg_low16 = ["s14"],
159 s15: sreg, sreg_low16 = ["s15"],
160 s16: sreg = ["s16"],
161 s17: sreg = ["s17"],
162 s18: sreg = ["s18"],
163 s19: sreg = ["s19"],
164 s20: sreg = ["s20"],
165 s21: sreg = ["s21"],
166 s22: sreg = ["s22"],
167 s23: sreg = ["s23"],
168 s24: sreg = ["s24"],
169 s25: sreg = ["s25"],
170 s26: sreg = ["s26"],
171 s27: sreg = ["s27"],
172 s28: sreg = ["s28"],
173 s29: sreg = ["s29"],
174 s30: sreg = ["s30"],
175 s31: sreg = ["s31"],
176 d0: dreg, dreg_low16, dreg_low8 = ["d0"],
177 d1: dreg, dreg_low16, dreg_low8 = ["d1"],
178 d2: dreg, dreg_low16, dreg_low8 = ["d2"],
179 d3: dreg, dreg_low16, dreg_low8 = ["d3"],
180 d4: dreg, dreg_low16, dreg_low8 = ["d4"],
181 d5: dreg, dreg_low16, dreg_low8 = ["d5"],
182 d6: dreg, dreg_low16, dreg_low8 = ["d6"],
183 d7: dreg, dreg_low16, dreg_low8 = ["d7"],
184 d8: dreg, dreg_low16 = ["d8"],
185 d9: dreg, dreg_low16 = ["d9"],
186 d10: dreg, dreg_low16 = ["d10"],
187 d11: dreg, dreg_low16 = ["d11"],
188 d12: dreg, dreg_low16 = ["d12"],
189 d13: dreg, dreg_low16 = ["d13"],
190 d14: dreg, dreg_low16 = ["d14"],
191 d15: dreg, dreg_low16 = ["d15"],
192 d16: dreg = ["d16"],
193 d17: dreg = ["d17"],
194 d18: dreg = ["d18"],
195 d19: dreg = ["d19"],
196 d20: dreg = ["d20"],
197 d21: dreg = ["d21"],
198 d22: dreg = ["d22"],
199 d23: dreg = ["d23"],
200 d24: dreg = ["d24"],
201 d25: dreg = ["d25"],
202 d26: dreg = ["d26"],
203 d27: dreg = ["d27"],
204 d28: dreg = ["d28"],
205 d29: dreg = ["d29"],
206 d30: dreg = ["d30"],
207 d31: dreg = ["d31"],
208 q0: qreg, qreg_low8, qreg_low4 = ["q0"],
209 q1: qreg, qreg_low8, qreg_low4 = ["q1"],
210 q2: qreg, qreg_low8, qreg_low4 = ["q2"],
211 q3: qreg, qreg_low8, qreg_low4 = ["q3"],
212 q4: qreg, qreg_low8 = ["q4"],
213 q5: qreg, qreg_low8 = ["q5"],
214 q6: qreg, qreg_low8 = ["q6"],
215 q7: qreg, qreg_low8 = ["q7"],
216 q8: qreg = ["q8"],
217 q9: qreg = ["q9"],
218 q10: qreg = ["q10"],
219 q11: qreg = ["q11"],
220 q12: qreg = ["q12"],
221 q13: qreg = ["q13"],
222 q14: qreg = ["q14"],
223 q15: qreg = ["q15"],
224 #error = ["r6", "v3"] =>
225 "r6 is used internally by LLVM and cannot be used as an operand for inline asm",
226 #error = ["r13", "sp"] =>
227 "the stack pointer cannot be used as an operand for inline asm",
228 #error = ["r15", "pc"] =>
229 "the program pointer cannot be used as an operand for inline asm",
230 }
231 }
232
233 impl ArmInlineAsmReg {
234 pub fn emit(
235 self,
236 out: &mut dyn fmt::Write,
237 _arch: InlineAsmArch,
238 modifier: Option<char>,
239 ) -> fmt::Result {
240 // Only qreg is allowed to have modifiers. This should have been
241 // validated already by now.
242 if let Some(modifier) = modifier {
243 let index = self as u32 - Self::q0 as u32;
244 assert!(index < 16);
245 let index = index * 2 + (modifier == 'f') as u32;
246 write!(out, "d{}", index)
247 } else {
248 out.write_str(self.name())
249 }
250 }
251
252 pub fn overlapping_regs(self, mut cb: impl FnMut(ArmInlineAsmReg)) {
253 cb(self);
254
255 macro_rules! reg_conflicts {
256 (
257 $(
258 $q:ident : $d0:ident $d1:ident : $s0:ident $s1:ident $s2:ident $s3:ident
259 ),*;
260 $(
261 $q_high:ident : $d0_high:ident $d1_high:ident
262 ),*;
263 ) => {
264 match self {
265 $(
266 Self::$q => {
267 cb(Self::$d0);
268 cb(Self::$d1);
269 cb(Self::$s0);
270 cb(Self::$s1);
271 cb(Self::$s2);
272 cb(Self::$s3);
273 }
274 Self::$d0 => {
275 cb(Self::$q);
276 cb(Self::$s0);
277 cb(Self::$s1);
278 }
279 Self::$d1 => {
280 cb(Self::$q);
281 cb(Self::$s2);
282 cb(Self::$s3);
283 }
284 Self::$s0 | Self::$s1 => {
285 cb(Self::$q);
286 cb(Self::$d0);
287 }
288 Self::$s2 | Self::$s3 => {
289 cb(Self::$q);
290 cb(Self::$d1);
291 }
292 )*
293 $(
294 Self::$q_high => {
295 cb(Self::$d0_high);
296 cb(Self::$d1_high);
297 }
298 Self::$d0_high | Self::$d1_high => {
299 cb(Self::$q_high);
300 }
301 )*
302 _ => {},
303 }
304 };
305 }
306
307 // ARM's floating-point register file is interesting in that it can be
308 // viewed as 16 128-bit registers, 32 64-bit registers or 32 32-bit
309 // registers. Because these views overlap, the registers of different
310 // widths will conflict (e.g. d0 overlaps with s0 and s1, and q1
311 // overlaps with d2 and d3).
312 //
313 // See section E1.3.1 of the ARM Architecture Reference Manual for
314 // ARMv8-A for more details.
315 reg_conflicts! {
316 q0 : d0 d1 : s0 s1 s2 s3,
317 q1 : d2 d3 : s4 s5 s6 s7,
318 q2 : d4 d5 : s8 s9 s10 s11,
319 q3 : d6 d7 : s12 s13 s14 s15,
320 q4 : d8 d9 : s16 s17 s18 s19,
321 q5 : d10 d11 : s20 s21 s22 s23,
322 q6 : d12 d13 : s24 s25 s26 s27,
323 q7 : d14 d15 : s28 s29 s30 s31;
324 q8 : d16 d17,
325 q9 : d18 d19,
326 q10 : d20 d21,
327 q11 : d22 d23,
328 q12 : d24 d25,
329 q13 : d26 d27,
330 q14 : d28 d29,
331 q15 : d30 d31;
332 }
333 }
334 }