Skip to content

Commit c963c3b

Browse files
committed
Basic inline assembly support for SPARC and SPARC64
1 parent a8e1186 commit c963c3b

File tree

15 files changed

+757
-4
lines changed

15 files changed

+757
-4
lines changed

Diff for: compiler/rustc_codegen_gcc/src/asm.rs

+5
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,8 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
687687
) => {
688688
unreachable!("clobber-only")
689689
}
690+
InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::reg) => "r",
691+
InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::yreg) => unreachable!("clobber-only"),
690692
InlineAsmRegClass::Err => unreachable!(),
691693
},
692694
};
@@ -765,6 +767,8 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
765767
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg) => {
766768
unreachable!("clobber-only")
767769
}
770+
InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::reg) => cx.type_i32(),
771+
InlineAsmRegClass::Sparc(SparcInlineAsmRegClass::yreg) => unreachable!("clobber-only"),
768772
InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => cx.type_i16(),
769773
InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => cx.type_i32(),
770774
InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg_addr) => cx.type_i32(),
@@ -944,6 +948,7 @@ fn modifier_to_gcc(
944948
},
945949
InlineAsmRegClass::Avr(_) => None,
946950
InlineAsmRegClass::S390x(_) => None,
951+
InlineAsmRegClass::Sparc(_) => None,
947952
InlineAsmRegClass::Msp430(_) => None,
948953
InlineAsmRegClass::M68k(_) => None,
949954
InlineAsmRegClass::CSKY(_) => None,

Diff for: compiler/rustc_codegen_llvm/src/asm.rs

+14
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,15 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
268268
InlineAsmArch::S390x => {
269269
constraints.push("~{cc}".to_string());
270270
}
271+
InlineAsmArch::Sparc | InlineAsmArch::Sparc64 => {
272+
// In LLVM, ~{icc} represents icc and xcc in 64-bit code.
273+
// https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/Sparc/SparcRegisterInfo.td#L64
274+
constraints.push("~{icc}".to_string());
275+
constraints.push("~{fcc0}".to_string());
276+
constraints.push("~{fcc1}".to_string());
277+
constraints.push("~{fcc2}".to_string());
278+
constraints.push("~{fcc3}".to_string());
279+
}
271280
InlineAsmArch::SpirV => {}
272281
InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => {}
273282
InlineAsmArch::Bpf => {}
@@ -670,6 +679,8 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) ->
670679
S390x(S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg) => {
671680
unreachable!("clobber-only")
672681
}
682+
Sparc(SparcInlineAsmRegClass::reg) => "r",
683+
Sparc(SparcInlineAsmRegClass::yreg) => unreachable!("clobber-only"),
673684
Msp430(Msp430InlineAsmRegClass::reg) => "r",
674685
M68k(M68kInlineAsmRegClass::reg) => "r",
675686
M68k(M68kInlineAsmRegClass::reg_addr) => "a",
@@ -763,6 +774,7 @@ fn modifier_to_llvm(
763774
},
764775
Avr(_) => None,
765776
S390x(_) => None,
777+
Sparc(_) => None,
766778
Msp430(_) => None,
767779
SpirV(SpirVInlineAsmRegClass::reg) => bug!("LLVM backend does not support SPIR-V"),
768780
M68k(_) => None,
@@ -831,6 +843,8 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &'
831843
S390x(S390xInlineAsmRegClass::vreg | S390xInlineAsmRegClass::areg) => {
832844
unreachable!("clobber-only")
833845
}
846+
Sparc(SparcInlineAsmRegClass::reg) => cx.type_i32(),
847+
Sparc(SparcInlineAsmRegClass::yreg) => unreachable!("clobber-only"),
834848
Msp430(Msp430InlineAsmRegClass::reg) => cx.type_i16(),
835849
M68k(M68kInlineAsmRegClass::reg) => cx.type_i32(),
836850
M68k(M68kInlineAsmRegClass::reg_addr) => cx.type_i32(),

Diff for: compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2175,6 +2175,7 @@ symbols! {
21752175
yes,
21762176
yield_expr,
21772177
ymm_reg,
2178+
yreg,
21782179
zfh,
21792180
zfhmin,
21802181
zmm_reg,

Diff for: compiler/rustc_target/src/asm/mod.rs

+30
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ mod nvptx;
191191
mod powerpc;
192192
mod riscv;
193193
mod s390x;
194+
mod sparc;
194195
mod spirv;
195196
mod wasm;
196197
mod x86;
@@ -209,6 +210,7 @@ pub use nvptx::{NvptxInlineAsmReg, NvptxInlineAsmRegClass};
209210
pub use powerpc::{PowerPCInlineAsmReg, PowerPCInlineAsmRegClass};
210211
pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass};
211212
pub use s390x::{S390xInlineAsmReg, S390xInlineAsmRegClass};
213+
pub use sparc::{SparcInlineAsmReg, SparcInlineAsmRegClass};
212214
pub use spirv::{SpirVInlineAsmReg, SpirVInlineAsmRegClass};
213215
pub use wasm::{WasmInlineAsmReg, WasmInlineAsmRegClass};
214216
pub use x86::{X86InlineAsmReg, X86InlineAsmRegClass};
@@ -230,6 +232,8 @@ pub enum InlineAsmArch {
230232
PowerPC,
231233
PowerPC64,
232234
S390x,
235+
Sparc,
236+
Sparc64,
233237
SpirV,
234238
Wasm32,
235239
Wasm64,
@@ -260,6 +264,8 @@ impl FromStr for InlineAsmArch {
260264
"mips" | "mips32r6" => Ok(Self::Mips),
261265
"mips64" | "mips64r6" => Ok(Self::Mips64),
262266
"s390x" => Ok(Self::S390x),
267+
"sparc" => Ok(Self::Sparc),
268+
"sparc64" => Ok(Self::Sparc64),
263269
"spirv" => Ok(Self::SpirV),
264270
"wasm32" => Ok(Self::Wasm32),
265271
"wasm64" => Ok(Self::Wasm64),
@@ -286,6 +292,7 @@ pub enum InlineAsmReg {
286292
LoongArch(LoongArchInlineAsmReg),
287293
Mips(MipsInlineAsmReg),
288294
S390x(S390xInlineAsmReg),
295+
Sparc(SparcInlineAsmReg),
289296
SpirV(SpirVInlineAsmReg),
290297
Wasm(WasmInlineAsmReg),
291298
Bpf(BpfInlineAsmReg),
@@ -309,6 +316,7 @@ impl InlineAsmReg {
309316
Self::LoongArch(r) => r.name(),
310317
Self::Mips(r) => r.name(),
311318
Self::S390x(r) => r.name(),
319+
Self::Sparc(r) => r.name(),
312320
Self::Bpf(r) => r.name(),
313321
Self::Avr(r) => r.name(),
314322
Self::Msp430(r) => r.name(),
@@ -329,6 +337,7 @@ impl InlineAsmReg {
329337
Self::LoongArch(r) => InlineAsmRegClass::LoongArch(r.reg_class()),
330338
Self::Mips(r) => InlineAsmRegClass::Mips(r.reg_class()),
331339
Self::S390x(r) => InlineAsmRegClass::S390x(r.reg_class()),
340+
Self::Sparc(r) => InlineAsmRegClass::Sparc(r.reg_class()),
332341
Self::Bpf(r) => InlineAsmRegClass::Bpf(r.reg_class()),
333342
Self::Avr(r) => InlineAsmRegClass::Avr(r.reg_class()),
334343
Self::Msp430(r) => InlineAsmRegClass::Msp430(r.reg_class()),
@@ -361,6 +370,9 @@ impl InlineAsmReg {
361370
Self::Mips(MipsInlineAsmReg::parse(name)?)
362371
}
363372
InlineAsmArch::S390x => Self::S390x(S390xInlineAsmReg::parse(name)?),
373+
InlineAsmArch::Sparc | InlineAsmArch::Sparc64 => {
374+
Self::Sparc(SparcInlineAsmReg::parse(name)?)
375+
}
364376
InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmReg::parse(name)?),
365377
InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => {
366378
Self::Wasm(WasmInlineAsmReg::parse(name)?)
@@ -393,6 +405,7 @@ impl InlineAsmReg {
393405
}
394406
Self::Mips(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
395407
Self::S390x(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
408+
Self::Sparc(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
396409
Self::Bpf(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
397410
Self::Avr(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
398411
Self::Msp430(r) => r.validate(arch, reloc_model, target_features, target, is_clobber),
@@ -420,6 +433,7 @@ impl InlineAsmReg {
420433
Self::LoongArch(r) => r.emit(out, arch, modifier),
421434
Self::Mips(r) => r.emit(out, arch, modifier),
422435
Self::S390x(r) => r.emit(out, arch, modifier),
436+
Self::Sparc(r) => r.emit(out, arch, modifier),
423437
Self::Bpf(r) => r.emit(out, arch, modifier),
424438
Self::Avr(r) => r.emit(out, arch, modifier),
425439
Self::Msp430(r) => r.emit(out, arch, modifier),
@@ -440,6 +454,7 @@ impl InlineAsmReg {
440454
Self::LoongArch(_) => cb(self),
441455
Self::Mips(_) => cb(self),
442456
Self::S390x(r) => r.overlapping_regs(|r| cb(Self::S390x(r))),
457+
Self::Sparc(_) => cb(self),
443458
Self::Bpf(r) => r.overlapping_regs(|r| cb(Self::Bpf(r))),
444459
Self::Avr(r) => r.overlapping_regs(|r| cb(Self::Avr(r))),
445460
Self::Msp430(_) => cb(self),
@@ -463,6 +478,7 @@ pub enum InlineAsmRegClass {
463478
LoongArch(LoongArchInlineAsmRegClass),
464479
Mips(MipsInlineAsmRegClass),
465480
S390x(S390xInlineAsmRegClass),
481+
Sparc(SparcInlineAsmRegClass),
466482
SpirV(SpirVInlineAsmRegClass),
467483
Wasm(WasmInlineAsmRegClass),
468484
Bpf(BpfInlineAsmRegClass),
@@ -487,6 +503,7 @@ impl InlineAsmRegClass {
487503
Self::LoongArch(r) => r.name(),
488504
Self::Mips(r) => r.name(),
489505
Self::S390x(r) => r.name(),
506+
Self::Sparc(r) => r.name(),
490507
Self::SpirV(r) => r.name(),
491508
Self::Wasm(r) => r.name(),
492509
Self::Bpf(r) => r.name(),
@@ -513,6 +530,7 @@ impl InlineAsmRegClass {
513530
Self::LoongArch(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::LoongArch),
514531
Self::Mips(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Mips),
515532
Self::S390x(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::S390x),
533+
Self::Sparc(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Sparc),
516534
Self::SpirV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::SpirV),
517535
Self::Wasm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Wasm),
518536
Self::Bpf(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Bpf),
@@ -542,6 +560,7 @@ impl InlineAsmRegClass {
542560
Self::LoongArch(r) => r.suggest_modifier(arch, ty),
543561
Self::Mips(r) => r.suggest_modifier(arch, ty),
544562
Self::S390x(r) => r.suggest_modifier(arch, ty),
563+
Self::Sparc(r) => r.suggest_modifier(arch, ty),
545564
Self::SpirV(r) => r.suggest_modifier(arch, ty),
546565
Self::Wasm(r) => r.suggest_modifier(arch, ty),
547566
Self::Bpf(r) => r.suggest_modifier(arch, ty),
@@ -571,6 +590,7 @@ impl InlineAsmRegClass {
571590
Self::LoongArch(r) => r.default_modifier(arch),
572591
Self::Mips(r) => r.default_modifier(arch),
573592
Self::S390x(r) => r.default_modifier(arch),
593+
Self::Sparc(r) => r.default_modifier(arch),
574594
Self::SpirV(r) => r.default_modifier(arch),
575595
Self::Wasm(r) => r.default_modifier(arch),
576596
Self::Bpf(r) => r.default_modifier(arch),
@@ -599,6 +619,7 @@ impl InlineAsmRegClass {
599619
Self::LoongArch(r) => r.supported_types(arch),
600620
Self::Mips(r) => r.supported_types(arch),
601621
Self::S390x(r) => r.supported_types(arch),
622+
Self::Sparc(r) => r.supported_types(arch),
602623
Self::SpirV(r) => r.supported_types(arch),
603624
Self::Wasm(r) => r.supported_types(arch),
604625
Self::Bpf(r) => r.supported_types(arch),
@@ -632,6 +653,9 @@ impl InlineAsmRegClass {
632653
Self::Mips(MipsInlineAsmRegClass::parse(name)?)
633654
}
634655
InlineAsmArch::S390x => Self::S390x(S390xInlineAsmRegClass::parse(name)?),
656+
InlineAsmArch::Sparc | InlineAsmArch::Sparc64 => {
657+
Self::Sparc(SparcInlineAsmRegClass::parse(name)?)
658+
}
635659
InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmRegClass::parse(name)?),
636660
InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => {
637661
Self::Wasm(WasmInlineAsmRegClass::parse(name)?)
@@ -658,6 +682,7 @@ impl InlineAsmRegClass {
658682
Self::LoongArch(r) => r.valid_modifiers(arch),
659683
Self::Mips(r) => r.valid_modifiers(arch),
660684
Self::S390x(r) => r.valid_modifiers(arch),
685+
Self::Sparc(r) => r.valid_modifiers(arch),
661686
Self::SpirV(r) => r.valid_modifiers(arch),
662687
Self::Wasm(r) => r.valid_modifiers(arch),
663688
Self::Bpf(r) => r.valid_modifiers(arch),
@@ -843,6 +868,11 @@ pub fn allocatable_registers(
843868
s390x::fill_reg_map(arch, reloc_model, target_features, target, &mut map);
844869
map
845870
}
871+
InlineAsmArch::Sparc | InlineAsmArch::Sparc64 => {
872+
let mut map = sparc::regclass_map();
873+
sparc::fill_reg_map(arch, reloc_model, target_features, target, &mut map);
874+
map
875+
}
846876
InlineAsmArch::SpirV => {
847877
let mut map = spirv::regclass_map();
848878
spirv::fill_reg_map(arch, reloc_model, target_features, target, &mut map);

Diff for: compiler/rustc_target/src/asm/sparc.rs

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
use std::fmt;
2+
3+
use rustc_data_structures::fx::FxIndexSet;
4+
use rustc_span::Symbol;
5+
6+
use super::{InlineAsmArch, InlineAsmType, ModifierInfo};
7+
use crate::spec::{RelocModel, Target};
8+
9+
def_reg_class! {
10+
Sparc SparcInlineAsmRegClass {
11+
reg,
12+
yreg,
13+
}
14+
}
15+
16+
impl SparcInlineAsmRegClass {
17+
pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] {
18+
&[]
19+
}
20+
21+
pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
22+
None
23+
}
24+
25+
pub fn suggest_modifier(
26+
self,
27+
_arch: InlineAsmArch,
28+
_ty: InlineAsmType,
29+
) -> Option<ModifierInfo> {
30+
None
31+
}
32+
33+
pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> {
34+
None
35+
}
36+
37+
pub fn supported_types(
38+
self,
39+
arch: InlineAsmArch,
40+
) -> &'static [(InlineAsmType, Option<Symbol>)] {
41+
match self {
42+
Self::reg => {
43+
// FIXME: i64 is ok for g*/o* registers on SPARC-V8+ ("h" constraint in GCC)
44+
if arch == InlineAsmArch::Sparc {
45+
types! { _: I8, I16, I32; }
46+
} else {
47+
types! { _: I8, I16, I32, I64; }
48+
}
49+
}
50+
Self::yreg => &[],
51+
}
52+
}
53+
}
54+
55+
pub(crate) fn is_v7_or_v8(arch: InlineAsmArch, target: &Target) -> bool {
56+
// FIXME: There are any number of CPUs that imply SPARC-V8+, and checking the cpu
57+
// string in the target specs is not the ideal approach here, but there
58+
// doesn't seem to be another good way to do this yet (https://github.com/rust-lang/rust/pull/131222).
59+
// However, in LLVM 20, feature "+v8plus" will be available for this:
60+
// https://github.com/llvm/llvm-project/commit/aca971d336d9c7650120fc0fd6dfe58866408216
61+
arch == InlineAsmArch::Sparc && target.options.cpu != "v9"
62+
}
63+
64+
fn reserved_g5(
65+
arch: InlineAsmArch,
66+
_reloc_model: RelocModel,
67+
_target_features: &FxIndexSet<Symbol>,
68+
target: &Target,
69+
_is_clobber: bool,
70+
) -> Result<(), &'static str> {
71+
if is_v7_or_v8(arch, target) { Err("g5 is reserved for system on SPARC-V7/V8") } else { Ok(()) }
72+
}
73+
74+
def_regs! {
75+
Sparc SparcInlineAsmReg SparcInlineAsmRegClass {
76+
// FIXME:
77+
// - LLVM has reserve-{g,o,l,i}N feature to reserve each general-purpose registers.
78+
// - g2-g4 are reserved for application (optional in both LLVM and GCC, and GCC has -mno-app-regs option to reserve them).
79+
// There are currently no builtin targets that use them, but in the future they may need to
80+
// be supported via options similar to AArch64's -Z fixed-x18.
81+
r2: reg = ["r2", "g2"], // % reserved_g2
82+
r3: reg = ["r3", "g3"], // % reserved_g3
83+
r4: reg = ["r4", "g4"], // % reserved_g4
84+
r5: reg = ["r5", "g5"] % reserved_g5,
85+
r8: reg = ["r8", "o0"], // % reserved_o0
86+
r9: reg = ["r9", "o1"], // % reserved_o1
87+
r10: reg = ["r10", "o2"], // % reserved_o2
88+
r11: reg = ["r11", "o3"], // % reserved_o3
89+
r12: reg = ["r12", "o4"], // % reserved_o4
90+
r13: reg = ["r13", "o5"], // % reserved_o5
91+
r15: reg = ["r15", "o7"], // % reserved_o7
92+
r16: reg = ["r16", "l0"], // % reserved_l0
93+
r17: reg = ["r17", "l1"], // % reserved_l1
94+
r18: reg = ["r18", "l2"], // % reserved_l2
95+
r19: reg = ["r19", "l3"], // % reserved_l3
96+
r20: reg = ["r20", "l4"], // % reserved_l4
97+
r21: reg = ["r21", "l5"], // % reserved_l5
98+
r22: reg = ["r22", "l6"], // % reserved_l6
99+
r23: reg = ["r23", "l7"], // % reserved_l7
100+
r24: reg = ["r24", "i0"], // % reserved_i0
101+
r25: reg = ["r25", "i1"], // % reserved_i1
102+
r26: reg = ["r26", "i2"], // % reserved_i2
103+
r27: reg = ["r27", "i3"], // % reserved_i3
104+
r28: reg = ["r28", "i4"], // % reserved_i4
105+
r29: reg = ["r29", "i5"], // % reserved_i5
106+
y: yreg = ["y"],
107+
#error = ["r0", "g0"] =>
108+
"g0 is always zero and cannot be used as an operand for inline asm",
109+
// FIXME: %g1 is volatile in ABI, but used internally by LLVM.
110+
// https://github.com/llvm/llvm-project/blob/llvmorg-19.1.0/llvm/lib/Target/Sparc/SparcRegisterInfo.cpp#L55-L56
111+
// > FIXME: G1 reserved for now for large imm generation by frame code.
112+
#error = ["r1", "g1"] =>
113+
"reserved by LLVM and cannot be used as an operand for inline asm",
114+
#error = ["r6", "g6", "r7", "g7"] =>
115+
"reserved for system and cannot be used as an operand for inline asm",
116+
#error = ["sp", "r14", "o6"] =>
117+
"the stack pointer cannot be used as an operand for inline asm",
118+
#error = ["fp", "r30", "i6"] =>
119+
"the frame pointer cannot be used as an operand for inline asm",
120+
#error = ["r31", "i7"] =>
121+
"the return address register cannot be used as an operand for inline asm",
122+
}
123+
}
124+
125+
impl SparcInlineAsmReg {
126+
pub fn emit(
127+
self,
128+
out: &mut dyn fmt::Write,
129+
_arch: InlineAsmArch,
130+
_modifier: Option<char>,
131+
) -> fmt::Result {
132+
write!(out, "%{}", self.name())
133+
}
134+
}

0 commit comments

Comments
 (0)