diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index b44d4aa8cc82b..6b067b35e7127 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -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, diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index a1ccf0d171957..bb74dfe148703 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -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(), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 21a74bd4020ac..cb107b0607e22 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2175,6 +2175,7 @@ symbols! { yes, yield_expr, ymm_reg, + yreg, zfh, zfhmin, zmm_reg, diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index dc602e76daadc..10778e9acf1fd 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -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); diff --git a/compiler/rustc_target/src/asm/sparc.rs b/compiler/rustc_target/src/asm/sparc.rs new file mode 100644 index 0000000000000..6261708642b28 --- /dev/null +++ b/compiler/rustc_target/src/asm/sparc.rs @@ -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 { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { + None + } + + pub fn supported_types( + self, + arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option)] { + 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, + _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, + ) -> fmt::Result { + write!(out, "%{}", self.name()) + } +} diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md index 5264f778a93b5..3029c3989c9fa 100644 --- a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md +++ b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md @@ -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`,
`i8x8`, `i16x4`, `i32x2`, `i64x1`, `f32x2`, `f64x1`,
`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). diff --git a/tests/assembly/asm/sparc-types.rs b/tests/assembly/asm/sparc-types.rs new file mode 100644 index 0000000000000..2270679e837e3 --- /dev/null +++ b/tests/assembly/asm/sparc-types.rs @@ -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"); diff --git a/tests/codegen/asm/sparc-clobbers.rs b/tests/codegen/asm/sparc-clobbers.rs new file mode 100644 index 0000000000000..843abd553523e --- /dev/null +++ b/tests/codegen/asm/sparc-clobbers.rs @@ -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)); +} diff --git a/tests/ui/asm/bad-arch.rs b/tests/ui/asm/bad-arch.rs deleted file mode 100644 index f84b9944b3666..0000000000000 --- a/tests/ui/asm/bad-arch.rs +++ /dev/null @@ -1,26 +0,0 @@ -//@ compile-flags: --target sparc-unknown-linux-gnu -//@ needs-llvm-components: sparc - -#![feature(no_core, lang_items, rustc_attrs)] -#![no_core] - -#[rustc_builtin_macro] -macro_rules! asm { - () => {}; -} -#[rustc_builtin_macro] -macro_rules! global_asm { - () => {}; -} -#[lang = "sized"] -trait Sized {} - -fn main() { - unsafe { - asm!(""); - //~^ ERROR inline assembly is unsupported on this target - } -} - -global_asm!(""); -//~^ ERROR inline assembly is unsupported on this target diff --git a/tests/ui/asm/bad-arch.stderr b/tests/ui/asm/bad-arch.stderr deleted file mode 100644 index c6f726600eb4e..0000000000000 --- a/tests/ui/asm/bad-arch.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0472]: inline assembly is unsupported on this target - --> $DIR/bad-arch.rs:20:9 - | -LL | asm!(""); - | ^^^^^^^^ - -error[E0472]: inline assembly is unsupported on this target - --> $DIR/bad-arch.rs:25:1 - | -LL | global_asm!(""); - | ^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0472`. diff --git a/tests/ui/asm/sparc/bad-reg.rs b/tests/ui/asm/sparc/bad-reg.rs new file mode 100644 index 0000000000000..b824f5adf3a18 --- /dev/null +++ b/tests/ui/asm/sparc/bad-reg.rs @@ -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 + } +} diff --git a/tests/ui/asm/sparc/bad-reg.sparc.stderr b/tests/ui/asm/sparc/bad-reg.sparc.stderr new file mode 100644 index 0000000000000..cb7558c0f43f3 --- /dev/null +++ b/tests/ui/asm/sparc/bad-reg.sparc.stderr @@ -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 + diff --git a/tests/ui/asm/sparc/bad-reg.sparc64.stderr b/tests/ui/asm/sparc/bad-reg.sparc64.stderr new file mode 100644 index 0000000000000..e5606ab312448 --- /dev/null +++ b/tests/ui/asm/sparc/bad-reg.sparc64.stderr @@ -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 + diff --git a/tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr b/tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr new file mode 100644 index 0000000000000..cb7558c0f43f3 --- /dev/null +++ b/tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr @@ -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 +