about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-11-07 21:07:06 +0000
committerbors <bors@rust-lang.org>2024-11-07 21:07:06 +0000
commitb91a3a05609a46f73d23e0995ae7ebb4a4f429a5 (patch)
tree042c9f3e7be8d29c741e1fac9f28f2ad45b9d228
parent57a8a7efdb898966282e3f57528c3b9ea4d9bf49 (diff)
parent241f82ad915b167992ec9d3bb729f095a7829424 (diff)
downloadrust-b91a3a05609a46f73d23e0995ae7ebb4a4f429a5.tar.gz
rust-b91a3a05609a46f73d23e0995ae7ebb4a4f429a5.zip
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 https://github.com/gcc-mirror/gcc/blob/63b6967b06b5387821c4e5f2c113da6aaeeae2b7/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
-rw-r--r--compiler/rustc_codegen_gcc/src/asm.rs5
-rw-r--r--compiler/rustc_codegen_llvm/src/asm.rs14
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_target/src/asm/mod.rs30
-rw-r--r--compiler/rustc_target/src/asm/sparc.rs138
-rw-r--r--src/doc/unstable-book/src/language-features/asm-experimental-arch.md22
-rw-r--r--tests/assembly/asm/sparc-types.rs168
-rw-r--r--tests/codegen/asm/sparc-clobbers.rs40
-rw-r--r--tests/ui/asm/bad-arch.rs26
-rw-r--r--tests/ui/asm/bad-arch.stderr15
-rw-r--r--tests/ui/asm/sparc/bad-reg.rs66
-rw-r--r--tests/ui/asm/sparc/bad-reg.sparc.stderr98
-rw-r--r--tests/ui/asm/sparc/bad-reg.sparc64.stderr92
-rw-r--r--tests/ui/asm/sparc/bad-reg.sparcv8plus.stderr98
14 files changed, 770 insertions, 43 deletions
diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs
index b44d4aa8cc8..6b067b35e71 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 a1ccf0d1719..bb74dfe1487 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 21a74bd4020..cb107b0607e 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 dc602e76daa..10778e9acf1 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 00000000000..6261708642b
--- /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<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())
+    }
+}
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 5264f778a93..3029c3989c9 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`, <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).
diff --git a/tests/assembly/asm/sparc-types.rs b/tests/assembly/asm/sparc-types.rs
new file mode 100644
index 00000000000..2270679e837
--- /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 00000000000..843abd55352
--- /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 f84b9944b36..00000000000
--- 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 c6f726600eb..00000000000
--- 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 00000000000..b824f5adf3a
--- /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 00000000000..cb7558c0f43
--- /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 00000000000..e5606ab3124
--- /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 00000000000..cb7558c0f43
--- /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
+