Skip to content

Basic inline assembly support for SPARC and SPARC64 #132472

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 7, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions compiler/rustc_codegen_gcc/src/asm.rs
Original file line number Diff line number Diff line change
@@ -688,6 +688,8 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::reg) => "r",
InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::yreg) => unreachable!("clobber-only"),
InlineAsmRegClass::Err => unreachable!(),
},
};
@@ -767,6 +769,8 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::reg) => cx.type_i32(),
InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::yreg) => unreachable!("clobber-only"),
InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => cx.type_i16(),
InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => cx.type_i32(),
InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_addr) => cx.type_i32(),
@@ -946,6 +950,7 @@ fn modifier_to_gcc(
},
InlineAsmRegClass::Avr(_) => None,
InlineAsmRegClass::S390x(_) => None,
InlineAsmRegClass::Sparc(_) => None,
InlineAsmRegClass::Msp430(_) => None,
InlineAsmRegClass::M68k(_) => None,
InlineAsmRegClass::CSKY(_) => None,
14 changes: 14 additions & 0 deletions compiler/rustc_codegen_llvm/src/asm.rs
Original file line number Diff line number Diff line change
@@ -268,6 +268,15 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
InlineAsmArch::S390x => {
constraints.push("~{cc}".to_string());
}
InlineAsmArch::Sparc | InlineAsmArch::Sparc64 => {
// In LLVM, ~{icc} represents icc and xcc in 64-bit code.
// https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/Sparc/SparcRegisterInfo.td#L64
constraints.push("~{icc}".to_string());
constraints.push("~{fcc0}".to_string());
constraints.push("~{fcc1}".to_string());
constraints.push("~{fcc2}".to_string());
constraints.push("~{fcc3}".to_string());
}
InlineAsmArch::SpirV => {}
InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => {}
InlineAsmArch::Bpf => {}
@@ -672,6 +681,8 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) ->
S390x(S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg) => {
unreachable!("clobber-only")
}
Sparc(SparcInlineAsmRegClass::reg) => "r",
Sparc(SparcInlineAsmRegClass::yreg) => unreachable!("clobber-only"),
Msp430(Msp430InlineAsmRegClass::reg) => "r",
M68k(M68kInlineAsmRegClass::reg) => "r",
M68k(M68kInlineAsmRegClass::reg_addr) => "a",
@@ -765,6 +776,7 @@ fn modifier_to_llvm(
},
Avr(_) => None,
S390x(_) => None,
Sparc(_) => None,
Msp430(_) => None,
SpirV(SpirVInlineAsmRegClass::reg) => bug!("LLVM backend does not support SPIR-V"),
M68k(_) => None,
@@ -835,6 +847,8 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &'
S390x(S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg) => {
unreachable!("clobber-only")
}
Sparc(SparcInlineAsmRegClass::reg) => cx.type_i32(),
Sparc(SparcInlineAsmRegClass::yreg) => unreachable!("clobber-only"),
Msp430(Msp430InlineAsmRegClass::reg) => cx.type_i16(),
M68k(M68kInlineAsmRegClass::reg) => cx.type_i32(),
M68k(M68kInlineAsmRegClass::reg_addr) => cx.type_i32(),
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
@@ -2175,6 +2175,7 @@ symbols! {
yes,
yield_expr,
ymm_reg,
yreg,
zfh,
zfhmin,
zmm_reg,
30 changes: 30 additions & 0 deletions compiler/rustc_target/src/asm/mod.rs
Original file line number Diff line number Diff line change
@@ -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);
138 changes: 138 additions & 0 deletions compiler/rustc_target/src/asm/sparc.rs
Original file line number Diff line number Diff line change
@@ -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())
}
}
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
- CSKY
- s390x
- Arm64EC
- SPARC

## Register classes

@@ -56,6 +57,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
| s390x | `freg` | `f[0-15]` | `f` |
| s390x | `vreg` | `v[0-31]` | Only clobbers |
| s390x | `areg` | `a[2-15]` | Only clobbers |
| SPARC | `reg` | `r[2-29]` | `r` |
| SPARC | `yreg` | `y` | Only clobbers |
| Arm64EC | `reg` | `x[0-12]`, `x[15-22]`, `x[25-27]`, `x30` | `r` |
| Arm64EC | `vreg` | `v[0-15]` | `w` |
| Arm64EC | `vreg_low16` | `v[0-15]` | `x` |
@@ -97,6 +100,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
| s390x | `freg` | None | `f32`, `f64` |
| s390x | `vreg` | N/A | Only clobbers |
| s390x | `areg` | N/A | Only clobbers |
| SPARC | `reg` | None | `i8`, `i16`, `i32`, `i64` (SPARC64 only) |
| SPARC | `yreg` | N/A | Only clobbers |
| Arm64EC | `reg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64` |
| Arm64EC | `vreg` | None | `i8`, `i16`, `i32`, `f32`, `i64`, `f64`, <br> `i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2`, `f64x1`, <br> `i8x16`, `i16x8`, `i32x4`, `i64x2`, `f32x4`, `f64x2` |

@@ -135,6 +140,10 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
| CSKY | `r29` | `rtb` |
| CSKY | `r30` | `svbr` |
| CSKY | `r31` | `tls` |
| SPARC | `r[0-7]` | `g[0-7]` |
| SPARC | `r[8-15]` | `o[0-7]` |
| SPARC | `r[16-23]` | `l[0-7]` |
| SPARC | `r[24-31]` | `i[0-7]` |
| Arm64EC | `x[0-30]` | `w[0-30]` |
| Arm64EC | `x29` | `fp` |
| Arm64EC | `x30` | `lr` |
@@ -150,8 +159,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect

| Architecture | Unsupported register | Reason |
| ------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| All | `sp`, `r15` (s390x) | The stack pointer must be restored to its original value at the end of an asm code block. |
| All | `fr` (Hexagon), `fp` (PowerPC), `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r11` (s390x), `x29` (Arm64EC) | The frame pointer cannot be used as an input or output. |
| All | `sp`, `r15` (s390x), `r14`/`o6` (SPARC) | The stack pointer must be restored to its original value at the end of an asm code block. |
| All | `fr` (Hexagon), `fp` (PowerPC), `$fp` (MIPS), `Y` (AVR), `r4` (MSP430), `a6` (M68k), `r11` (s390x), `r30`/`i6` (SPARC), `x29` (Arm64EC) | The frame pointer cannot be used as an input or output. |
| All | `r19` (Hexagon), `r29` (PowerPC), `r30` (PowerPC), `x19` (Arm64EC) | These are used internally by LLVM as "base pointer" for functions with complex stack frames. |
| MIPS | `$0` or `$zero` | This is a constant zero register which can't be modified. |
| MIPS | `$1` or `$at` | Reserved for assembler. |
@@ -174,6 +183,11 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
| CSKY | `r31` | This is the TLS register. |
| s390x | `c[0-15]` | Reserved by the kernel. |
| s390x | `a[0-1]` | Reserved for system use. |
| SPARC | `r0`/`g0` | This is always zero and cannot be used as inputs or outputs. |
| SPARC | `r1`/`g1` | Used internally by LLVM. |
| SPARC | `r5`/`g5` | Reserved for system. (SPARC32 only) |
| SPARC | `r6`/`g6`, `r7`/`g7` | Reserved for system. |
| SPARC | `r31`/`i7` | Return address cannot be used as inputs or outputs. |
| Arm64EC | `xzr` | This is a constant zero register which can't be modified. |
| Arm64EC | `x18` | This is an OS-reserved register. |
| Arm64EC | `x13`, `x14`, `x23`, `x24`, `x28`, `v[16-31]` | These are AArch64 registers that are not supported for Arm64EC. |
@@ -195,6 +209,7 @@ This feature tracks `asm!` and `global_asm!` support for the following architect
| s390x | `reg` | None | `%r0` | None |
| s390x | `reg_addr` | None | `%r1` | None |
| s390x | `freg` | None | `%f0` | None |
| SPARC | `reg` | None | `%o0` | None |
| CSKY | `reg` | None | `r0` | None |
| CSKY | `freg` | None | `f0` | None |
| Arm64EC | `reg` | None | `x0` | `x` |
@@ -219,6 +234,9 @@ These flags registers must be restored upon exiting the asm block if the `preser
- The condition code register `ccr`.
- s390x
- The condition code register `cc`.
- SPARC
- Integer condition codes (`icc` and `xcc`)
- Floating-point condition codes (`fcc[0-3]`)
- Arm64EC
- Condition flags (`NZCV` register).
- Floating-point status (`FPSR` register).
168 changes: 168 additions & 0 deletions tests/assembly/asm/sparc-types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
//@ revisions: sparc sparcv8plus sparc64
//@ assembly-output: emit-asm
//@[sparc] compile-flags: --target sparc-unknown-none-elf
//@[sparc] needs-llvm-components: sparc
//@[sparcv8plus] compile-flags: --target sparc-unknown-linux-gnu
//@[sparcv8plus] needs-llvm-components: sparc
//@[sparc64] compile-flags: --target sparc64-unknown-linux-gnu
//@[sparc64] needs-llvm-components: sparc
//@ compile-flags: -Zmerge-functions=disabled

#![feature(no_core, lang_items, rustc_attrs, repr_simd, asm_experimental_arch)]
#![crate_type = "rlib"]
#![no_core]
#![allow(asm_sub_register, non_camel_case_types)]

#[rustc_builtin_macro]
macro_rules! asm {
() => {};
}
#[rustc_builtin_macro]
macro_rules! concat {
() => {};
}
#[rustc_builtin_macro]
macro_rules! stringify {
() => {};
}

#[lang = "sized"]
trait Sized {}
#[lang = "copy"]
trait Copy {}

type ptr = *const i32;

impl Copy for i8 {}
impl Copy for u8 {}
impl Copy for i16 {}
impl Copy for i32 {}
impl Copy for i64 {}
impl Copy for f32 {}
impl Copy for f64 {}
impl Copy for ptr {}

extern "C" {
fn extern_func();
static extern_static: u8;
}

macro_rules! check { ($func:ident, $ty:ty, $class:ident, $mov:literal) => {
#[no_mangle]
pub unsafe fn $func(x: $ty) -> $ty {
let y;
asm!(concat!($mov," {}, {}"), in($class) x, out($class) y);
y
}
};}

macro_rules! check_reg { ($func:ident, $ty:ty, $reg:tt, $mov:literal) => {
#[no_mangle]
pub unsafe fn $func(x: $ty) -> $ty {
let y;
asm!(concat!($mov, " %", $reg, ", %", $reg), in($reg) x, lateout($reg) y);
y
}
};}

// CHECK-LABEL: sym_fn_32:
// CHECK: !APP
// CHECK-NEXT: call extern_func
// CHECK-NEXT: !NO_APP
#[no_mangle]
pub unsafe fn sym_fn_32() {
asm!("call {}", sym extern_func);
}

// CHECK-LABEL: sym_static:
// CHECK: !APP
// CHECK-NEXT: call extern_static
// CHECK-NEXT: !NO_APP
#[no_mangle]
pub unsafe fn sym_static() {
asm!("call {}", sym extern_static);
}

// CHECK-LABEL: reg_i8:
// CHECK: !APP
// CHECK-NEXT: mov %{{[goli]}}{{[0-9]+}}, %{{[goli]}}{{[0-9]+}}
// CHECK-NEXT: !NO_APP
check!(reg_i8, i8, reg, "mov");

// CHECK-LABEL: reg_i16:
// CHECK: !APP
// CHECK-NEXT: mov %{{[goli]}}{{[0-9]+}}, %{{[goli]}}{{[0-9]+}}
// CHECK-NEXT: !NO_APP
check!(reg_i16, i16, reg, "mov");

// CHECK-LABEL: reg_i32:
// CHECK: !APP
// CHECK-NEXT: mov %{{[goli]}}{{[0-9]+}}, %{{[goli]}}{{[0-9]+}}
// CHECK-NEXT: !NO_APP
check!(reg_i32, i32, reg, "mov");

// FIXME: should be allowed for sparcv8plus but not yet supported in LLVM
// sparc64-LABEL: reg_i64:
// sparc64: !APP
// sparc64-NEXT: mov %{{[goli]}}{{[0-9]+}}, %{{[goli]}}{{[0-9]+}}
// sparc64-NEXT: !NO_APP
#[cfg(sparc64)]
check!(reg_i64, i64, reg, "mov");

// CHECK-LABEL: reg_ptr:
// CHECK: !APP
// CHECK-NEXT: mov %{{[goli]}}{{[0-9]+}}, %{{[goli]}}{{[0-9]+}}
// CHECK-NEXT: !NO_APP
check!(reg_ptr, ptr, reg, "mov");

// CHECK-LABEL: o0_i8:
// CHECK: !APP
// CHECK-NEXT: mov %o0, %o0
// CHECK-NEXT: !NO_APP
check_reg!(o0_i8, i8, "o0", "mov");

// CHECK-LABEL: o0_i16:
// CHECK: !APP
// CHECK-NEXT: mov %o0, %o0
// CHECK-NEXT: !NO_APP
check_reg!(o0_i16, i16, "o0", "mov");

// CHECK-LABEL: o0_i32:
// CHECK: !APP
// CHECK-NEXT: mov %o0, %o0
// CHECK-NEXT: !NO_APP
check_reg!(o0_i32, i32, "o0", "mov");

// FIXME: should be allowed for sparcv8plus but not yet supported in LLVM
// sparc64-LABEL: o0_i64:
// sparc64: !APP
// sparc64-NEXT: mov %o0, %o0
// sparc64-NEXT: !NO_APP
#[cfg(sparc64)]
check_reg!(o0_i64, i64, "o0", "mov");

// CHECK-LABEL: r9_i8:
// CHECK: !APP
// CHECK-NEXT: mov %o1, %o1
// CHECK-NEXT: !NO_APP
check_reg!(r9_i8, i8, "r9", "mov");

// CHECK-LABEL: r9_i16:
// CHECK: !APP
// CHECK-NEXT: mov %o1, %o1
// CHECK-NEXT: !NO_APP
check_reg!(r9_i16, i16, "r9", "mov");

// CHECK-LABEL: r9_i32:
// CHECK: !APP
// CHECK-NEXT: mov %o1, %o1
// CHECK-NEXT: !NO_APP
check_reg!(r9_i32, i32, "r9", "mov");

// FIXME: should be allowed for sparcv8plus but not yet supported in LLVM
// sparc64-LABEL: r9_i64:
// sparc64: !APP
// sparc64-NEXT: mov %o1, %o1
// sparc64-NEXT: !NO_APP
#[cfg(sparc64)]
check_reg!(r9_i64, i64, "r9", "mov");
40 changes: 40 additions & 0 deletions tests/codegen/asm/sparc-clobbers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//@ revisions: sparc sparcv8plus sparc64
//@[sparc] compile-flags: --target sparc-unknown-none-elf
//@[sparc] needs-llvm-components: sparc
//@[sparcv8plus] compile-flags: --target sparc-unknown-linux-gnu
//@[sparcv8plus] needs-llvm-components: sparc
//@[sparc64] compile-flags: --target sparc64-unknown-linux-gnu
//@[sparc64] needs-llvm-components: sparc

#![crate_type = "rlib"]
#![feature(no_core, rustc_attrs, lang_items, asm_experimental_arch)]
#![no_core]

#[lang = "sized"]
trait Sized {}

#[rustc_builtin_macro]
macro_rules! asm {
() => {};
}

// CHECK-LABEL: @cc_clobber
// CHECK: call void asm sideeffect "", "~{icc},~{fcc0},~{fcc1},~{fcc2},~{fcc3}"()
#[no_mangle]
pub unsafe fn cc_clobber() {
asm!("", options(nostack, nomem));
}

// CHECK-LABEL: @no_clobber
// CHECK: call void asm sideeffect "", ""()
#[no_mangle]
pub unsafe fn no_clobber() {
asm!("", options(nostack, nomem, preserves_flags));
}

// CHECK-LABEL: @y_clobber
// CHECK: call void asm sideeffect "", "~{y}"()
#[no_mangle]
pub unsafe fn y_clobber() {
asm!("", out("y") _, options(nostack, nomem, preserves_flags));
}
26 changes: 0 additions & 26 deletions tests/ui/asm/bad-arch.rs

This file was deleted.

15 changes: 0 additions & 15 deletions tests/ui/asm/bad-arch.stderr

This file was deleted.

66 changes: 66 additions & 0 deletions tests/ui/asm/sparc/bad-reg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//@ revisions: sparc sparcv8plus sparc64
//@[sparc] compile-flags: --target sparc-unknown-none-elf
//@[sparc] needs-llvm-components: sparc
//@[sparcv8plus] compile-flags: --target sparc-unknown-linux-gnu
//@[sparcv8plus] needs-llvm-components: sparc
//@[sparc64] compile-flags: --target sparc64-unknown-linux-gnu
//@[sparc64] needs-llvm-components: sparc
//@ needs-asm-support

#![crate_type = "rlib"]
#![feature(no_core, rustc_attrs, lang_items, asm_experimental_arch)]
#![no_core]

#[lang = "sized"]
trait Sized {}
#[lang = "copy"]
trait Copy {}

impl Copy for i32 {}

#[rustc_builtin_macro]
macro_rules! asm {
() => {};
}

fn f() {
let mut x = 0;
unsafe {
// Unsupported registers
asm!("", out("g0") _);
//~^ ERROR invalid register `g0`: g0 is always zero and cannot be used as an operand for inline asm
// FIXME: see FIXME in compiler/rustc_target/src/asm/sparc.rs.
asm!("", out("g1") _);
//~^ ERROR invalid register `g1`: reserved by LLVM and cannot be used as an operand for inline asm
asm!("", out("g2") _);
asm!("", out("g3") _);
asm!("", out("g4") _);
asm!("", out("g5") _);
//[sparc,sparcv8plus]~^ ERROR cannot use register `r5`: g5 is reserved for system on SPARC32
asm!("", out("g6") _);
//~^ ERROR invalid register `g6`: reserved for system and cannot be used as an operand for inline asm
asm!("", out("g7") _);
//~^ ERROR invalid register `g7`: reserved for system and cannot be used as an operand for inline asm
asm!("", out("sp") _);
//~^ ERROR invalid register `sp`: the stack pointer cannot be used as an operand for inline asm
asm!("", out("fp") _);
//~^ ERROR invalid register `fp`: the frame pointer cannot be used as an operand for inline asm
asm!("", out("i7") _);
//~^ ERROR invalid register `i7`: the return address register cannot be used as an operand for inline asm

// Clobber-only registers
// yreg
asm!("", out("y") _); // ok
asm!("", in("y") x);
//~^ ERROR can only be used as a clobber
//~| ERROR type `i32` cannot be used with this register class
asm!("", out("y") x);
//~^ ERROR can only be used as a clobber
//~| ERROR type `i32` cannot be used with this register class
asm!("/* {} */", in(yreg) x);
//~^ ERROR can only be used as a clobber
//~| ERROR type `i32` cannot be used with this register class
asm!("/* {} */", out(yreg) _);
//~^ ERROR can only be used as a clobber
}
}
98 changes: 98 additions & 0 deletions tests/ui/asm/sparc/bad-reg.sparc.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
error: invalid register `g0`: g0 is always zero and cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:30:18
|
LL | asm!("", out("g0") _);
| ^^^^^^^^^^^

error: invalid register `g1`: reserved by LLVM and cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:33:18
|
LL | asm!("", out("g1") _);
| ^^^^^^^^^^^

error: invalid register `g6`: reserved for system and cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:40:18
|
LL | asm!("", out("g6") _);
| ^^^^^^^^^^^

error: invalid register `g7`: reserved for system and cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:42:18
|
LL | asm!("", out("g7") _);
| ^^^^^^^^^^^

error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:44:18
|
LL | asm!("", out("sp") _);
| ^^^^^^^^^^^

error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:46:18
|
LL | asm!("", out("fp") _);
| ^^^^^^^^^^^

error: invalid register `i7`: the return address register cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:48:18
|
LL | asm!("", out("i7") _);
| ^^^^^^^^^^^

error: register class `yreg` can only be used as a clobber, not as an input or output
--> $DIR/bad-reg.rs:54:18
|
LL | asm!("", in("y") x);
| ^^^^^^^^^

error: register class `yreg` can only be used as a clobber, not as an input or output
--> $DIR/bad-reg.rs:57:18
|
LL | asm!("", out("y") x);
| ^^^^^^^^^^

error: register class `yreg` can only be used as a clobber, not as an input or output
--> $DIR/bad-reg.rs:60:26
|
LL | asm!("/* {} */", in(yreg) x);
| ^^^^^^^^^^

error: register class `yreg` can only be used as a clobber, not as an input or output
--> $DIR/bad-reg.rs:63:26
|
LL | asm!("/* {} */", out(yreg) _);
| ^^^^^^^^^^^

error: cannot use register `r5`: g5 is reserved for system on SPARC32
--> $DIR/bad-reg.rs:38:18
|
LL | asm!("", out("g5") _);
| ^^^^^^^^^^^

error: type `i32` cannot be used with this register class
--> $DIR/bad-reg.rs:54:26
|
LL | asm!("", in("y") x);
| ^
|
= note: register class `yreg` supports these types:

error: type `i32` cannot be used with this register class
--> $DIR/bad-reg.rs:57:27
|
LL | asm!("", out("y") x);
| ^
|
= note: register class `yreg` supports these types:

error: type `i32` cannot be used with this register class
--> $DIR/bad-reg.rs:60:35
|
LL | asm!("/* {} */", in(yreg) x);
| ^
|
= note: register class `yreg` supports these types:

error: aborting due to 15 previous errors

92 changes: 92 additions & 0 deletions tests/ui/asm/sparc/bad-reg.sparc64.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
error: invalid register `g0`: g0 is always zero and cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:30:18
|
LL | asm!("", out("g0") _);
| ^^^^^^^^^^^

error: invalid register `g1`: reserved by LLVM and cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:33:18
|
LL | asm!("", out("g1") _);
| ^^^^^^^^^^^

error: invalid register `g6`: reserved for system and cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:40:18
|
LL | asm!("", out("g6") _);
| ^^^^^^^^^^^

error: invalid register `g7`: reserved for system and cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:42:18
|
LL | asm!("", out("g7") _);
| ^^^^^^^^^^^

error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:44:18
|
LL | asm!("", out("sp") _);
| ^^^^^^^^^^^

error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:46:18
|
LL | asm!("", out("fp") _);
| ^^^^^^^^^^^

error: invalid register `i7`: the return address register cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:48:18
|
LL | asm!("", out("i7") _);
| ^^^^^^^^^^^

error: register class `yreg` can only be used as a clobber, not as an input or output
--> $DIR/bad-reg.rs:54:18
|
LL | asm!("", in("y") x);
| ^^^^^^^^^

error: register class `yreg` can only be used as a clobber, not as an input or output
--> $DIR/bad-reg.rs:57:18
|
LL | asm!("", out("y") x);
| ^^^^^^^^^^

error: register class `yreg` can only be used as a clobber, not as an input or output
--> $DIR/bad-reg.rs:60:26
|
LL | asm!("/* {} */", in(yreg) x);
| ^^^^^^^^^^

error: register class `yreg` can only be used as a clobber, not as an input or output
--> $DIR/bad-reg.rs:63:26
|
LL | asm!("/* {} */", out(yreg) _);
| ^^^^^^^^^^^

error: type `i32` cannot be used with this register class
--> $DIR/bad-reg.rs:54:26
|
LL | asm!("", in("y") x);
| ^
|
= note: register class `yreg` supports these types:

error: type `i32` cannot be used with this register class
--> $DIR/bad-reg.rs:57:27
|
LL | asm!("", out("y") x);
| ^
|
= note: register class `yreg` supports these types:

error: type `i32` cannot be used with this register class
--> $DIR/bad-reg.rs:60:35
|
LL | asm!("/* {} */", in(yreg) x);
| ^
|
= note: register class `yreg` supports these types:

error: aborting due to 14 previous errors

98 changes: 98 additions & 0 deletions tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
error: invalid register `g0`: g0 is always zero and cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:30:18
|
LL | asm!("", out("g0") _);
| ^^^^^^^^^^^

error: invalid register `g1`: reserved by LLVM and cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:33:18
|
LL | asm!("", out("g1") _);
| ^^^^^^^^^^^

error: invalid register `g6`: reserved for system and cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:40:18
|
LL | asm!("", out("g6") _);
| ^^^^^^^^^^^

error: invalid register `g7`: reserved for system and cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:42:18
|
LL | asm!("", out("g7") _);
| ^^^^^^^^^^^

error: invalid register `sp`: the stack pointer cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:44:18
|
LL | asm!("", out("sp") _);
| ^^^^^^^^^^^

error: invalid register `fp`: the frame pointer cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:46:18
|
LL | asm!("", out("fp") _);
| ^^^^^^^^^^^

error: invalid register `i7`: the return address register cannot be used as an operand for inline asm
--> $DIR/bad-reg.rs:48:18
|
LL | asm!("", out("i7") _);
| ^^^^^^^^^^^

error: register class `yreg` can only be used as a clobber, not as an input or output
--> $DIR/bad-reg.rs:54:18
|
LL | asm!("", in("y") x);
| ^^^^^^^^^

error: register class `yreg` can only be used as a clobber, not as an input or output
--> $DIR/bad-reg.rs:57:18
|
LL | asm!("", out("y") x);
| ^^^^^^^^^^

error: register class `yreg` can only be used as a clobber, not as an input or output
--> $DIR/bad-reg.rs:60:26
|
LL | asm!("/* {} */", in(yreg) x);
| ^^^^^^^^^^

error: register class `yreg` can only be used as a clobber, not as an input or output
--> $DIR/bad-reg.rs:63:26
|
LL | asm!("/* {} */", out(yreg) _);
| ^^^^^^^^^^^

error: cannot use register `r5`: g5 is reserved for system on SPARC32
--> $DIR/bad-reg.rs:38:18
|
LL | asm!("", out("g5") _);
| ^^^^^^^^^^^

error: type `i32` cannot be used with this register class
--> $DIR/bad-reg.rs:54:26
|
LL | asm!("", in("y") x);
| ^
|
= note: register class `yreg` supports these types:

error: type `i32` cannot be used with this register class
--> $DIR/bad-reg.rs:57:27
|
LL | asm!("", out("y") x);
| ^
|
= note: register class `yreg` supports these types:

error: type `i32` cannot be used with this register class
--> $DIR/bad-reg.rs:60:35
|
LL | asm!("/* {} */", in(yreg) x);
| ^
|
= note: register class `yreg` supports these types:

error: aborting due to 15 previous errors