1
Fork 0

Auto merge of #132472 - taiki-e:sparc-asm, r=Amanieu

Basic inline assembly support for SPARC and SPARC64

This implements asm_experimental_arch (tracking issue https://github.com/rust-lang/rust/issues/93335) for SPARC and SPARC64.

This PR includes:

- General-purpose registers `r[0-31]` (`reg` register class, LLVM/GCC constraint `r`)
  Supported types: i8, i16, i32, i64 (SPARC64-only)
  Aliases: `g[0-7]` (`r[0-7]`), `o[0-7]` (`r[8-15]`), `l[0-7]` (`r[16-23]`), `i[0-7]` (`r[24-31]`)
- `y` register (clobber-only, needed for clobber_abi)
- preserves_flags: Integer condition codes (`icc`, `xcc`) and floating-point condition codes (`fcc*`)

The following are *not* included:

- 64-bit integer support on SPARC-V8+'s global or out registers (`g[0-7]`, `o[0-7]`): GCC's `h` constraint (it seems that there is no corresponding constraint in LLVM?)
- Floating-point registers (LLVM/GCC constraint `e`/`f`):
  I initially tried to implement this, but postponed it for now because there seemed to be several parts in LLVM that behaved differently than in the LangRef's description.
- clobber_abi: Support for floating-point registers is needed.

Refs:
- LLVM
  - Reserved registers https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/Sparc/SparcRegisterInfo.cpp#L52
  - Register definitions https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/Sparc/SparcRegisterInfo.td
  - Supported constraints https://llvm.org/docs/LangRef.html#supported-constraint-code-list
- GCC
  - Reserved registers 63b6967b06/gcc/config/sparc/sparc.h (L633-L658)
  - Supported constraints https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html
- SPARC ISA/ABI
  - (64-bit ISA) The SPARC Architecture Manual, Version 9
    (32-bit ISA) The SPARC Architecture Manual, Version 8
    (64-bit ABI) System V Application Binary Interface SPARC Version 9 Processor Supplement, Rev 1.35
    (32-bit ABI) System V Application Binary Interface SPARC Processor Supplement, Third Edition
    The above docs can be downloaded from https://sparc.org/technical-documents
  - (32-bit V8+ ABI) The V8+ Technical Specification
    https://temlib.org/pub/SparcStation/Standards/V8plus.pdf

cc `@thejpster` (sparc-unknown-none-elf target maintainer)
(AFAIK, other sparc/sprac64 targets don't have target maintainers)

r? `@Amanieu`

`@rustbot` label +O-SPARC +A-inline-assembly
This commit is contained in:
bors 2024-11-07 21:07:06 +00:00
commit b91a3a0560
14 changed files with 770 additions and 43 deletions

View file

@ -191,6 +191,7 @@ mod nvptx;
mod powerpc;
mod riscv;
mod s390x;
mod sparc;
mod spirv;
mod wasm;
mod x86;
@ -209,6 +210,7 @@ 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};
@ -230,6 +232,8 @@ pub enum InlineAsmArch {
PowerPC,
PowerPC64,
S390x,
Sparc,
Sparc64,
SpirV,
Wasm32,
Wasm64,
@ -260,6 +264,8 @@ impl FromStr for InlineAsmArch {
"mips" | "mips32r6" => Ok(Self::Mips),
"mips64" | "mips64r6" => Ok(Self::Mips64),
"s390x" => Ok(Self::S390x),
"sparc" => Ok(Self::Sparc),
"sparc64" => Ok(Self::Sparc64),
"spirv" => Ok(Self::SpirV),
"wasm32" => Ok(Self::Wasm32),
"wasm64" => Ok(Self::Wasm64),
@ -286,6 +292,7 @@ pub enum InlineAsmReg {
LoongArch(LoongArchInlineAsmReg),
Mips(MipsInlineAsmReg),
S390x(S390xInlineAsmReg),
Sparc(SparcInlineAsmReg),
SpirV(SpirVInlineAsmReg),
Wasm(WasmInlineAsmReg),
Bpf(BpfInlineAsmReg),
@ -309,6 +316,7 @@ impl InlineAsmReg {
Self::LoongArch(r) => r.name(),
Self::Mips(r) => r.name(),
Self::S390x(r) => r.name(),
Self::Sparc(r) => r.name(),
Self::Bpf(r) => r.name(),
Self::Avr(r) => r.name(),
Self::Msp430(r) => r.name(),
@ -329,6 +337,7 @@ impl InlineAsmReg {
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::Bpf(r) => InlineAsmRegClass::Bpf(r.reg_class()),
Self::Avr(r) => InlineAsmRegClass::Avr(r.reg_class()),
Self::Msp430(r) => InlineAsmRegClass::Msp430(r.reg_class()),
@ -361,6 +370,9 @@ impl InlineAsmReg {
Self::Mips(MipsInlineAsmReg::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)?)
@ -393,6 +405,7 @@ impl InlineAsmReg {
}
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::Msp430(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
@ -420,6 +433,7 @@ impl InlineAsmReg {
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::Bpf(r) => r.emit(out, arch, modifier),
Self::Avr(r) => r.emit(out, arch, modifier),
Self::Msp430(r) => r.emit(out, arch, modifier),
@ -440,6 +454,7 @@ impl InlineAsmReg {
Self::LoongArch(_) => cb(self),
Self::Mips(_) => cb(self),
Self::S390x(r) => r.overlapping_regs(|r| cb(Self::S390x(r))),
Self::Sparc(_) => 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),
@ -463,6 +478,7 @@ pub enum InlineAsmRegClass {
LoongArch(LoongArchInlineAsmRegClass),
Mips(MipsInlineAsmRegClass),
S390x(S390xInlineAsmRegClass),
Sparc(SparcInlineAsmRegClass),
SpirV(SpirVInlineAsmRegClass),
Wasm(WasmInlineAsmRegClass),
Bpf(BpfInlineAsmRegClass),
@ -487,6 +503,7 @@ impl InlineAsmRegClass {
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::Bpf(r) => r.name(),
@ -513,6 +530,7 @@ impl InlineAsmRegClass {
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::Bpf(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Bpf),
@ -542,6 +560,7 @@ impl InlineAsmRegClass {
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::Bpf(r) => r.suggest_modifier(arch, ty),
@ -571,6 +590,7 @@ impl InlineAsmRegClass {
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::Bpf(r) => r.default_modifier(arch),
@ -599,6 +619,7 @@ impl InlineAsmRegClass {
Self::LoongArch(r) => r.supported_types(arch),
Self::Mips(r) => r.supported_types(arch),
Self::S390x(r) => r.supported_types(arch),
Self::Sparc(r) => r.supported_types(arch),
Self::SpirV(r) => r.supported_types(arch),
Self::Wasm(r) => r.supported_types(arch),
Self::Bpf(r) => r.supported_types(arch),
@ -632,6 +653,9 @@ impl InlineAsmRegClass {
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)?)
@ -658,6 +682,7 @@ impl InlineAsmRegClass {
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::Bpf(r) => r.valid_modifiers(arch),
@ -843,6 +868,11 @@ pub fn allocatable_registers(
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);

View file

@ -0,0 +1,138 @@
use std::fmt;
use rustc_data_structures::fx::FxIndexSet;
use rustc_span::Symbol;
use super::{InlineAsmArch, InlineAsmType, ModifierInfo};
use crate::spec::{RelocModel, Target};
def_reg_class! {
Sparc SparcInlineAsmRegClass {
reg,
yreg,
}
}
impl SparcInlineAsmRegClass {
pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] {
&[]
}
pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
None
}
pub fn suggest_modifier(
self,
_arch: InlineAsmArch,
_ty: InlineAsmType,
) -> Option<ModifierInfo> {
None
}
pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> {
None
}
pub fn supported_types(
self,
arch: InlineAsmArch,
) -> &'static [(InlineAsmType, Option<Symbol>)] {
match self {
Self::reg => {
if arch == InlineAsmArch::Sparc {
types! {
_: I8, I16, I32;
// FIXME: i64 is ok for g*/o* registers on SPARC-V8+ ("h" constraint in GCC),
// but not yet supported in LLVM.
// v8plus: I64;
}
} else {
types! { _: I8, I16, I32, I64; }
}
}
Self::yreg => &[],
}
}
}
fn reserved_g5(
arch: InlineAsmArch,
_reloc_model: RelocModel,
_target_features: &FxIndexSet<Symbol>,
_target: &Target,
_is_clobber: bool,
) -> Result<(), &'static str> {
if arch == InlineAsmArch::Sparc {
// FIXME: Section 2.1.5 "Function Registers with Unassigned Roles" of the V8+ Technical
// Specification says "%g5; no longer reserved for system software" [1], but LLVM always
// reserves it on SPARC32 [2].
// [1]: https://temlib.org/pub/SparcStation/Standards/V8plus.pdf
// [2]: https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/Sparc/SparcRegisterInfo.cpp#L64-L66
Err("g5 is reserved for system on SPARC32")
} else {
Ok(())
}
}
def_regs! {
Sparc SparcInlineAsmReg SparcInlineAsmRegClass {
// FIXME:
// - LLVM has reserve-{g,o,l,i}N feature to reserve each general-purpose registers.
// - g2-g4 are reserved for application (optional in both LLVM and GCC, and GCC has -mno-app-regs option to reserve them).
// There are currently no builtin targets that use them, but in the future they may need to
// be supported via options similar to AArch64's -Z fixed-x18.
r2: reg = ["r2", "g2"], // % reserved_g2
r3: reg = ["r3", "g3"], // % reserved_g3
r4: reg = ["r4", "g4"], // % reserved_g4
r5: reg = ["r5", "g5"] % reserved_g5,
r8: reg = ["r8", "o0"], // % reserved_o0
r9: reg = ["r9", "o1"], // % reserved_o1
r10: reg = ["r10", "o2"], // % reserved_o2
r11: reg = ["r11", "o3"], // % reserved_o3
r12: reg = ["r12", "o4"], // % reserved_o4
r13: reg = ["r13", "o5"], // % reserved_o5
r15: reg = ["r15", "o7"], // % reserved_o7
r16: reg = ["r16", "l0"], // % reserved_l0
r17: reg = ["r17", "l1"], // % reserved_l1
r18: reg = ["r18", "l2"], // % reserved_l2
r19: reg = ["r19", "l3"], // % reserved_l3
r20: reg = ["r20", "l4"], // % reserved_l4
r21: reg = ["r21", "l5"], // % reserved_l5
r22: reg = ["r22", "l6"], // % reserved_l6
r23: reg = ["r23", "l7"], // % reserved_l7
r24: reg = ["r24", "i0"], // % reserved_i0
r25: reg = ["r25", "i1"], // % reserved_i1
r26: reg = ["r26", "i2"], // % reserved_i2
r27: reg = ["r27", "i3"], // % reserved_i3
r28: reg = ["r28", "i4"], // % reserved_i4
r29: reg = ["r29", "i5"], // % reserved_i5
y: yreg = ["y"],
#error = ["r0", "g0"] =>
"g0 is always zero and cannot be used as an operand for inline asm",
// FIXME: %g1 is volatile in ABI, but used internally by LLVM.
// https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/Sparc/SparcRegisterInfo.cpp#L55-L56
// > FIXME: G1 reserved for now for large imm generation by frame code.
#error = ["r1", "g1"] =>
"reserved by LLVM and cannot be used as an operand for inline asm",
#error = ["r6", "g6", "r7", "g7"] =>
"reserved for system and cannot be used as an operand for inline asm",
#error = ["sp", "r14", "o6"] =>
"the stack pointer cannot be used as an operand for inline asm",
#error = ["fp", "r30", "i6"] =>
"the frame pointer cannot be used as an operand for inline asm",
#error = ["r31", "i7"] =>
"the return address register cannot be used as an operand for inline asm",
}
}
impl SparcInlineAsmReg {
pub fn emit(
self,
out: &mut dyn fmt::Write,
_arch: InlineAsmArch,
_modifier: Option<char>,
) -> fmt::Result {
write!(out, "%{}", self.name())
}
}