]> git.lizzy.rs Git - rust.git/blob - src/librustc_target/asm/mod.rs
ccec17817d37df6b0de2920cbe2a7d95fb862fef
[rust.git] / src / librustc_target / asm / mod.rs
1 use crate::abi::Size;
2 use crate::spec::Target;
3 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
4 use rustc_macros::HashStable_Generic;
5 use rustc_span::Symbol;
6 use std::fmt;
7 use std::str::FromStr;
8
9 #[macro_use]
10 macro_rules! def_reg_class {
11     ($arch:ident $arch_regclass:ident {
12         $(
13             $class:ident,
14         )*
15     }) => {
16         #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)]
17         #[allow(non_camel_case_types)]
18         pub enum $arch_regclass {
19             $($class,)*
20         }
21
22         impl $arch_regclass {
23             pub fn name(self) -> &'static str {
24                 match self {
25                     $(Self::$class => stringify!($class),)*
26                 }
27             }
28
29             pub fn parse(_arch: super::InlineAsmArch, name: &str) -> Result<Self, &'static str> {
30                 match name {
31                     $(
32                         stringify!($class) => Ok(Self::$class),
33                     )*
34                     _ => Err("unknown register class"),
35                 }
36             }
37         }
38
39         pub(super) fn regclass_map() -> rustc_data_structures::fx::FxHashMap<
40             super::InlineAsmRegClass,
41             rustc_data_structures::fx::FxHashSet<super::InlineAsmReg>,
42         > {
43             use rustc_data_structures::fx::{FxHashMap, FxHashSet};
44             use super::InlineAsmRegClass;
45             let mut map = FxHashMap::default();
46             $(
47                 map.insert(InlineAsmRegClass::$arch($arch_regclass::$class), FxHashSet::default());
48             )*
49             map
50         }
51     }
52 }
53
54 #[macro_use]
55 macro_rules! def_regs {
56     ($arch:ident $arch_reg:ident $arch_regclass:ident {
57         $(
58             $reg:ident: $class:ident $(, $extra_class:ident)* = [$reg_name:literal $(, $alias:literal)*] $(% $filter:ident)?,
59         )*
60         $(
61             #error = [$($bad_reg:literal),+] => $error:literal,
62         )*
63     }) => {
64         #[allow(unreachable_code)]
65         #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, Eq, PartialEq, Hash, HashStable_Generic)]
66         #[allow(non_camel_case_types)]
67         pub enum $arch_reg {
68             $($reg,)*
69         }
70
71         impl $arch_reg {
72             pub fn name(self) -> &'static str {
73                 match self {
74                     $(Self::$reg => $reg_name,)*
75                 }
76             }
77
78             pub fn reg_class(self) -> $arch_regclass {
79                 match self {
80                     $(Self::$reg => $arch_regclass::$class,)*
81                 }
82             }
83
84             pub fn parse(
85                 _arch: super::InlineAsmArch,
86                 mut _has_feature: impl FnMut(&str) -> bool,
87                 _target: &crate::spec::Target,
88                 name: &str,
89             ) -> Result<Self, &'static str> {
90                 match name {
91                     $(
92                         $($alias)|* | $reg_name => {
93                             $($filter(_arch, &mut _has_feature, _target, false)?;)?
94                             Ok(Self::$reg)
95                         }
96                     )*
97                     $(
98                         $($bad_reg)|* => Err($error),
99                     )*
100                     _ => Err("unknown register"),
101                 }
102             }
103         }
104
105         pub(super) fn fill_reg_map(
106             _arch: super::InlineAsmArch,
107             mut _has_feature: impl FnMut(&str) -> bool,
108             _target: &crate::spec::Target,
109             _map: &mut rustc_data_structures::fx::FxHashMap<
110                 super::InlineAsmRegClass,
111                 rustc_data_structures::fx::FxHashSet<super::InlineAsmReg>,
112             >,
113         ) {
114             #[allow(unused_imports)]
115             use super::{InlineAsmReg, InlineAsmRegClass};
116             $(
117                 if $($filter(_arch, &mut _has_feature, _target, true).is_ok() &&)? true {
118                     if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) {
119                         set.insert(InlineAsmReg::$arch($arch_reg::$reg));
120                     }
121                     $(
122                         if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$extra_class)) {
123                             set.insert(InlineAsmReg::$arch($arch_reg::$reg));
124                         }
125                     )*
126                 }
127             )*
128         }
129     }
130 }
131
132 #[macro_use]
133 macro_rules! types {
134     (
135         $(_ : $($ty:expr),+;)?
136         $($feature:literal: $($ty2:expr),+;)*
137     ) => {
138         {
139             use super::InlineAsmType::*;
140             &[
141                 $($(
142                     ($ty, None),
143                 )*)?
144                 $($(
145                     ($ty2, Some($feature)),
146                 )*)*
147             ]
148         }
149     };
150 }
151
152 mod aarch64;
153 mod arm;
154 mod hexagon;
155 mod nvptx;
156 mod riscv;
157 mod x86;
158
159 pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass};
160 pub use arm::{ArmInlineAsmReg, ArmInlineAsmRegClass};
161 pub use hexagon::{HexagonInlineAsmReg, HexagonInlineAsmRegClass};
162 pub use nvptx::{NvptxInlineAsmReg, NvptxInlineAsmRegClass};
163 pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass};
164 pub use x86::{X86InlineAsmReg, X86InlineAsmRegClass};
165
166 #[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, Eq, PartialEq, Hash)]
167 pub enum InlineAsmArch {
168     X86,
169     X86_64,
170     Arm,
171     AArch64,
172     RiscV32,
173     RiscV64,
174     Nvptx64,
175     Hexagon,
176 }
177
178 impl FromStr for InlineAsmArch {
179     type Err = ();
180
181     fn from_str(s: &str) -> Result<InlineAsmArch, ()> {
182         match s {
183             "x86" => Ok(Self::X86),
184             "x86_64" => Ok(Self::X86_64),
185             "arm" => Ok(Self::Arm),
186             "aarch64" => Ok(Self::AArch64),
187             "riscv32" => Ok(Self::RiscV32),
188             "riscv64" => Ok(Self::RiscV64),
189             "nvptx64" => Ok(Self::Nvptx64),
190             "hexagon" => Ok(Self::Hexagon),
191             _ => Err(()),
192         }
193     }
194 }
195
196 #[derive(
197     Copy,
198     Clone,
199     RustcEncodable,
200     RustcDecodable,
201     Debug,
202     Eq,
203     PartialEq,
204     Hash,
205     HashStable_Generic
206 )]
207 pub enum InlineAsmReg {
208     X86(X86InlineAsmReg),
209     Arm(ArmInlineAsmReg),
210     AArch64(AArch64InlineAsmReg),
211     RiscV(RiscVInlineAsmReg),
212     Nvptx(NvptxInlineAsmReg),
213     Hexagon(HexagonInlineAsmReg),
214 }
215
216 impl InlineAsmReg {
217     pub fn name(self) -> &'static str {
218         match self {
219             Self::X86(r) => r.name(),
220             Self::Arm(r) => r.name(),
221             Self::AArch64(r) => r.name(),
222             Self::RiscV(r) => r.name(),
223             Self::Hexagon(r) => r.name(),
224         }
225     }
226
227     pub fn reg_class(self) -> InlineAsmRegClass {
228         match self {
229             Self::X86(r) => InlineAsmRegClass::X86(r.reg_class()),
230             Self::Arm(r) => InlineAsmRegClass::Arm(r.reg_class()),
231             Self::AArch64(r) => InlineAsmRegClass::AArch64(r.reg_class()),
232             Self::RiscV(r) => InlineAsmRegClass::RiscV(r.reg_class()),
233             Self::Hexagon(r) => InlineAsmRegClass::Hexagon(r.reg_class()),
234         }
235     }
236
237     pub fn parse(
238         arch: InlineAsmArch,
239         has_feature: impl FnMut(&str) -> bool,
240         target: &Target,
241         name: Symbol,
242     ) -> Result<Self, &'static str> {
243         // FIXME: use direct symbol comparison for register names
244         // Use `Symbol::as_str` instead of `Symbol::with` here because `has_feature` may access `Symbol`.
245         let name = name.as_str();
246         Ok(match arch {
247             InlineAsmArch::X86 | InlineAsmArch::X86_64 => {
248                 Self::X86(X86InlineAsmReg::parse(arch, has_feature, target, &name)?)
249             }
250             InlineAsmArch::Arm => {
251                 Self::Arm(ArmInlineAsmReg::parse(arch, has_feature, target, &name)?)
252             }
253             InlineAsmArch::AArch64 => {
254                 Self::AArch64(AArch64InlineAsmReg::parse(arch, has_feature, target, &name)?)
255             }
256             InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {
257                 Self::RiscV(RiscVInlineAsmReg::parse(arch, has_feature, target, &name)?)
258             }
259             InlineAsmArch::Nvptx64 => {
260                 Self::Nvptx(NvptxInlineAsmReg::parse(arch, has_feature, target, &name)?)
261             }
262             InlineAsmArch::Hexagon => {
263                 Self::Hexagon(HexagonInlineAsmReg::parse(arch, has_feature, target, &name)?)
264             }
265         })
266     }
267
268     // NOTE: This function isn't used at the moment, but is needed to support
269     // falling back to an external assembler.
270     pub fn emit(
271         self,
272         out: &mut dyn fmt::Write,
273         arch: InlineAsmArch,
274         modifier: Option<char>,
275     ) -> fmt::Result {
276         match self {
277             Self::X86(r) => r.emit(out, arch, modifier),
278             Self::Arm(r) => r.emit(out, arch, modifier),
279             Self::AArch64(r) => r.emit(out, arch, modifier),
280             Self::RiscV(r) => r.emit(out, arch, modifier),
281             Self::Hexagon(r) => r.emit(out, arch, modifier),
282         }
283     }
284
285     pub fn overlapping_regs(self, mut cb: impl FnMut(InlineAsmReg)) {
286         match self {
287             Self::X86(r) => r.overlapping_regs(|r| cb(Self::X86(r))),
288             Self::Arm(r) => r.overlapping_regs(|r| cb(Self::Arm(r))),
289             Self::AArch64(_) => cb(self),
290             Self::RiscV(_) => cb(self),
291             Self::Hexagon(r) => r.overlapping_regs(|r| cb(Self::Hexagon(r))),
292         }
293     }
294 }
295
296 #[derive(
297     Copy,
298     Clone,
299     RustcEncodable,
300     RustcDecodable,
301     Debug,
302     Eq,
303     PartialEq,
304     Hash,
305     HashStable_Generic
306 )]
307 pub enum InlineAsmRegClass {
308     X86(X86InlineAsmRegClass),
309     Arm(ArmInlineAsmRegClass),
310     AArch64(AArch64InlineAsmRegClass),
311     RiscV(RiscVInlineAsmRegClass),
312     Nvptx(NvptxInlineAsmRegClass),
313     Hexagon(HexagonInlineAsmRegClass),
314 }
315
316 impl InlineAsmRegClass {
317     pub fn name(self) -> &'static str {
318         match self {
319             Self::X86(r) => r.name(),
320             Self::Arm(r) => r.name(),
321             Self::AArch64(r) => r.name(),
322             Self::RiscV(r) => r.name(),
323             Self::Nvptx(r) => r.name(),
324             Self::Hexagon(r) => r.name(),
325         }
326     }
327
328     /// Returns a suggested register class to use for this type. This is called
329     /// after type checking via `supported_types` fails to give a better error
330     /// message to the user.
331     pub fn suggest_class(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option<Self> {
332         match self {
333             Self::X86(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::X86),
334             Self::Arm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Arm),
335             Self::AArch64(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::AArch64),
336             Self::RiscV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::RiscV),
337             Self::Nvptx(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Nvptx),
338             Self::Hexagon(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Hexagon),
339         }
340     }
341
342     /// Returns a suggested template modifier to use for this type and an
343     /// example of a  register named formatted with it.
344     ///
345     /// Such suggestions are useful if a type smaller than the full register
346     /// size is used and a modifier can be used to point to the subregister of
347     /// the correct size.
348     pub fn suggest_modifier(
349         self,
350         arch: InlineAsmArch,
351         ty: InlineAsmType,
352     ) -> Option<(char, &'static str)> {
353         match self {
354             Self::X86(r) => r.suggest_modifier(arch, ty),
355             Self::Arm(r) => r.suggest_modifier(arch, ty),
356             Self::AArch64(r) => r.suggest_modifier(arch, ty),
357             Self::RiscV(r) => r.suggest_modifier(arch, ty),
358             Self::Nvptx(r) => r.suggest_modifier(arch, ty),
359             Self::Hexagon(r) => r.suggest_modifier(arch, ty),
360         }
361     }
362
363     /// Returns the default modifier for this register and an example of a
364     /// register named formatted with it.
365     ///
366     /// This is only needed when the register class can suggest a modifier, so
367     /// that the user can be shown how to get the default behavior without a
368     /// warning.
369     pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> {
370         match self {
371             Self::X86(r) => r.default_modifier(arch),
372             Self::Arm(r) => r.default_modifier(arch),
373             Self::AArch64(r) => r.default_modifier(arch),
374             Self::RiscV(r) => r.default_modifier(arch),
375             Self::Nvptx(r) => r.default_modifier(arch),
376             Self::Hexagon(r) => r.default_modifier(arch),
377         }
378     }
379
380     /// Returns a list of supported types for this register class, each with a
381     /// options target feature required to use this type.
382     pub fn supported_types(
383         self,
384         arch: InlineAsmArch,
385     ) -> &'static [(InlineAsmType, Option<&'static str>)] {
386         match self {
387             Self::X86(r) => r.supported_types(arch),
388             Self::Arm(r) => r.supported_types(arch),
389             Self::AArch64(r) => r.supported_types(arch),
390             Self::RiscV(r) => r.supported_types(arch),
391             Self::Nvptx(r) => r.supported_types(arch),
392             Self::Hexagon(r) => r.supported_types(arch),
393         }
394     }
395
396     pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result<Self, &'static str> {
397         // FIXME: use direct symbol comparison for register class names
398         name.with(|name| {
399             Ok(match arch {
400                 InlineAsmArch::X86 | InlineAsmArch::X86_64 => {
401                     Self::X86(X86InlineAsmRegClass::parse(arch, name)?)
402                 }
403                 InlineAsmArch::Arm => Self::Arm(ArmInlineAsmRegClass::parse(arch, name)?),
404                 InlineAsmArch::AArch64 => {
405                     Self::AArch64(AArch64InlineAsmRegClass::parse(arch, name)?)
406                 }
407                 InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {
408                     Self::RiscV(RiscVInlineAsmRegClass::parse(arch, name)?)
409                 }
410                 InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmRegClass::parse(arch, name)?),
411                 InlineAsmArch::Hexagon => {
412                     Self::Hexagon(HexagonInlineAsmRegClass::parse(arch, name)?)
413                 }
414             })
415         })
416     }
417
418     /// Returns the list of template modifiers that can be used with this
419     /// register class.
420     pub fn valid_modifiers(self, arch: InlineAsmArch) -> &'static [char] {
421         match self {
422             Self::X86(r) => r.valid_modifiers(arch),
423             Self::Arm(r) => r.valid_modifiers(arch),
424             Self::AArch64(r) => r.valid_modifiers(arch),
425             Self::RiscV(r) => r.valid_modifiers(arch),
426             Self::Nvptx(r) => r.valid_modifiers(arch),
427             Self::Hexagon(r) => r.valid_modifiers(arch),
428         }
429     }
430 }
431
432 #[derive(
433     Copy,
434     Clone,
435     RustcEncodable,
436     RustcDecodable,
437     Debug,
438     Eq,
439     PartialEq,
440     Hash,
441     HashStable_Generic
442 )]
443 pub enum InlineAsmRegOrRegClass {
444     Reg(InlineAsmReg),
445     RegClass(InlineAsmRegClass),
446 }
447
448 impl InlineAsmRegOrRegClass {
449     pub fn reg_class(self) -> InlineAsmRegClass {
450         match self {
451             Self::Reg(r) => r.reg_class(),
452             Self::RegClass(r) => r,
453         }
454     }
455 }
456
457 impl fmt::Display for InlineAsmRegOrRegClass {
458     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
459         match self {
460             Self::Reg(r) => write!(f, "\"{}\"", r.name()),
461             Self::RegClass(r) => f.write_str(r.name()),
462         }
463     }
464 }
465
466 /// Set of types which can be used with a particular register class.
467 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
468 pub enum InlineAsmType {
469     I8,
470     I16,
471     I32,
472     I64,
473     I128,
474     F32,
475     F64,
476     VecI8(u64),
477     VecI16(u64),
478     VecI32(u64),
479     VecI64(u64),
480     VecI128(u64),
481     VecF32(u64),
482     VecF64(u64),
483 }
484
485 impl InlineAsmType {
486     pub fn is_integer(self) -> bool {
487         match self {
488             Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128 => true,
489             _ => false,
490         }
491     }
492
493     pub fn size(self) -> Size {
494         Size::from_bytes(match self {
495             Self::I8 => 1,
496             Self::I16 => 2,
497             Self::I32 => 4,
498             Self::I64 => 8,
499             Self::I128 => 16,
500             Self::F32 => 4,
501             Self::F64 => 8,
502             Self::VecI8(n) => n * 1,
503             Self::VecI16(n) => n * 2,
504             Self::VecI32(n) => n * 4,
505             Self::VecI64(n) => n * 8,
506             Self::VecI128(n) => n * 16,
507             Self::VecF32(n) => n * 4,
508             Self::VecF64(n) => n * 8,
509         })
510     }
511 }
512
513 impl fmt::Display for InlineAsmType {
514     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
515         match *self {
516             Self::I8 => f.write_str("i8"),
517             Self::I16 => f.write_str("i16"),
518             Self::I32 => f.write_str("i32"),
519             Self::I64 => f.write_str("i64"),
520             Self::I128 => f.write_str("i128"),
521             Self::F32 => f.write_str("f32"),
522             Self::F64 => f.write_str("f64"),
523             Self::VecI8(n) => write!(f, "i8x{}", n),
524             Self::VecI16(n) => write!(f, "i16x{}", n),
525             Self::VecI32(n) => write!(f, "i32x{}", n),
526             Self::VecI64(n) => write!(f, "i64x{}", n),
527             Self::VecI128(n) => write!(f, "i128x{}", n),
528             Self::VecF32(n) => write!(f, "f32x{}", n),
529             Self::VecF64(n) => write!(f, "f64x{}", n),
530         }
531     }
532 }
533
534 /// Returns the full set of allocatable registers for a given architecture.
535 ///
536 /// The registers are structured as a map containing the set of allocatable
537 /// registers in each register class. A particular register may be allocatable
538 /// from multiple register classes, in which case it will appear multiple times
539 /// in the map.
540 // NOTE: This function isn't used at the moment, but is needed to support
541 // falling back to an external assembler.
542 pub fn allocatable_registers(
543     arch: InlineAsmArch,
544     has_feature: impl FnMut(&str) -> bool,
545     target: &crate::spec::Target,
546 ) -> FxHashMap<InlineAsmRegClass, FxHashSet<InlineAsmReg>> {
547     match arch {
548         InlineAsmArch::X86 | InlineAsmArch::X86_64 => {
549             let mut map = x86::regclass_map();
550             x86::fill_reg_map(arch, has_feature, target, &mut map);
551             map
552         }
553         InlineAsmArch::Arm => {
554             let mut map = arm::regclass_map();
555             arm::fill_reg_map(arch, has_feature, target, &mut map);
556             map
557         }
558         InlineAsmArch::AArch64 => {
559             let mut map = aarch64::regclass_map();
560             aarch64::fill_reg_map(arch, has_feature, target, &mut map);
561             map
562         }
563         InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => {
564             let mut map = riscv::regclass_map();
565             riscv::fill_reg_map(arch, has_feature, target, &mut map);
566             map
567         }
568         InlineAsmArch::Nvptx64 => {
569             let mut map = nvptx::regclass_map();
570             nvptx::fill_reg_map(arch, has_feature, target, &mut map);
571             map
572         }
573         InlineAsmArch::Hexagon => {
574             let mut map = hexagon::regclass_map();
575             hexagon::fill_reg_map(arch, has_feature, target, &mut map);
576             map
577         }
578     }
579 }