diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /compiler/rustc_target/src/asm/arm.rs | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_target/src/asm/arm.rs')
-rw-r--r-- | compiler/rustc_target/src/asm/arm.rs | 340 |
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; + } + } +} |