| use std::borrow::Cow; | |
| use std::fmt; | |
| use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; | |
| use rustc_macros::{Decodable, Encodable, StableHash}; | |
| use rustc_span::Symbol; | |
| use crate::spec::{Arch, RelocModel, Target}; | |
| pub struct ModifierInfo { | |
| pub modifier: char, | |
| pub result: &'static str, | |
| pub size: InlineAsmSize, | |
| } | |
| impl From<(char, &'static str, InlineAsmSize)> for ModifierInfo { | |
| fn from((modifier, result, size): (char, &'static str, InlineAsmSize)) -> Self { | |
| Self { modifier, result, size } | |
| } | |
| } | |
| macro_rules! def_reg_class { | |
| ($arch:ident $arch_regclass:ident { | |
| $( | |
| $class:ident, | |
| )* | |
| }) => { | |
| #[derive(Copy, Clone, rustc_macros::Encodable, rustc_macros::Decodable, Debug, Eq, PartialEq, PartialOrd, Hash, rustc_macros::StableHash)] | |
| #[allow(non_camel_case_types)] | |
| pub enum $arch_regclass { | |
| $($class,)* | |
| } | |
| impl $arch_regclass { | |
| pub fn name(self) -> rustc_span::Symbol { | |
| match self { | |
| $(Self::$class => rustc_span::sym::$class,)* | |
| } | |
| } | |
| pub fn parse(name: rustc_span::Symbol) -> Result<Self, &'static [rustc_span::Symbol]> { | |
| match name { | |
| $( | |
| rustc_span::sym::$class => Ok(Self::$class), | |
| )* | |
| _ => Err(&[$(rustc_span::sym::$class),*]), | |
| } | |
| } | |
| } | |
| pub(super) fn regclass_map() -> rustc_data_structures::fx::FxHashMap< | |
| super::InlineAsmRegClass, | |
| rustc_data_structures::fx::FxIndexSet<super::InlineAsmReg>, | |
| > { | |
| use rustc_data_structures::fx::FxHashMap; | |
| use rustc_data_structures::fx::FxIndexSet; | |
| use super::InlineAsmRegClass; | |
| let mut map = FxHashMap::default(); | |
| $( | |
| map.insert(InlineAsmRegClass::$arch($arch_regclass::$class), FxIndexSet::default()); | |
| )* | |
| map | |
| } | |
| } | |
| } | |
| macro_rules! def_regs { | |
| ($arch:ident $arch_reg:ident $arch_regclass:ident { | |
| $( | |
| $reg:ident: $class:ident $(, $extra_class:ident)* = [$reg_name:literal $(, $alias:literal)*] $(% $filter:ident)*, | |
| )* | |
| $( | |
| #error = [$($bad_reg:literal),+] => $error:literal, | |
| )* | |
| }) => { | |
| #[allow(unreachable_code)] | |
| #[derive(Copy, Clone, rustc_macros::Encodable, rustc_macros::Decodable, Debug, Eq, PartialEq, PartialOrd, Hash, rustc_macros::StableHash)] | |
| #[allow(non_camel_case_types)] | |
| pub enum $arch_reg { | |
| $($reg,)* | |
| } | |
| impl $arch_reg { | |
| pub fn name(self) -> &'static str { | |
| match self { | |
| $(Self::$reg => $reg_name,)* | |
| } | |
| } | |
| pub fn reg_class(self) -> $arch_regclass { | |
| match self { | |
| $(Self::$reg => $arch_regclass::$class,)* | |
| } | |
| } | |
| pub fn parse(name: &str) -> Result<Self, &'static str> { | |
| match name { | |
| $( | |
| $($alias)|* | $reg_name => Ok(Self::$reg), | |
| )* | |
| $( | |
| $($bad_reg)|* => Err($error), | |
| )* | |
| _ => Err("unknown register"), | |
| } | |
| } | |
| pub fn validate(self, | |
| _arch: super::InlineAsmArch, | |
| _reloc_model: crate::spec::RelocModel, | |
| _target_features: &rustc_data_structures::fx::FxIndexSet<Symbol>, | |
| _target: &crate::spec::Target, | |
| _is_clobber: bool, | |
| ) -> Result<(), &'static str> { | |
| match self { | |
| $( | |
| Self::$reg => { | |
| $($filter( | |
| _arch, | |
| _reloc_model, | |
| _target_features, | |
| _target, | |
| _is_clobber | |
| )?;)* | |
| Ok(()) | |
| } | |
| )* | |
| } | |
| } | |
| } | |
| pub(super) fn fill_reg_map( | |
| _arch: super::InlineAsmArch, | |
| _reloc_model: crate::spec::RelocModel, | |
| _target_features: &rustc_data_structures::fx::FxIndexSet<Symbol>, | |
| _target: &crate::spec::Target, | |
| _map: &mut rustc_data_structures::fx::FxHashMap< | |
| super::InlineAsmRegClass, | |
| rustc_data_structures::fx::FxIndexSet<super::InlineAsmReg>, | |
| >, | |
| ) { | |
| #[allow(unused_imports)] | |
| use super::{InlineAsmReg, InlineAsmRegClass}; | |
| $( | |
| if $($filter(_arch, _reloc_model, _target_features, _target, false).is_ok() &&)* true { | |
| if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) { | |
| set.insert(InlineAsmReg::$arch($arch_reg::$reg)); | |
| } | |
| $( | |
| if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$extra_class)) { | |
| set.insert(InlineAsmReg::$arch($arch_reg::$reg)); | |
| } | |
| )* | |
| } | |
| )* | |
| } | |
| } | |
| } | |
| macro_rules! types { | |
| ( | |
| $(_ : $($ty:expr),+;)? | |
| $($feature:ident: $($ty2:expr),+;)* | |
| ) => { | |
| { | |
| use super::InlineAsmType::*; | |
| &[ | |
| $($( | |
| ($ty, None), | |
| )*)? | |
| $($( | |
| ($ty2, Some(rustc_span::sym::$feature)), | |
| )*)* | |
| ] | |
| } | |
| }; | |
| } | |
| mod aarch64; | |
| mod amdgpu; | |
| mod arm; | |
| mod avr; | |
| mod bpf; | |
| mod csky; | |
| mod hexagon; | |
| mod loongarch; | |
| mod m68k; | |
| mod mips; | |
| mod msp430; | |
| mod nvptx; | |
| mod powerpc; | |
| mod riscv; | |
| mod s390x; | |
| mod sparc; | |
| mod spirv; | |
| mod wasm; | |
| mod x86; | |
| mod xtensa; | |
| pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass}; | |
| pub use amdgpu::{AmdgpuInlineAsmReg, AmdgpuInlineAsmRegClass}; | |
| pub use arm::{ArmInlineAsmReg, ArmInlineAsmRegClass}; | |
| pub use avr::{AvrInlineAsmReg, AvrInlineAsmRegClass}; | |
| pub use bpf::{BpfInlineAsmReg, BpfInlineAsmRegClass}; | |
| pub use csky::{CSKYInlineAsmReg, CSKYInlineAsmRegClass}; | |
| pub use hexagon::{HexagonInlineAsmReg, HexagonInlineAsmRegClass}; | |
| pub use loongarch::{LoongArchInlineAsmReg, LoongArchInlineAsmRegClass}; | |
| pub use m68k::{M68kInlineAsmReg, M68kInlineAsmRegClass}; | |
| pub use mips::{MipsInlineAsmReg, MipsInlineAsmRegClass}; | |
| pub use msp430::{Msp430InlineAsmReg, Msp430InlineAsmRegClass}; | |
| pub use nvptx::{NvptxInlineAsmReg, NvptxInlineAsmRegClass}; | |
| pub use powerpc::{PowerPCInlineAsmReg, PowerPCInlineAsmRegClass}; | |
| pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass}; | |
| pub use s390x::{S390xInlineAsmReg, S390xInlineAsmRegClass}; | |
| pub use sparc::{SparcInlineAsmReg, SparcInlineAsmRegClass}; | |
| pub use spirv::{SpirVInlineAsmReg, SpirVInlineAsmRegClass}; | |
| pub use wasm::{WasmInlineAsmReg, WasmInlineAsmRegClass}; | |
| pub use x86::{X86InlineAsmReg, X86InlineAsmRegClass}; | |
| pub use xtensa::{XtensaInlineAsmReg, XtensaInlineAsmRegClass}; | |
| #[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash)] | |
| pub enum InlineAsmArch { | |
| X86, | |
| X86_64, | |
| Arm, | |
| AArch64, | |
| Arm64EC, | |
| RiscV32, | |
| RiscV64, | |
| Nvptx64, | |
| Amdgpu, | |
| Hexagon, | |
| LoongArch32, | |
| LoongArch64, | |
| Mips, | |
| Mips64, | |
| PowerPC, | |
| PowerPC64, | |
| S390x, | |
| Sparc, | |
| Sparc64, | |
| SpirV, | |
| Wasm32, | |
| Wasm64, | |
| Xtensa, | |
| Bpf, | |
| Avr, | |
| Msp430, | |
| M68k, | |
| CSKY, | |
| } | |
| impl InlineAsmArch { | |
| pub fn from_arch(arch: &Arch) -> Option<Self> { | |
| match arch { | |
| Arch::X86 => Some(Self::X86), | |
| Arch::X86_64 => Some(Self::X86_64), | |
| Arch::Arm => Some(Self::Arm), | |
| Arch::Arm64EC => Some(Self::Arm64EC), | |
| Arch::AArch64 => Some(Self::AArch64), | |
| Arch::AmdGpu => Some(Self::Amdgpu), | |
| Arch::RiscV32 => Some(Self::RiscV32), | |
| Arch::RiscV64 => Some(Self::RiscV64), | |
| Arch::Nvptx64 => Some(Self::Nvptx64), | |
| Arch::Hexagon => Some(Self::Hexagon), | |
| Arch::LoongArch32 => Some(Self::LoongArch32), | |
| Arch::LoongArch64 => Some(Self::LoongArch64), | |
| Arch::Mips | Arch::Mips32r6 => Some(Self::Mips), | |
| Arch::Mips64 | Arch::Mips64r6 => Some(Self::Mips64), | |
| Arch::PowerPC => Some(Self::PowerPC), | |
| Arch::PowerPC64 => Some(Self::PowerPC64), | |
| Arch::S390x => Some(Self::S390x), | |
| Arch::Sparc => Some(Self::Sparc), | |
| Arch::Sparc64 => Some(Self::Sparc64), | |
| Arch::SpirV => Some(Self::SpirV), | |
| Arch::Wasm32 => Some(Self::Wasm32), | |
| Arch::Wasm64 => Some(Self::Wasm64), | |
| Arch::Bpf => Some(Self::Bpf), | |
| Arch::Avr => Some(Self::Avr), | |
| Arch::Msp430 => Some(Self::Msp430), | |
| Arch::M68k => Some(Self::M68k), | |
| Arch::CSky => Some(Self::CSKY), | |
| Arch::Xtensa => Some(Self::Xtensa), | |
| Arch::Other(_) => None, | |
| } | |
| } | |
| } | |
| #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] | |
| #[derive(StableHash, Encodable, Decodable)] | |
| pub enum InlineAsmReg { | |
| X86(X86InlineAsmReg), | |
| Arm(ArmInlineAsmReg), | |
| Amdgpu(AmdgpuInlineAsmReg), | |
| AArch64(AArch64InlineAsmReg), | |
| RiscV(RiscVInlineAsmReg), | |
| Nvptx(NvptxInlineAsmReg), | |
| PowerPC(PowerPCInlineAsmReg), | |
| Hexagon(HexagonInlineAsmReg), | |
| LoongArch(LoongArchInlineAsmReg), | |
| Mips(MipsInlineAsmReg), | |
| S390x(S390xInlineAsmReg), | |
| Sparc(SparcInlineAsmReg), | |
| SpirV(SpirVInlineAsmReg), | |
| Wasm(WasmInlineAsmReg), | |
| Xtensa(XtensaInlineAsmReg), | |
| Bpf(BpfInlineAsmReg), | |
| Avr(AvrInlineAsmReg), | |
| Msp430(Msp430InlineAsmReg), | |
| M68k(M68kInlineAsmReg), | |
| CSKY(CSKYInlineAsmReg), | |
| // Placeholder for invalid register constraints for the current target | |
| Err, | |
| } | |
| impl InlineAsmReg { | |
| pub fn name(self) -> Cow<'static, str> { | |
| match self { | |
| Self::X86(r) => r.name().into(), | |
| Self::Arm(r) => r.name().into(), | |
| Self::AArch64(r) => r.name().into(), | |
| Self::Amdgpu(r) => r.name().into(), | |
| Self::RiscV(r) => r.name().into(), | |
| Self::PowerPC(r) => r.name().into(), | |
| Self::Hexagon(r) => r.name().into(), | |
| Self::LoongArch(r) => r.name().into(), | |
| Self::Mips(r) => r.name().into(), | |
| Self::S390x(r) => r.name().into(), | |
| Self::Sparc(r) => r.name().into(), | |
| Self::Xtensa(r) => r.name().into(), | |
| Self::Bpf(r) => r.name().into(), | |
| Self::Avr(r) => r.name().into(), | |
| Self::Msp430(r) => r.name().into(), | |
| Self::M68k(r) => r.name().into(), | |
| Self::CSKY(r) => r.name().into(), | |
| Self::Err => "<reg>".into(), | |
| } | |
| } | |
| pub fn reg_class(self) -> InlineAsmRegClass { | |
| match self { | |
| Self::X86(r) => InlineAsmRegClass::X86(r.reg_class()), | |
| Self::Arm(r) => InlineAsmRegClass::Arm(r.reg_class()), | |
| Self::AArch64(r) => InlineAsmRegClass::AArch64(r.reg_class()), | |
| Self::Amdgpu(r) => InlineAsmRegClass::Amdgpu(r.reg_class()), | |
| Self::RiscV(r) => InlineAsmRegClass::RiscV(r.reg_class()), | |
| Self::PowerPC(r) => InlineAsmRegClass::PowerPC(r.reg_class()), | |
| Self::Hexagon(r) => InlineAsmRegClass::Hexagon(r.reg_class()), | |
| Self::LoongArch(r) => InlineAsmRegClass::LoongArch(r.reg_class()), | |
| Self::Mips(r) => InlineAsmRegClass::Mips(r.reg_class()), | |
| Self::S390x(r) => InlineAsmRegClass::S390x(r.reg_class()), | |
| Self::Sparc(r) => InlineAsmRegClass::Sparc(r.reg_class()), | |
| Self::Xtensa(r) => InlineAsmRegClass::Xtensa(r.reg_class()), | |
| Self::Bpf(r) => InlineAsmRegClass::Bpf(r.reg_class()), | |
| Self::Avr(r) => InlineAsmRegClass::Avr(r.reg_class()), | |
| Self::Msp430(r) => InlineAsmRegClass::Msp430(r.reg_class()), | |
| Self::M68k(r) => InlineAsmRegClass::M68k(r.reg_class()), | |
| Self::CSKY(r) => InlineAsmRegClass::CSKY(r.reg_class()), | |
| Self::Err => InlineAsmRegClass::Err, | |
| } | |
| } | |
| pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result<Self, &'static str> { | |
| // FIXME: use direct symbol comparison for register names | |
| // Use `Symbol::as_str` instead of `Symbol::with` here because `has_feature` may access `Symbol`. | |
| let name = name.as_str(); | |
| Ok(match arch { | |
| InlineAsmArch::X86 | InlineAsmArch::X86_64 => Self::X86(X86InlineAsmReg::parse(name)?), | |
| InlineAsmArch::Arm => Self::Arm(ArmInlineAsmReg::parse(name)?), | |
| InlineAsmArch::AArch64 | InlineAsmArch::Arm64EC => { | |
| Self::AArch64(AArch64InlineAsmReg::parse(name)?) | |
| } | |
| InlineAsmArch::Amdgpu => Self::Amdgpu(AmdgpuInlineAsmReg::parse(name)?), | |
| InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { | |
| Self::RiscV(RiscVInlineAsmReg::parse(name)?) | |
| } | |
| InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmReg::parse(name)?), | |
| InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => { | |
| Self::PowerPC(PowerPCInlineAsmReg::parse(name)?) | |
| } | |
| InlineAsmArch::Hexagon => Self::Hexagon(HexagonInlineAsmReg::parse(name)?), | |
| InlineAsmArch::LoongArch32 | InlineAsmArch::LoongArch64 => { | |
| Self::LoongArch(LoongArchInlineAsmReg::parse(name)?) | |
| } | |
| InlineAsmArch::Mips | InlineAsmArch::Mips64 => { | |
| Self::Mips(MipsInlineAsmReg::parse(name)?) | |
| } | |
| InlineAsmArch::Xtensa => Self::Xtensa(XtensaInlineAsmReg::parse(name)?), | |
| InlineAsmArch::S390x => Self::S390x(S390xInlineAsmReg::parse(name)?), | |
| InlineAsmArch::Sparc | InlineAsmArch::Sparc64 => { | |
| Self::Sparc(SparcInlineAsmReg::parse(name)?) | |
| } | |
| InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmReg::parse(name)?), | |
| InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => { | |
| Self::Wasm(WasmInlineAsmReg::parse(name)?) | |
| } | |
| InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmReg::parse(name)?), | |
| InlineAsmArch::Avr => Self::Avr(AvrInlineAsmReg::parse(name)?), | |
| InlineAsmArch::Msp430 => Self::Msp430(Msp430InlineAsmReg::parse(name)?), | |
| InlineAsmArch::M68k => Self::M68k(M68kInlineAsmReg::parse(name)?), | |
| InlineAsmArch::CSKY => Self::CSKY(CSKYInlineAsmReg::parse(name)?), | |
| }) | |
| } | |
| pub fn validate( | |
| self, | |
| arch: InlineAsmArch, | |
| reloc_model: RelocModel, | |
| target_features: &FxIndexSet<Symbol>, | |
| target: &Target, | |
| is_clobber: bool, | |
| ) -> Result<(), &'static str> { | |
| match self { | |
| Self::X86(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), | |
| Self::Arm(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), | |
| Self::AArch64(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), | |
| Self::Amdgpu(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), | |
| Self::RiscV(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), | |
| Self::PowerPC(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), | |
| Self::Hexagon(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), | |
| Self::LoongArch(r) => { | |
| r.validate(arch, reloc_model, target_features, target, is_clobber) | |
| } | |
| Self::Mips(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), | |
| Self::S390x(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), | |
| Self::Sparc(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), | |
| Self::Bpf(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), | |
| Self::Avr(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), | |
| Self::Xtensa(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), | |
| Self::Msp430(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), | |
| Self::M68k(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), | |
| Self::CSKY(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), | |
| Self::Err => unreachable!(), | |
| } | |
| } | |
| // NOTE: This function isn't used at the moment, but is needed to support | |
| // falling back to an external assembler. | |
| pub fn emit( | |
| self, | |
| out: &mut dyn fmt::Write, | |
| arch: InlineAsmArch, | |
| modifier: Option<char>, | |
| ) -> fmt::Result { | |
| match self { | |
| Self::X86(r) => r.emit(out, arch, modifier), | |
| Self::Arm(r) => r.emit(out, arch, modifier), | |
| Self::AArch64(r) => r.emit(out, arch, modifier), | |
| Self::Amdgpu(r) => r.emit(out, arch, modifier), | |
| Self::RiscV(r) => r.emit(out, arch, modifier), | |
| Self::PowerPC(r) => r.emit(out, arch, modifier), | |
| Self::Hexagon(r) => r.emit(out, arch, modifier), | |
| Self::LoongArch(r) => r.emit(out, arch, modifier), | |
| Self::Mips(r) => r.emit(out, arch, modifier), | |
| Self::S390x(r) => r.emit(out, arch, modifier), | |
| Self::Sparc(r) => r.emit(out, arch, modifier), | |
| Self::Xtensa(r) => r.emit(out, arch, modifier), | |
| Self::Bpf(r) => r.emit(out, arch, modifier), | |
| Self::Avr(r) => r.emit(out, arch, modifier), | |
| Self::Msp430(r) => r.emit(out, arch, modifier), | |
| Self::M68k(r) => r.emit(out, arch, modifier), | |
| Self::CSKY(r) => r.emit(out, arch, modifier), | |
| Self::Err => unreachable!("Use of InlineAsmReg::Err"), | |
| } | |
| } | |
| pub fn overlapping_regs(self, mut cb: impl FnMut(InlineAsmReg)) { | |
| match self { | |
| Self::X86(r) => r.overlapping_regs(|r| cb(Self::X86(r))), | |
| Self::Arm(r) => r.overlapping_regs(|r| cb(Self::Arm(r))), | |
| Self::AArch64(_) => cb(self), | |
| Self::Amdgpu(r) => r.overlapping_regs(|r| cb(Self::Amdgpu(r))), | |
| Self::RiscV(_) => cb(self), | |
| Self::PowerPC(r) => r.overlapping_regs(|r| cb(Self::PowerPC(r))), | |
| Self::Hexagon(r) => r.overlapping_regs(|r| cb(Self::Hexagon(r))), | |
| Self::LoongArch(r) => r.overlapping_regs(|r| cb(Self::LoongArch(r))), | |
| Self::Mips(r) => r.overlapping_regs(|r| cb(Self::Mips(r))), | |
| Self::S390x(r) => r.overlapping_regs(|r| cb(Self::S390x(r))), | |
| Self::Sparc(r) => r.overlapping_regs(|r| cb(Self::Sparc(r))), | |
| Self::Xtensa(_) => cb(self), | |
| Self::Bpf(r) => r.overlapping_regs(|r| cb(Self::Bpf(r))), | |
| Self::Avr(r) => r.overlapping_regs(|r| cb(Self::Avr(r))), | |
| Self::Msp430(_) => cb(self), | |
| Self::M68k(_) => cb(self), | |
| Self::CSKY(_) => cb(self), | |
| Self::Err => unreachable!("Use of InlineAsmReg::Err"), | |
| } | |
| } | |
| } | |
| #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] | |
| #[derive(StableHash, Encodable, Decodable)] | |
| pub enum InlineAsmRegClass { | |
| X86(X86InlineAsmRegClass), | |
| Arm(ArmInlineAsmRegClass), | |
| AArch64(AArch64InlineAsmRegClass), | |
| Amdgpu(AmdgpuInlineAsmRegClass), | |
| RiscV(RiscVInlineAsmRegClass), | |
| Nvptx(NvptxInlineAsmRegClass), | |
| PowerPC(PowerPCInlineAsmRegClass), | |
| Hexagon(HexagonInlineAsmRegClass), | |
| LoongArch(LoongArchInlineAsmRegClass), | |
| Mips(MipsInlineAsmRegClass), | |
| S390x(S390xInlineAsmRegClass), | |
| Sparc(SparcInlineAsmRegClass), | |
| SpirV(SpirVInlineAsmRegClass), | |
| Wasm(WasmInlineAsmRegClass), | |
| Xtensa(XtensaInlineAsmRegClass), | |
| Bpf(BpfInlineAsmRegClass), | |
| Avr(AvrInlineAsmRegClass), | |
| Msp430(Msp430InlineAsmRegClass), | |
| M68k(M68kInlineAsmRegClass), | |
| CSKY(CSKYInlineAsmRegClass), | |
| // Placeholder for invalid register constraints for the current target | |
| Err, | |
| } | |
| impl InlineAsmRegClass { | |
| pub fn name(self) -> Symbol { | |
| match self { | |
| Self::X86(r) => r.name(), | |
| Self::Arm(r) => r.name(), | |
| Self::AArch64(r) => r.name(), | |
| Self::Amdgpu(r) => r.name(), | |
| Self::RiscV(r) => r.name(), | |
| Self::Nvptx(r) => r.name(), | |
| Self::PowerPC(r) => r.name(), | |
| Self::Hexagon(r) => r.name(), | |
| Self::LoongArch(r) => r.name(), | |
| Self::Mips(r) => r.name(), | |
| Self::S390x(r) => r.name(), | |
| Self::Sparc(r) => r.name(), | |
| Self::SpirV(r) => r.name(), | |
| Self::Wasm(r) => r.name(), | |
| Self::Xtensa(r) => r.name(), | |
| Self::Bpf(r) => r.name(), | |
| Self::Avr(r) => r.name(), | |
| Self::Msp430(r) => r.name(), | |
| Self::M68k(r) => r.name(), | |
| Self::CSKY(r) => r.name(), | |
| Self::Err => rustc_span::sym::reg, | |
| } | |
| } | |
| /// Returns a suggested register class to use for this type. This is called | |
| /// when `supported_types` fails to give a better error | |
| /// message to the user. | |
| pub fn suggest_class(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option<Self> { | |
| match self { | |
| Self::X86(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::X86), | |
| Self::Arm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Arm), | |
| Self::AArch64(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::AArch64), | |
| Self::Amdgpu(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Amdgpu), | |
| Self::RiscV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::RiscV), | |
| Self::Nvptx(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Nvptx), | |
| Self::PowerPC(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::PowerPC), | |
| Self::Hexagon(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Hexagon), | |
| Self::LoongArch(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::LoongArch), | |
| Self::Mips(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Mips), | |
| Self::S390x(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::S390x), | |
| Self::Sparc(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Sparc), | |
| Self::SpirV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::SpirV), | |
| Self::Wasm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Wasm), | |
| Self::Xtensa(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Xtensa), | |
| Self::Bpf(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Bpf), | |
| Self::Avr(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Avr), | |
| Self::Msp430(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Msp430), | |
| Self::M68k(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::M68k), | |
| Self::CSKY(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::CSKY), | |
| Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), | |
| } | |
| } | |
| /// Returns a suggested template modifier to use for this type and an | |
| /// example of a register named formatted with it. | |
| /// | |
| /// Such suggestions are useful if a type smaller than the full register | |
| /// size is used and a modifier can be used to point to the subregister of | |
| /// the correct size. | |
| pub fn suggest_modifier(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option<ModifierInfo> { | |
| match self { | |
| Self::X86(r) => r.suggest_modifier(arch, ty), | |
| Self::Arm(r) => r.suggest_modifier(arch, ty), | |
| Self::AArch64(r) => r.suggest_modifier(arch, ty), | |
| Self::Amdgpu(r) => r.suggest_modifier(arch, ty), | |
| Self::RiscV(r) => r.suggest_modifier(arch, ty), | |
| Self::Nvptx(r) => r.suggest_modifier(arch, ty), | |
| Self::PowerPC(r) => r.suggest_modifier(arch, ty), | |
| Self::Hexagon(r) => r.suggest_modifier(arch, ty), | |
| Self::LoongArch(r) => r.suggest_modifier(arch, ty), | |
| Self::Mips(r) => r.suggest_modifier(arch, ty), | |
| Self::S390x(r) => r.suggest_modifier(arch, ty), | |
| Self::Sparc(r) => r.suggest_modifier(arch, ty), | |
| Self::SpirV(r) => r.suggest_modifier(arch, ty), | |
| Self::Wasm(r) => r.suggest_modifier(arch, ty), | |
| Self::Xtensa(r) => r.suggest_modifier(arch, ty), | |
| Self::Bpf(r) => r.suggest_modifier(arch, ty), | |
| Self::Avr(r) => r.suggest_modifier(arch, ty), | |
| Self::Msp430(r) => r.suggest_modifier(arch, ty), | |
| Self::M68k(r) => r.suggest_modifier(arch, ty), | |
| Self::CSKY(r) => r.suggest_modifier(arch, ty), | |
| Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), | |
| } | |
| } | |
| /// Returns the default modifier for this register and an example of a | |
| /// register named formatted with it. | |
| /// | |
| /// This is only needed when the register class can suggest a modifier, so | |
| /// that the user can be shown how to get the default behavior without a | |
| /// warning. | |
| pub fn default_modifier(self, arch: InlineAsmArch) -> Option<ModifierInfo> { | |
| match self { | |
| Self::X86(r) => r.default_modifier(arch), | |
| Self::Arm(r) => r.default_modifier(arch), | |
| Self::AArch64(r) => r.default_modifier(arch), | |
| Self::Amdgpu(r) => r.default_modifier(arch), | |
| Self::RiscV(r) => r.default_modifier(arch), | |
| Self::Nvptx(r) => r.default_modifier(arch), | |
| Self::PowerPC(r) => r.default_modifier(arch), | |
| Self::Hexagon(r) => r.default_modifier(arch), | |
| Self::LoongArch(r) => r.default_modifier(arch), | |
| Self::Mips(r) => r.default_modifier(arch), | |
| Self::S390x(r) => r.default_modifier(arch), | |
| Self::Sparc(r) => r.default_modifier(arch), | |
| Self::SpirV(r) => r.default_modifier(arch), | |
| Self::Wasm(r) => r.default_modifier(arch), | |
| Self::Xtensa(r) => r.default_modifier(arch), | |
| Self::Bpf(r) => r.default_modifier(arch), | |
| Self::Avr(r) => r.default_modifier(arch), | |
| Self::Msp430(r) => r.default_modifier(arch), | |
| Self::M68k(r) => r.default_modifier(arch), | |
| Self::CSKY(r) => r.default_modifier(arch), | |
| Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), | |
| } | |
| } | |
| /// Returns a list of supported types for this register class, each with an | |
| /// options target feature required to use this type. | |
| /// | |
| /// At the codegen stage, it is fine to always pass true for `allow_experimental_reg`, | |
| /// since all the stability checking will have been done in prior stages. | |
| pub fn supported_types( | |
| self, | |
| arch: InlineAsmArch, | |
| allow_experimental_reg: bool, | |
| ) -> Cow<'static, [(InlineAsmType, Option<Symbol>)]> { | |
| match self { | |
| Self::X86(r) => r.supported_types(arch, allow_experimental_reg).into(), | |
| Self::Arm(r) => r.supported_types(arch).into(), | |
| Self::AArch64(r) => r.supported_types(arch, allow_experimental_reg).into(), | |
| Self::Amdgpu(r) => r.supported_types(arch).into(), | |
| Self::RiscV(r) => r.supported_types(arch).into(), | |
| Self::Nvptx(r) => r.supported_types(arch).into(), | |
| Self::PowerPC(r) => r.supported_types(arch).into(), | |
| Self::Hexagon(r) => r.supported_types(arch).into(), | |
| Self::LoongArch(r) => r.supported_types(arch, allow_experimental_reg).into(), | |
| Self::Mips(r) => r.supported_types(arch).into(), | |
| Self::S390x(r) => r.supported_types(arch).into(), | |
| Self::Sparc(r) => r.supported_types(arch).into(), | |
| Self::SpirV(r) => r.supported_types(arch).into(), | |
| Self::Wasm(r) => r.supported_types(arch).into(), | |
| Self::Xtensa(r) => r.supported_types(arch).into(), | |
| Self::Bpf(r) => r.supported_types(arch).into(), | |
| Self::Avr(r) => r.supported_types(arch).into(), | |
| Self::Msp430(r) => r.supported_types(arch).into(), | |
| Self::M68k(r) => r.supported_types(arch).into(), | |
| Self::CSKY(r) => r.supported_types(arch).into(), | |
| Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), | |
| } | |
| } | |
| pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result<Self, &'static [rustc_span::Symbol]> { | |
| Ok(match arch { | |
| InlineAsmArch::X86 | InlineAsmArch::X86_64 => { | |
| Self::X86(X86InlineAsmRegClass::parse(name)?) | |
| } | |
| InlineAsmArch::Arm => Self::Arm(ArmInlineAsmRegClass::parse(name)?), | |
| InlineAsmArch::AArch64 | InlineAsmArch::Arm64EC => { | |
| Self::AArch64(AArch64InlineAsmRegClass::parse(name)?) | |
| } | |
| InlineAsmArch::Amdgpu => Self::Amdgpu(AmdgpuInlineAsmRegClass::parse(name)?), | |
| InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { | |
| Self::RiscV(RiscVInlineAsmRegClass::parse(name)?) | |
| } | |
| InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmRegClass::parse(name)?), | |
| InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => { | |
| Self::PowerPC(PowerPCInlineAsmRegClass::parse(name)?) | |
| } | |
| InlineAsmArch::Hexagon => Self::Hexagon(HexagonInlineAsmRegClass::parse(name)?), | |
| InlineAsmArch::LoongArch32 | InlineAsmArch::LoongArch64 => { | |
| Self::LoongArch(LoongArchInlineAsmRegClass::parse(name)?) | |
| } | |
| InlineAsmArch::Mips | InlineAsmArch::Mips64 => { | |
| Self::Mips(MipsInlineAsmRegClass::parse(name)?) | |
| } | |
| InlineAsmArch::S390x => Self::S390x(S390xInlineAsmRegClass::parse(name)?), | |
| InlineAsmArch::Sparc | InlineAsmArch::Sparc64 => { | |
| Self::Sparc(SparcInlineAsmRegClass::parse(name)?) | |
| } | |
| InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmRegClass::parse(name)?), | |
| InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => { | |
| Self::Wasm(WasmInlineAsmRegClass::parse(name)?) | |
| } | |
| InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmRegClass::parse(name)?), | |
| InlineAsmArch::Avr => Self::Avr(AvrInlineAsmRegClass::parse(name)?), | |
| InlineAsmArch::Xtensa => Self::Xtensa(XtensaInlineAsmRegClass::parse(name)?), | |
| InlineAsmArch::Msp430 => Self::Msp430(Msp430InlineAsmRegClass::parse(name)?), | |
| InlineAsmArch::M68k => Self::M68k(M68kInlineAsmRegClass::parse(name)?), | |
| InlineAsmArch::CSKY => Self::CSKY(CSKYInlineAsmRegClass::parse(name)?), | |
| }) | |
| } | |
| /// Returns the list of template modifiers that can be used with this | |
| /// register class. | |
| pub fn valid_modifiers(self, arch: InlineAsmArch) -> &'static [char] { | |
| match self { | |
| Self::X86(r) => r.valid_modifiers(arch), | |
| Self::Arm(r) => r.valid_modifiers(arch), | |
| Self::AArch64(r) => r.valid_modifiers(arch), | |
| Self::Amdgpu(r) => r.valid_modifiers(arch), | |
| Self::RiscV(r) => r.valid_modifiers(arch), | |
| Self::Nvptx(r) => r.valid_modifiers(arch), | |
| Self::PowerPC(r) => r.valid_modifiers(arch), | |
| Self::Hexagon(r) => r.valid_modifiers(arch), | |
| Self::LoongArch(r) => r.valid_modifiers(arch), | |
| Self::Mips(r) => r.valid_modifiers(arch), | |
| Self::S390x(r) => r.valid_modifiers(arch), | |
| Self::Sparc(r) => r.valid_modifiers(arch), | |
| Self::SpirV(r) => r.valid_modifiers(arch), | |
| Self::Wasm(r) => r.valid_modifiers(arch), | |
| Self::Xtensa(r) => r.valid_modifiers(arch), | |
| Self::Bpf(r) => r.valid_modifiers(arch), | |
| Self::Avr(r) => r.valid_modifiers(arch), | |
| Self::Msp430(r) => r.valid_modifiers(arch), | |
| Self::M68k(r) => r.valid_modifiers(arch), | |
| Self::CSKY(r) => r.valid_modifiers(arch), | |
| Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), | |
| } | |
| } | |
| /// Returns whether registers in this class can only be used as clobbers | |
| /// and not as inputs/outputs. | |
| /// | |
| /// At the codegen stage, it is fine to always pass true for `allow_experimental_reg`, | |
| /// since all the stability checking will have been done in prior stages. | |
| pub fn is_clobber_only(self, arch: InlineAsmArch, allow_experimental_reg: bool) -> bool { | |
| self.supported_types(arch, allow_experimental_reg).is_empty() | |
| } | |
| } | |
| #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] | |
| #[derive(StableHash, Encodable, Decodable)] | |
| pub enum InlineAsmRegOrRegClass { | |
| Reg(InlineAsmReg), | |
| RegClass(InlineAsmRegClass), | |
| } | |
| impl InlineAsmRegOrRegClass { | |
| pub fn reg_class(self) -> InlineAsmRegClass { | |
| match self { | |
| Self::Reg(r) => r.reg_class(), | |
| Self::RegClass(r) => r, | |
| } | |
| } | |
| } | |
| impl fmt::Display for InlineAsmRegOrRegClass { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| match self { | |
| Self::Reg(r) => write!(f, "\"{}\"", r.name()), | |
| Self::RegClass(r) => write!(f, "{}", r.name()), | |
| } | |
| } | |
| } | |
| /// Set of types which can be used with a particular register class. | |
| #[derive(Copy, Clone, Debug, Eq, PartialEq)] | |
| pub enum InlineAsmType { | |
| I8, | |
| I16, | |
| I32, | |
| I64, | |
| I128, | |
| F16, | |
| F32, | |
| F64, | |
| F128, | |
| VecI8(u64), | |
| VecI16(u64), | |
| VecI32(u64), | |
| VecI64(u64), | |
| VecI128(u64), | |
| VecF16(u64), | |
| VecF32(u64), | |
| VecF64(u64), | |
| VecF128(u64), | |
| SveVecI8, | |
| SveVecI16, | |
| SveVecI32, | |
| SveVecI64, | |
| SveVecI128, | |
| SveVecF16, | |
| SveVecF32, | |
| SveVecF64, | |
| SveVecF128, | |
| SveVecBool, | |
| } | |
| #[derive(Copy, Clone, Debug, Eq, PartialEq)] | |
| pub enum InlineAsmSize { | |
| FixedBytes(u64), | |
| Scalable, | |
| } | |
| impl InlineAsmSize { | |
| pub fn fixed_size_bytes(self) -> Option<u64> { | |
| match self { | |
| Self::FixedBytes(size) => Some(size), | |
| Self::Scalable => None, | |
| } | |
| } | |
| } | |
| impl InlineAsmType { | |
| pub fn is_integer(self) -> bool { | |
| matches!(self, Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128) | |
| } | |
| pub fn size(self) -> InlineAsmSize { | |
| match self { | |
| Self::I8 => InlineAsmSize::FixedBytes(1), | |
| Self::I16 | Self::F16 => InlineAsmSize::FixedBytes(2), | |
| Self::I32 | Self::F32 => InlineAsmSize::FixedBytes(4), | |
| Self::I64 | Self::F64 => InlineAsmSize::FixedBytes(8), | |
| Self::I128 | Self::F128 => InlineAsmSize::FixedBytes(16), | |
| Self::VecI8(n) => InlineAsmSize::FixedBytes(n), | |
| Self::VecI16(n) | Self::VecF16(n) => InlineAsmSize::FixedBytes(n * 2), | |
| Self::VecI32(n) | Self::VecF32(n) => InlineAsmSize::FixedBytes(n * 4), | |
| Self::VecI64(n) | Self::VecF64(n) => InlineAsmSize::FixedBytes(n * 8), | |
| Self::VecI128(n) | Self::VecF128(n) => InlineAsmSize::FixedBytes(n * 16), | |
| Self::SveVecI8 | |
| | Self::SveVecI16 | |
| | Self::SveVecI32 | |
| | Self::SveVecI64 | |
| | Self::SveVecI128 | |
| | Self::SveVecF16 | |
| | Self::SveVecF32 | |
| | Self::SveVecF64 | |
| | Self::SveVecF128 | |
| | Self::SveVecBool => InlineAsmSize::Scalable, | |
| } | |
| } | |
| } | |
| impl fmt::Display for InlineAsmType { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| match *self { | |
| Self::I8 => f.write_str("i8"), | |
| Self::I16 => f.write_str("i16"), | |
| Self::I32 => f.write_str("i32"), | |
| Self::I64 => f.write_str("i64"), | |
| Self::I128 => f.write_str("i128"), | |
| Self::F16 => f.write_str("f16"), | |
| Self::F32 => f.write_str("f32"), | |
| Self::F64 => f.write_str("f64"), | |
| Self::F128 => f.write_str("f128"), | |
| Self::VecI8(n) => write!(f, "i8x{n}"), | |
| Self::VecI16(n) => write!(f, "i16x{n}"), | |
| Self::VecI32(n) => write!(f, "i32x{n}"), | |
| Self::VecI64(n) => write!(f, "i64x{n}"), | |
| Self::VecI128(n) => write!(f, "i128x{n}"), | |
| Self::VecF16(n) => write!(f, "f16x{n}"), | |
| Self::VecF32(n) => write!(f, "f32x{n}"), | |
| Self::VecF64(n) => write!(f, "f64x{n}"), | |
| Self::VecF128(n) => write!(f, "f128x{n}"), | |
| Self::SveVecI8 => f.write_str("svint8_t"), | |
| Self::SveVecI16 => f.write_str("svint16_t"), | |
| Self::SveVecI32 => f.write_str("svint32_t"), | |
| Self::SveVecI64 => f.write_str("svint64_t"), | |
| Self::SveVecI128 => f.write_str("svint128_t"), | |
| Self::SveVecF16 => f.write_str("svfloat26_t"), | |
| Self::SveVecF32 => f.write_str("svfloat32_t"), | |
| Self::SveVecF64 => f.write_str("svfloat64_t"), | |
| Self::SveVecF128 => f.write_str("svfloat128_t"), | |
| Self::SveVecBool => f.write_str("svbool_t"), | |
| } | |
| } | |
| } | |
| /// Returns the full set of allocatable registers for a given architecture. | |
| /// | |
| /// The registers are structured as a map containing the set of allocatable | |
| /// registers in each register class. A particular register may be allocatable | |
| /// from multiple register classes, in which case it will appear multiple times | |
| /// in the map. | |
| // NOTE: This function isn't used at the moment, but is needed to support | |
| // falling back to an external assembler. | |
| pub fn allocatable_registers( | |
| arch: InlineAsmArch, | |
| reloc_model: RelocModel, | |
| target_features: &FxIndexSet<Symbol>, | |
| target: &crate::spec::Target, | |
| ) -> FxHashMap<InlineAsmRegClass, FxIndexSet<InlineAsmReg>> { | |
| match arch { | |
| InlineAsmArch::X86 | InlineAsmArch::X86_64 => { | |
| let mut map = x86::regclass_map(); | |
| x86::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::Arm => { | |
| let mut map = arm::regclass_map(); | |
| arm::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::AArch64 | InlineAsmArch::Arm64EC => { | |
| let mut map = aarch64::regclass_map(); | |
| aarch64::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::Amdgpu => { | |
| let mut map = amdgpu::regclass_map(); | |
| amdgpu::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { | |
| let mut map = riscv::regclass_map(); | |
| riscv::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::Nvptx64 => { | |
| let mut map = nvptx::regclass_map(); | |
| nvptx::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => { | |
| let mut map = powerpc::regclass_map(); | |
| powerpc::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::Hexagon => { | |
| let mut map = hexagon::regclass_map(); | |
| hexagon::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::LoongArch32 | InlineAsmArch::LoongArch64 => { | |
| let mut map = loongarch::regclass_map(); | |
| loongarch::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::Mips | InlineAsmArch::Mips64 => { | |
| let mut map = mips::regclass_map(); | |
| mips::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::S390x => { | |
| let mut map = s390x::regclass_map(); | |
| s390x::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::Sparc | InlineAsmArch::Sparc64 => { | |
| let mut map = sparc::regclass_map(); | |
| sparc::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::SpirV => { | |
| let mut map = spirv::regclass_map(); | |
| spirv::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => { | |
| let mut map = wasm::regclass_map(); | |
| wasm::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::Xtensa => { | |
| let mut map = xtensa::regclass_map(); | |
| xtensa::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::Bpf => { | |
| let mut map = bpf::regclass_map(); | |
| bpf::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::Avr => { | |
| let mut map = avr::regclass_map(); | |
| avr::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::Msp430 => { | |
| let mut map = msp430::regclass_map(); | |
| msp430::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::M68k => { | |
| let mut map = m68k::regclass_map(); | |
| m68k::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| InlineAsmArch::CSKY => { | |
| let mut map = csky::regclass_map(); | |
| csky::fill_reg_map(arch, reloc_model, target_features, target, &mut map); | |
| map | |
| } | |
| } | |
| } | |
| #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)] | |
| #[derive(StableHash, Encodable, Decodable)] | |
| pub enum InlineAsmClobberAbi { | |
| X86, | |
| X86_64Win, | |
| X86_64SysV, | |
| Arm, | |
| AArch64, | |
| AArch64NoX18, | |
| Arm64EC, | |
| Avr, | |
| RiscV, | |
| RiscVE, | |
| LoongArch, | |
| PowerPC, | |
| PowerPCSPE, | |
| S390x, | |
| Bpf, | |
| Msp430, | |
| Xtensa, | |
| } | |
| impl InlineAsmClobberAbi { | |
| /// Parses a clobber ABI for the given target, or returns a list of supported | |
| /// clobber ABIs for the target. | |
| pub fn parse( | |
| arch: InlineAsmArch, | |
| target: &Target, | |
| target_features: &FxIndexSet<Symbol>, | |
| name: Symbol, | |
| ) -> Result<Self, &'static [&'static str]> { | |
| let name = name.as_str(); | |
| match arch { | |
| InlineAsmArch::X86 => match name { | |
| "C" | "system" | "efiapi" | "cdecl" | "stdcall" | "fastcall" => { | |
| Ok(InlineAsmClobberAbi::X86) | |
| } | |
| _ => Err(&["C", "system", "efiapi", "cdecl", "stdcall", "fastcall"]), | |
| }, | |
| InlineAsmArch::X86_64 => match name { | |
| "C" | "system" if !target.is_like_windows => Ok(InlineAsmClobberAbi::X86_64SysV), | |
| "C" | "system" if target.is_like_windows => Ok(InlineAsmClobberAbi::X86_64Win), | |
| "win64" | "efiapi" => Ok(InlineAsmClobberAbi::X86_64Win), | |
| "sysv64" => Ok(InlineAsmClobberAbi::X86_64SysV), | |
| _ => Err(&["C", "system", "efiapi", "win64", "sysv64"]), | |
| }, | |
| InlineAsmArch::Arm => match name { | |
| "C" | "system" | "efiapi" | "aapcs" => Ok(InlineAsmClobberAbi::Arm), | |
| _ => Err(&["C", "system", "efiapi", "aapcs"]), | |
| }, | |
| InlineAsmArch::AArch64 => match name { | |
| "C" | "system" | "efiapi" => { | |
| Ok(if aarch64::target_reserves_x18(target, target_features) { | |
| InlineAsmClobberAbi::AArch64NoX18 | |
| } else { | |
| InlineAsmClobberAbi::AArch64 | |
| }) | |
| } | |
| _ => Err(&["C", "system", "efiapi"]), | |
| }, | |
| InlineAsmArch::Arm64EC => match name { | |
| "C" | "system" => Ok(InlineAsmClobberAbi::Arm64EC), | |
| _ => Err(&["C", "system"]), | |
| }, | |
| InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => match name { | |
| "C" | "system" | "efiapi" => Ok(if riscv::is_e(target_features) { | |
| InlineAsmClobberAbi::RiscVE | |
| } else { | |
| InlineAsmClobberAbi::RiscV | |
| }), | |
| _ => Err(&["C", "system", "efiapi"]), | |
| }, | |
| InlineAsmArch::Avr => match name { | |
| "C" | "system" => Ok(InlineAsmClobberAbi::Avr), | |
| _ => Err(&["C", "system"]), | |
| }, | |
| InlineAsmArch::LoongArch32 | InlineAsmArch::LoongArch64 => match name { | |
| "C" | "system" | "efiapi" => Ok(InlineAsmClobberAbi::LoongArch), | |
| _ => Err(&["C", "system", "efiapi"]), | |
| }, | |
| InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => match name { | |
| "C" | "system" => Ok(if powerpc::is_spe(target) { | |
| InlineAsmClobberAbi::PowerPCSPE | |
| } else { | |
| InlineAsmClobberAbi::PowerPC | |
| }), | |
| _ => Err(&["C", "system"]), | |
| }, | |
| InlineAsmArch::S390x => match name { | |
| "C" | "system" => Ok(InlineAsmClobberAbi::S390x), | |
| _ => Err(&["C", "system"]), | |
| }, | |
| InlineAsmArch::Bpf => match name { | |
| "C" | "system" => Ok(InlineAsmClobberAbi::Bpf), | |
| _ => Err(&["C", "system"]), | |
| }, | |
| InlineAsmArch::Msp430 => match name { | |
| "C" | "system" => Ok(InlineAsmClobberAbi::Msp430), | |
| _ => Err(&["C", "system"]), | |
| }, | |
| InlineAsmArch::Xtensa => match name { | |
| "C" | "system" => Ok(InlineAsmClobberAbi::Xtensa), | |
| _ => Err(&["C", "system"]), | |
| }, | |
| _ => Err(&[]), | |
| } | |
| } | |
| /// Returns the set of registers which are clobbered by this ABI. | |
| pub fn clobbered_regs(self) -> &'static [InlineAsmReg] { | |
| macro_rules! clobbered_regs { | |
| ($arch:ident $arch_reg:ident { | |
| $( | |
| $reg:ident, | |
| )* | |
| }) => { | |
| &[ | |
| $(InlineAsmReg::$arch($arch_reg::$reg),)* | |
| ] | |
| }; | |
| } | |
| match self { | |
| InlineAsmClobberAbi::X86 => clobbered_regs! { | |
| X86 X86InlineAsmReg { | |
| ax, cx, dx, | |
| xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, | |
| k0, k1, k2, k3, k4, k5, k6, k7, | |
| mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7, | |
| st0, st1, st2, st3, st4, st5, st6, st7, | |
| } | |
| }, | |
| InlineAsmClobberAbi::X86_64SysV => clobbered_regs! { | |
| X86 X86InlineAsmReg { | |
| ax, cx, dx, si, di, r8, r9, r10, r11, | |
| xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, | |
| xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, | |
| zmm16, zmm17, zmm18, zmm19, zmm20, zmm21, zmm22, zmm23, | |
| zmm24, zmm25, zmm26, zmm27, zmm28, zmm29, zmm30, zmm31, | |
| k0, k1, k2, k3, k4, k5, k6, k7, | |
| mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7, | |
| st0, st1, st2, st3, st4, st5, st6, st7, | |
| tmm0, tmm1, tmm2, tmm3, tmm4, tmm5, tmm6, tmm7, | |
| } | |
| }, | |
| InlineAsmClobberAbi::X86_64Win => clobbered_regs! { | |
| X86 X86InlineAsmReg { | |
| // rdi and rsi are callee-saved on windows | |
| ax, cx, dx, r8, r9, r10, r11, | |
| // xmm6-xmm15 are callee-saved on windows, but we need to | |
| // mark them as clobbered anyways because the upper portions | |
| // of ymm6-ymm15 are volatile. | |
| xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, | |
| xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15, | |
| zmm16, zmm17, zmm18, zmm19, zmm20, zmm21, zmm22, zmm23, | |
| zmm24, zmm25, zmm26, zmm27, zmm28, zmm29, zmm30, zmm31, | |
| k0, k1, k2, k3, k4, k5, k6, k7, | |
| mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7, | |
| st0, st1, st2, st3, st4, st5, st6, st7, | |
| tmm0, tmm1, tmm2, tmm3, tmm4, tmm5, tmm6, tmm7, | |
| } | |
| }, | |
| InlineAsmClobberAbi::AArch64 => clobbered_regs! { | |
| AArch64 AArch64InlineAsmReg { | |
| x0, x1, x2, x3, x4, x5, x6, x7, | |
| x8, x9, x10, x11, x12, x13, x14, x15, | |
| x16, x17, x18, x30, | |
| // Technically the low 64 bits of v8-v15 are preserved, but | |
| // we have no way of expressing this using clobbers. | |
| v0, v1, v2, v3, v4, v5, v6, v7, | |
| v8, v9, v10, v11, v12, v13, v14, v15, | |
| v16, v17, v18, v19, v20, v21, v22, v23, | |
| v24, v25, v26, v27, v28, v29, v30, v31, | |
| p0, p1, p2, p3, p4, p5, p6, p7, | |
| p8, p9, p10, p11, p12, p13, p14, p15, | |
| ffr, | |
| } | |
| }, | |
| InlineAsmClobberAbi::AArch64NoX18 => clobbered_regs! { | |
| AArch64 AArch64InlineAsmReg { | |
| x0, x1, x2, x3, x4, x5, x6, x7, | |
| x8, x9, x10, x11, x12, x13, x14, x15, | |
| x16, x17, x30, | |
| // Technically the low 64 bits of v8-v15 are preserved, but | |
| // we have no way of expressing this using clobbers. | |
| v0, v1, v2, v3, v4, v5, v6, v7, | |
| v8, v9, v10, v11, v12, v13, v14, v15, | |
| v16, v17, v18, v19, v20, v21, v22, v23, | |
| v24, v25, v26, v27, v28, v29, v30, v31, | |
| p0, p1, p2, p3, p4, p5, p6, p7, | |
| p8, p9, p10, p11, p12, p13, p14, p15, | |
| ffr, | |
| } | |
| }, | |
| InlineAsmClobberAbi::Arm64EC => clobbered_regs! { | |
| AArch64 AArch64InlineAsmReg { | |
| // x13 and x14 cannot be used in Arm64EC. | |
| x0, x1, x2, x3, x4, x5, x6, x7, | |
| x8, x9, x10, x11, x12, x15, | |
| x16, x17, x30, | |
| // Technically the low 64 bits of v8-v15 are preserved, but | |
| // we have no way of expressing this using clobbers. | |
| v0, v1, v2, v3, v4, v5, v6, v7, | |
| v8, v9, v10, v11, v12, v13, v14, v15, | |
| // v16-v31, p*, and ffr cannot be used in Arm64EC. | |
| } | |
| }, | |
| InlineAsmClobberAbi::Arm => clobbered_regs! { | |
| Arm ArmInlineAsmReg { | |
| // r9 is either platform-reserved or callee-saved. Either | |
| // way we don't need to clobber it. | |
| r0, r1, r2, r3, r12, r14, | |
| // The finest-grained register variant is used here so that | |
| // partial uses of larger registers are properly handled. | |
| s0, s1, s2, s3, s4, s5, s6, s7, | |
| s8, s9, s10, s11, s12, s13, s14, s15, | |
| // s16-s31 are callee-saved | |
| d16, d17, d18, d19, d20, d21, d22, d23, | |
| d24, d25, d26, d27, d28, d29, d30, d31, | |
| } | |
| }, | |
| InlineAsmClobberAbi::Avr => clobbered_regs! { | |
| Avr AvrInlineAsmReg { | |
| // The list of "Call-Used Registers" according to | |
| // https://gcc.gnu.org/wiki/avr-gcc#Call-Used_Registers | |
| // Clobbered registers available in inline assembly | |
| r18, r19, r20, r21, r22, r23, r24, r25, r26, r27, r30, r31, | |
| // As per the AVR-GCC-ABI documentation linked above, the R0 | |
| // register is a clobbered register as well. Since we don't | |
| // allow the usage of R0 in inline assembly, nothing has to | |
| // be done here. | |
| // Likewise, the T-flag in the SREG should be clobbered, but | |
| // this is not necessary to be listed here, since the SREG | |
| // is considered clobbered anyways unless `preserve_flags` | |
| // is used. | |
| } | |
| }, | |
| InlineAsmClobberAbi::RiscV => clobbered_regs! { | |
| RiscV RiscVInlineAsmReg { | |
| // ra | |
| x1, | |
| // t0-t2 | |
| x5, x6, x7, | |
| // a0-a7 | |
| x10, x11, x12, x13, x14, x15, x16, x17, | |
| // t3-t6 | |
| x28, x29, x30, x31, | |
| // ft0-ft7 | |
| f0, f1, f2, f3, f4, f5, f6, f7, | |
| // fa0-fa7 | |
| f10, f11, f12, f13, f14, f15, f16, f17, | |
| // ft8-ft11 | |
| f28, f29, f30, f31, | |
| v0, v1, v2, v3, v4, v5, v6, v7, | |
| v8, v9, v10, v11, v12, v13, v14, v15, | |
| v16, v17, v18, v19, v20, v21, v22, v23, | |
| v24, v25, v26, v27, v28, v29, v30, v31, | |
| } | |
| }, | |
| InlineAsmClobberAbi::RiscVE => clobbered_regs! { | |
| RiscV RiscVInlineAsmReg { | |
| // Refs: | |
| // - ILP32E https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/draft-20240829-13bfa9f54634cb60d86b9b333e109f077805b4b3/riscv-cc.adoc#ilp32e-calling-convention | |
| // - LP64E https://github.com/riscv-non-isa/riscv-elf-psabi-doc/pull/299 | |
| // ra | |
| x1, | |
| // t0-t2 | |
| x5, x6, x7, | |
| // a0-a5 | |
| x10, x11, x12, x13, x14, x15, | |
| // ft0-ft7 | |
| f0, f1, f2, f3, f4, f5, f6, f7, | |
| // fa0-fa7 | |
| f10, f11, f12, f13, f14, f15, f16, f17, | |
| // ft8-ft11 | |
| f28, f29, f30, f31, | |
| v0, v1, v2, v3, v4, v5, v6, v7, | |
| v8, v9, v10, v11, v12, v13, v14, v15, | |
| v16, v17, v18, v19, v20, v21, v22, v23, | |
| v24, v25, v26, v27, v28, v29, v30, v31, | |
| } | |
| }, | |
| InlineAsmClobberAbi::LoongArch => clobbered_regs! { | |
| LoongArch LoongArchInlineAsmReg { | |
| // ra | |
| r1, | |
| // a0-a7 | |
| r4, r5, r6, r7, r8, r9, r10, r11, | |
| // t0-t8 | |
| r12, r13, r14, r15, r16, r17, r18, r19, r20, | |
| // fa0-fa7 | |
| f0, f1, f2, f3, f4, f5, f6, f7, | |
| // ft0-ft15 | |
| f8, f9, f10, f11, f12, f13, f14, f15, | |
| f16, f17, f18, f19, f20, f21, f22, f23, | |
| } | |
| }, | |
| InlineAsmClobberAbi::PowerPC => clobbered_regs! { | |
| PowerPC PowerPCInlineAsmReg { | |
| // Refs: | |
| // - PPC32 SysV: "3.2. Function Calling Sequence" in Power Architecture® 32-bit Application Binary Interface Supplement 1.0 - Linux® & Embedded | |
| // https://web.archive.org/web/20120608163804/https://www.power.org/resources/downloads/Power-Arch-32-bit-ABI-supp-1.0-Unified.pdf | |
| // - PPC64 ELFv1: "3.2. Function Calling Sequence" in 64-bit PowerPC ELF Application Binary Interface Supplement 1.9 | |
| // https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#FUNC-CALL | |
| // - PPC64 ELFv2: "2.2 Function Calling Sequence" in 64-Bit ELF V2 ABI Specification: Power Architecture, Revision 1.5 | |
| // https://openpowerfoundation.org/specifications/64bitelfabi/ | |
| // - AIX: | |
| // - Register usage and conventions | |
| // https://www.ibm.com/docs/en/aix/7.3?topic=overview-register-usage-conventions | |
| // - Special registers in the PowerPC® | |
| // https://www.ibm.com/docs/en/aix/7.3?topic=overview-special-registers-in-powerpc | |
| // - AIX vector programming | |
| // https://www.ibm.com/docs/en/aix/7.3?topic=concepts-aix-vector-programming | |
| // r0, r3-r12 | |
| r0, | |
| r3, r4, r5, r6, r7, | |
| r8, r9, r10, r11, r12, | |
| // f0-f13 and their vsx overlays. | |
| f0, f1, f2, f3, f4, f5, f6, f7, | |
| f8, f9, f10, f11, f12, f13, | |
| vs0, vs1, vs2, vs3, vs4, vs5, vs6, vs7, | |
| vs8, vs9, vs10, vs11, vs12, vs13, | |
| // vs14-31, the fpr portion is saved, but the rest of the register is volatile. | |
| // We can't express that here, so mark the entire vsx register as volatile. | |
| vs14, vs15, vs16, vs17, vs18, vs19, vs20, | |
| vs21, vs22, vs23, vs24, vs25, vs26, vs27, | |
| vs28, vs29, vs30, vs31, | |
| // v0-v19 | |
| v0, v1, v2, v3, v4, v5, v6, v7, | |
| v8, v9, v10, v11, v12, v13, v14, | |
| v15, v16, v17, v18, v19, | |
| // cr0-cr1, cr5-cr7, ctr, lr, xer | |
| cr0, cr1, | |
| cr5, cr6, cr7, | |
| ctr, | |
| lr, | |
| xer, | |
| } | |
| }, | |
| InlineAsmClobberAbi::PowerPCSPE => clobbered_regs! { | |
| PowerPC PowerPCInlineAsmReg { | |
| // r0, r3-r12 | |
| r0, | |
| r3, r4, r5, r6, r7, | |
| r8, r9, r10, r11, r12, | |
| // cr0-cr1, cr5-cr7, ctr, lr, xer, spe_acc | |
| cr0, cr1, | |
| cr5, cr6, cr7, | |
| ctr, | |
| lr, | |
| xer, | |
| spe_acc, | |
| } | |
| }, | |
| InlineAsmClobberAbi::S390x => clobbered_regs! { | |
| S390x S390xInlineAsmReg { | |
| r0, r1, r2, r3, r4, r5, | |
| r14, | |
| // f0-f7, v0-v7 | |
| f0, f1, f2, f3, f4, f5, f6, f7, | |
| v0, v1, v2, v3, v4, v5, v6, v7, | |
| // Technically the left halves of v8-v15 (i.e., f8-f15) are saved, but | |
| // we have no way of expressing this using clobbers. | |
| v8, v9, v10, v11, v12, v13, v14, v15, | |
| // Other vector registers are volatile | |
| v16, v17, v18, v19, v20, v21, v22, v23, | |
| v24, v25, v26, v27, v28, v29, v30, v31, | |
| // a0-a1 are reserved, other access registers are volatile | |
| a2, a3, a4, a5, a6, a7, | |
| a8, a9, a10, a11, a12, a13, a14, a15, | |
| } | |
| }, | |
| InlineAsmClobberAbi::Bpf => clobbered_regs! { | |
| Bpf BpfInlineAsmReg { | |
| // Refs: Section 1.1 "Registers and calling convention" in BPF ABI Recommended Conventions and Guidelines v1.0 | |
| // https://www.kernel.org/doc/html/latest/bpf/standardization/abi.html#registers-and-calling-convention | |
| r0, r1, r2, r3, r4, r5, | |
| } | |
| }, | |
| InlineAsmClobberAbi::Msp430 => clobbered_regs! { | |
| Msp430 Msp430InlineAsmReg { | |
| r11, r12, r13, r14, r15, | |
| } | |
| }, | |
| InlineAsmClobberAbi::Xtensa => clobbered_regs! { | |
| Xtensa XtensaInlineAsmReg { | |
| // Refs: | |
| // - Xtensa ISA Reference Manual, Section 8.1.4 & 8.1.6 | |
| // - "Except for LITBASE, all non-privileged special registers are | |
| // caller-saved." | |
| // Caller-saved general-purpose registers (a2-a11). | |
| // a0 is the return address (reserved by LLVM). | |
| // a1/sp is the stack pointer (reserved by LLVM). | |
| // a12-a15 are callee-saved. | |
| a2, a3, a4, a5, a6, a7, | |
| a8, a9, a10, a11, | |
| // All floating-point registers are caller-saved. | |
| f0, f1, f2, f3, f4, f5, f6, f7, | |
| f8, f9, f10, f11, f12, f13, f14, f15, | |
| // SAR (Shift Amount Register) - caller-saved, always present. | |
| sar, | |
| // SCOMPARE1 - caller-saved (s32c1i option). | |
| scompare1, | |
| // Loop registers - caller-saved (loop option). | |
| lbeg, lend, lcount, | |
| // MAC16 registers - caller-saved (mac16 option). | |
| acclo, acchi, | |
| m0, m1, m2, m3, | |
| // Boolean registers - caller-saved (boolean option). | |
| b0, b1, b2, b3, b4, b5, b6, b7, | |
| b8, b9, b10, b11, b12, b13, b14, b15, | |
| } | |
| }, | |
| } | |
| } | |
| } |