rustc_target/asm/
sparc.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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())
    }
}