summaryrefslogtreecommitdiffstats
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.rs340
1 files changed, 340 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 000000000..0db3eb6fc
--- /dev/null
+++ b/compiler/rustc_target/src/asm/arm.rs
@@ -0,0 +1,340 @@
+use super::{InlineAsmArch, InlineAsmType};
+use crate::spec::{RelocModel, Target};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_macros::HashStable_Generic;
+use rustc_span::{sym, Symbol};
+use std::fmt;
+
+def_reg_class! {
+ Arm ArmInlineAsmRegClass {
+ reg,
+ 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<Symbol>)] {
+ match self {
+ Self::reg => types! { _: I8, I16, I32, F32; },
+ Self::sreg | Self::sreg_low16 => types! { vfp2: I32, F32; },
+ Self::dreg_low16 | Self::dreg_low8 => types! {
+ vfp2: I64, F64, VecI8(8), VecI16(4), VecI32(2), VecI64(1), VecF32(2);
+ },
+ Self::dreg => types! {
+ d32: 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(target_features: &FxHashSet<Symbol>, target: &Target) -> bool {
+ target.is_like_osx || (!target.is_like_windows && target_features.contains(&sym::thumb_mode))
+}
+
+fn frame_pointer_r11(
+ arch: InlineAsmArch,
+ reloc_model: RelocModel,
+ target_features: &FxHashSet<Symbol>,
+ target: &Target,
+ is_clobber: bool,
+) -> Result<(), &'static str> {
+ not_thumb1(arch, reloc_model, target_features, target, is_clobber)?;
+
+ if !frame_pointer_is_r7(target_features, target) {
+ Err("the frame pointer (r11) cannot be used as an operand for inline asm")
+ } else {
+ Ok(())
+ }
+}
+
+fn frame_pointer_r7(
+ _arch: InlineAsmArch,
+ _reloc_model: RelocModel,
+ target_features: &FxHashSet<Symbol>,
+ target: &Target,
+ _is_clobber: bool,
+) -> Result<(), &'static str> {
+ if frame_pointer_is_r7(target_features, target) {
+ Err("the frame pointer (r7) cannot be used as an operand for inline asm")
+ } else {
+ Ok(())
+ }
+}
+
+fn not_thumb1(
+ _arch: InlineAsmArch,
+ _reloc_model: RelocModel,
+ target_features: &FxHashSet<Symbol>,
+ _target: &Target,
+ is_clobber: bool,
+) -> Result<(), &'static str> {
+ if !is_clobber
+ && target_features.contains(&sym::thumb_mode)
+ && !target_features.contains(&sym::thumb2)
+ {
+ Err("high registers (r8+) can only be used as clobbers in Thumb-1 code")
+ } else {
+ Ok(())
+ }
+}
+
+fn reserved_r9(
+ arch: InlineAsmArch,
+ reloc_model: RelocModel,
+ target_features: &FxHashSet<Symbol>,
+ target: &Target,
+ is_clobber: bool,
+) -> Result<(), &'static str> {
+ not_thumb1(arch, reloc_model, target_features, target, is_clobber)?;
+
+ match reloc_model {
+ RelocModel::Rwpi | RelocModel::RopiRwpi => {
+ Err("the RWPI static base register (r9) cannot be used as an operand for inline asm")
+ }
+ _ => Ok(()),
+ }
+}
+
+def_regs! {
+ Arm ArmInlineAsmReg ArmInlineAsmRegClass {
+ r0: reg = ["r0", "a1"],
+ r1: reg = ["r1", "a2"],
+ r2: reg = ["r2", "a3"],
+ r3: reg = ["r3", "a4"],
+ r4: reg = ["r4", "v1"],
+ r5: reg = ["r5", "v2"],
+ r7: reg = ["r7", "v4"] % frame_pointer_r7,
+ r8: reg = ["r8", "v5"] % not_thumb1,
+ r9: reg = ["r9", "v6", "rfp"] % reserved_r9,
+ r10: reg = ["r10", "sl"] % not_thumb1,
+ r11: reg = ["r11", "fp"] % frame_pointer_r11,
+ r12: reg = ["r12", "ip"] % not_thumb1,
+ r14: reg = ["r14", "lr"] % not_thumb1,
+ 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 = ["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;
+ }
+ }
+}