about summary refs log tree commit diff
path: root/compiler/rustc_target/src/asm/arm.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_target/src/asm/arm.rs')
-rw-r--r--compiler/rustc_target/src/asm/arm.rs297
1 files changed, 297 insertions, 0 deletions
diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs
new file mode 100644
index 00000000000..4c323fc35d6
--- /dev/null
+++ b/compiler/rustc_target/src/asm/arm.rs
@@ -0,0 +1,297 @@
+use super::{InlineAsmArch, InlineAsmType};
+use crate::spec::Target;
+use rustc_macros::HashStable_Generic;
+use std::fmt;
+
+def_reg_class! {
+    Arm ArmInlineAsmRegClass {
+        reg,
+        reg_thumb,
+        sreg,
+        sreg_low16,
+        dreg,
+        dreg_low16,
+        dreg_low8,
+        qreg,
+        qreg_low8,
+        qreg_low4,
+    }
+}
+
+impl ArmInlineAsmRegClass {
+    pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] {
+        match self {
+            Self::qreg | Self::qreg_low8 | Self::qreg_low4 => &['e', 'f'],
+            _ => &[],
+        }
+    }
+
+    pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+        None
+    }
+
+    pub fn suggest_modifier(
+        self,
+        _arch: InlineAsmArch,
+        _ty: InlineAsmType,
+    ) -> Option<(char, &'static str)> {
+        None
+    }
+
+    pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+        None
+    }
+
+    pub fn supported_types(
+        self,
+        _arch: InlineAsmArch,
+    ) -> &'static [(InlineAsmType, Option<&'static str>)] {
+        match self {
+            Self::reg | Self::reg_thumb => types! { _: I8, I16, I32, F32; },
+            Self::sreg | Self::sreg_low16 => types! { "vfp2": I32, F32; },
+            Self::dreg | Self::dreg_low16 | Self::dreg_low8 => types! {
+                "vfp2": I64, F64, VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2);
+            },
+            Self::qreg | Self::qreg_low8 | Self::qreg_low4 => types! {
+                "neon": VecI8(16), VecI16(8), VecI32(4), VecI64(2), VecF32(4);
+            },
+        }
+    }
+}
+
+// This uses the same logic as useR7AsFramePointer in LLVM
+fn frame_pointer_is_r7(mut has_feature: impl FnMut(&str) -> bool, target: &Target) -> bool {
+    target.is_like_osx || (!target.is_like_windows && has_feature("thumb-mode"))
+}
+
+fn frame_pointer_r11(
+    _arch: InlineAsmArch,
+    has_feature: impl FnMut(&str) -> bool,
+    target: &Target,
+) -> Result<(), &'static str> {
+    if !frame_pointer_is_r7(has_feature, target) {
+        Err("the frame pointer (r11) cannot be used as an operand for inline asm")
+    } else {
+        Ok(())
+    }
+}
+
+fn frame_pointer_r7(
+    _arch: InlineAsmArch,
+    has_feature: impl FnMut(&str) -> bool,
+    target: &Target,
+) -> Result<(), &'static str> {
+    if frame_pointer_is_r7(has_feature, target) {
+        Err("the frame pointer (r7) cannot be used as an operand for inline asm")
+    } else {
+        Ok(())
+    }
+}
+
+def_regs! {
+    Arm ArmInlineAsmReg ArmInlineAsmRegClass {
+        r0: reg, reg_thumb = ["r0", "a1"],
+        r1: reg, reg_thumb = ["r1", "a2"],
+        r2: reg, reg_thumb = ["r2", "a3"],
+        r3: reg, reg_thumb = ["r3", "a4"],
+        r4: reg, reg_thumb = ["r4", "v1"],
+        r5: reg, reg_thumb = ["r5", "v2"],
+        r7: reg, reg_thumb = ["r7", "v4"] % frame_pointer_r7,
+        r8: reg = ["r8", "v5"],
+        r10: reg = ["r10", "sl"],
+        r11: reg = ["r11", "fp"] % frame_pointer_r11,
+        r12: reg = ["r12", "ip"],
+        r14: reg = ["r14", "lr"],
+        s0: sreg, sreg_low16 = ["s0"],
+        s1: sreg, sreg_low16 = ["s1"],
+        s2: sreg, sreg_low16 = ["s2"],
+        s3: sreg, sreg_low16 = ["s3"],
+        s4: sreg, sreg_low16 = ["s4"],
+        s5: sreg, sreg_low16 = ["s5"],
+        s6: sreg, sreg_low16 = ["s6"],
+        s7: sreg, sreg_low16 = ["s7"],
+        s8: sreg, sreg_low16 = ["s8"],
+        s9: sreg, sreg_low16 = ["s9"],
+        s10: sreg, sreg_low16 = ["s10"],
+        s11: sreg, sreg_low16 = ["s11"],
+        s12: sreg, sreg_low16 = ["s12"],
+        s13: sreg, sreg_low16 = ["s13"],
+        s14: sreg, sreg_low16 = ["s14"],
+        s15: sreg, sreg_low16 = ["s15"],
+        s16: sreg = ["s16"],
+        s17: sreg = ["s17"],
+        s18: sreg = ["s18"],
+        s19: sreg = ["s19"],
+        s20: sreg = ["s20"],
+        s21: sreg = ["s21"],
+        s22: sreg = ["s22"],
+        s23: sreg = ["s23"],
+        s24: sreg = ["s24"],
+        s25: sreg = ["s25"],
+        s26: sreg = ["s26"],
+        s27: sreg = ["s27"],
+        s28: sreg = ["s28"],
+        s29: sreg = ["s29"],
+        s30: sreg = ["s30"],
+        s31: sreg = ["s31"],
+        d0: dreg, dreg_low16, dreg_low8 = ["d0"],
+        d1: dreg, dreg_low16, dreg_low8 = ["d1"],
+        d2: dreg, dreg_low16, dreg_low8 = ["d2"],
+        d3: dreg, dreg_low16, dreg_low8 = ["d3"],
+        d4: dreg, dreg_low16, dreg_low8 = ["d4"],
+        d5: dreg, dreg_low16, dreg_low8 = ["d5"],
+        d6: dreg, dreg_low16, dreg_low8 = ["d6"],
+        d7: dreg, dreg_low16, dreg_low8 = ["d7"],
+        d8: dreg, dreg_low16 = ["d8"],
+        d9: dreg, dreg_low16 = ["d9"],
+        d10: dreg, dreg_low16 = ["d10"],
+        d11: dreg, dreg_low16 = ["d11"],
+        d12: dreg, dreg_low16 = ["d12"],
+        d13: dreg, dreg_low16 = ["d13"],
+        d14: dreg, dreg_low16 = ["d14"],
+        d15: dreg, dreg_low16 = ["d15"],
+        d16: dreg = ["d16"],
+        d17: dreg = ["d17"],
+        d18: dreg = ["d18"],
+        d19: dreg = ["d19"],
+        d20: dreg = ["d20"],
+        d21: dreg = ["d21"],
+        d22: dreg = ["d22"],
+        d23: dreg = ["d23"],
+        d24: dreg = ["d24"],
+        d25: dreg = ["d25"],
+        d26: dreg = ["d26"],
+        d27: dreg = ["d27"],
+        d28: dreg = ["d28"],
+        d29: dreg = ["d29"],
+        d30: dreg = ["d30"],
+        d31: dreg = ["d31"],
+        q0: qreg, qreg_low8, qreg_low4 = ["q0"],
+        q1: qreg, qreg_low8, qreg_low4 = ["q1"],
+        q2: qreg, qreg_low8, qreg_low4 = ["q2"],
+        q3: qreg, qreg_low8, qreg_low4 = ["q3"],
+        q4: qreg, qreg_low8 = ["q4"],
+        q5: qreg, qreg_low8 = ["q5"],
+        q6: qreg, qreg_low8 = ["q6"],
+        q7: qreg, qreg_low8 = ["q7"],
+        q8: qreg = ["q8"],
+        q9: qreg = ["q9"],
+        q10: qreg = ["q10"],
+        q11: qreg = ["q11"],
+        q12: qreg = ["q12"],
+        q13: qreg = ["q13"],
+        q14: qreg = ["q14"],
+        q15: qreg = ["q15"],
+        #error = ["r6", "v3"] =>
+            "r6 is used internally by LLVM and cannot be used as an operand for inline asm",
+        #error = ["r9", "v6", "rfp"] =>
+            "r9 is used internally by LLVM and cannot be used as an operand for inline asm",
+        #error = ["r13", "sp"] =>
+            "the stack pointer cannot be used as an operand for inline asm",
+        #error = ["r15", "pc"] =>
+            "the program pointer cannot be used as an operand for inline asm",
+    }
+}
+
+impl ArmInlineAsmReg {
+    pub fn emit(
+        self,
+        out: &mut dyn fmt::Write,
+        _arch: InlineAsmArch,
+        modifier: Option<char>,
+    ) -> fmt::Result {
+        // Only qreg is allowed to have modifiers. This should have been
+        // validated already by now.
+        if let Some(modifier) = modifier {
+            let index = self as u32 - Self::q0 as u32;
+            assert!(index < 16);
+            let index = index * 2 + (modifier == 'f') as u32;
+            write!(out, "d{}", index)
+        } else {
+            out.write_str(self.name())
+        }
+    }
+
+    pub fn overlapping_regs(self, mut cb: impl FnMut(ArmInlineAsmReg)) {
+        cb(self);
+
+        macro_rules! reg_conflicts {
+            (
+                $(
+                    $q:ident : $d0:ident $d1:ident : $s0:ident $s1:ident $s2:ident $s3:ident
+                ),*;
+                $(
+                    $q_high:ident : $d0_high:ident $d1_high:ident
+                ),*;
+            ) => {
+                match self {
+                    $(
+                        Self::$q => {
+                            cb(Self::$d0);
+                            cb(Self::$d1);
+                            cb(Self::$s0);
+                            cb(Self::$s1);
+                            cb(Self::$s2);
+                            cb(Self::$s3);
+                        }
+                        Self::$d0 => {
+                            cb(Self::$q);
+                            cb(Self::$s0);
+                            cb(Self::$s1);
+                        }
+                        Self::$d1 => {
+                            cb(Self::$q);
+                            cb(Self::$s2);
+                            cb(Self::$s3);
+                        }
+                        Self::$s0 | Self::$s1 => {
+                            cb(Self::$q);
+                            cb(Self::$d0);
+                        }
+                        Self::$s2 | Self::$s3 => {
+                            cb(Self::$q);
+                            cb(Self::$d1);
+                        }
+                    )*
+                    $(
+                        Self::$q_high => {
+                            cb(Self::$d0_high);
+                            cb(Self::$d1_high);
+                        }
+                        Self::$d0_high | Self::$d1_high => {
+                            cb(Self::$q_high);
+                        }
+                    )*
+                    _ => {},
+                }
+            };
+        }
+
+        // ARM's floating-point register file is interesting in that it can be
+        // viewed as 16 128-bit registers, 32 64-bit registers or 32 32-bit
+        // registers. Because these views overlap, the registers of different
+        // widths will conflict (e.g. d0 overlaps with s0 and s1, and q1
+        // overlaps with d2 and d3).
+        //
+        // See section E1.3.1 of the ARM Architecture Reference Manual for
+        // ARMv8-A for more details.
+        reg_conflicts! {
+            q0 : d0 d1 : s0 s1 s2 s3,
+            q1 : d2 d3 : s4 s5 s6 s7,
+            q2 : d4 d5 : s8 s9 s10 s11,
+            q3 : d6 d7 : s12 s13 s14 s15,
+            q4 : d8 d9 : s16 s17 s18 s19,
+            q5 : d10 d11 : s20 s21 s22 s23,
+            q6 : d12 d13 : s24 s25 s26 s27,
+            q7 : d14 d15 : s28 s29 s30 s31;
+            q8 : d16 d17,
+            q9 : d18 d19,
+            q10 : d20 d21,
+            q11 : d22 d23,
+            q12 : d24 d25,
+            q13 : d26 d27,
+            q14 : d28 d29,
+            q15 : d30 d31;
+        }
+    }
+}