Skip to main content

rustc_target/callconv/
sparc64.rs

1use arrayvec::ArrayVec;
2use rustc_abi::{
3    Align, BackendRepr, FieldsShape, Float, HasDataLayout, Integer, Numeric, Primitive, Reg,
4    RegKind, Size, TyAbiInterface, TyAndLayout, Variants,
5};
6
7use crate::callconv::{ArgAbi, ArgAttribute, CastTarget, FnAbi, Uniform};
8use crate::spec::{HasTargetSpec, Os};
9
10// NOTE: GCC and Clang/LLVM have disagreements that the ABI doesn't resolve, we match the
11// Clang/LLVM behavior in these cases.
12
13#[derive(#[automatically_derived]
impl ::core::marker::Copy for DoubleWord { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for DoubleWord { }
#[automatically_derived]
impl ::core::clone::Clone for DoubleWord {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<[Word; 2]>;
        *self
    }
}Clone)]
14enum DoubleWord {
15    F64,
16    F128Start,
17    F128End,
18    Words([Word; 2]),
19}
20
21#[derive(#[automatically_derived]
impl ::core::marker::Copy for Word { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for Word { }
#[automatically_derived]
impl ::core::clone::Clone for Word {
    #[inline]
    fn clone(&self) -> Self { *self }
}Clone)]
22enum Word {
23    F32,
24    Integer,
25}
26
27fn classify<'a, Ty, C>(
28    cx: &C,
29    arg_layout: &TyAndLayout<'a, Ty>,
30    offset: Size,
31    double_words: &mut [DoubleWord; 4],
32) where
33    Ty: TyAbiInterface<'a, C> + Copy,
34    C: HasDataLayout,
35{
36    // If this function does not update the `double_words` array, the value will be passed via
37    // integer registers. The array is initialized with `DoubleWord::Words([Word::Integer; 2])`.
38
39    match arg_layout.backend_repr {
40        BackendRepr::Scalar(scalar) => match scalar.primitive() {
41            Primitive::Float(float) => {
42                if offset.is_aligned(Ord::min(*float.align(cx), Align::EIGHT)) {
43                    let index = offset.bytes_usize() / 8;
44                    match float {
45                        Float::F128 => {
46                            double_words[index] = DoubleWord::F128Start;
47                            double_words[index + 1] = DoubleWord::F128End;
48                        }
49                        Float::F64 => {
50                            double_words[index] = DoubleWord::F64;
51                        }
52                        Float::F32 => match &mut double_words[index] {
53                            DoubleWord::Words(words) => {
54                                words[(offset.bytes_usize() % 8) / 4] = Word::F32;
55                            }
56                            _ => ::core::panicking::unreachable_default_msg()unreachable!(),
57                        },
58                        Float::F16 => {
59                            // Match LLVM by passing `f16` in integer registers.
60                        }
61                        Float::F16B => {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("`f16b` unsupported on sparc64")));
}unreachable!("`f16b` unsupported on sparc64"),
62                        Float::PpcF128 => {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("`ppcf128` unsupported on sparc64")));
}unreachable!("`ppcf128` unsupported on sparc64"),
63                    }
64                } else {
65                    /* pass unaligned floats in integer registers */
66                }
67            }
68            Primitive::Int(_, _) | Primitive::Pointer(_) => { /* pass in integer registers */ }
69        },
70        BackendRepr::SimdVector { .. } => {}
71        BackendRepr::SimdScalableVector { .. } => {}
72        BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => match arg_layout.fields {
73            FieldsShape::Primitive => {
74                {
    ::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
            format_args!("aggregates can\'t have `FieldsShape::Primitive`")));
}unreachable!("aggregates can't have `FieldsShape::Primitive`")
75            }
76            FieldsShape::Union(_) => {
77                if !arg_layout.is_zst() {
78                    if arg_layout.is_transparent() {
79                        let non_1zst_elem = arg_layout.non_1zst_field(cx).expect("not exactly one non-1-ZST field in non-ZST repr(transparent) union").1;
80                        classify(cx, &non_1zst_elem, offset, double_words);
81                    }
82                }
83            }
84            FieldsShape::Array { .. } => {}
85            FieldsShape::Arbitrary { .. } => match arg_layout.variants {
86                Variants::Multiple { .. } => {}
87                Variants::Single { .. } | Variants::Empty => {
88                    // Match Clang by ignoring whether a struct is packed and just considering
89                    // whether individual fields are aligned. GCC currently uses only integer
90                    // registers when passing packed structs.
91                    for i in arg_layout.fields.index_by_increasing_offset() {
92                        classify(
93                            cx,
94                            &arg_layout.field(cx, i),
95                            offset + arg_layout.fields.offset(i),
96                            double_words,
97                        );
98                    }
99                }
100            },
101        },
102    }
103}
104
105fn classify_arg<'a, Ty, C>(
106    cx: &C,
107    arg: &mut ArgAbi<'a, Ty>,
108    in_registers_max: Size,
109    total_double_word_count: &mut usize,
110) where
111    Ty: TyAbiInterface<'a, C> + Copy,
112    C: HasDataLayout,
113{
114    // 64-bit SPARC allocates argument stack space in 64-bit chunks (double words), some of which
115    // are promoted to registers based on their position on the stack.
116
117    // Keep track of the total number of double words used by arguments so far. This allows padding
118    // arguments to be inserted where necessary to ensure that 16-aligned arguments are passed in an
119    // aligned set of registers.
120
121    let pad = !total_double_word_count.is_multiple_of(2) && arg.layout.align.abi.bytes() == 16;
122    // The number of double words used by this argument.
123    let double_word_count = arg.layout.size.bytes_usize().div_ceil(8);
124    // The number of double words before this argument, including any padding.
125    let start_double_word_count = *total_double_word_count + usize::from(pad);
126
127    if arg.layout.pass_indirectly_in_non_rustic_abis(cx) {
128        arg.make_indirect();
129        *total_double_word_count += 1;
130        return;
131    }
132
133    if !arg.layout.is_aggregate() {
134        arg.extend_integer_width_to(64);
135        *total_double_word_count = start_double_word_count + double_word_count;
136        return;
137    }
138
139    let total = arg.layout.size;
140    if total > in_registers_max {
141        arg.make_indirect();
142        *total_double_word_count += 1;
143        return;
144    }
145
146    *total_double_word_count = start_double_word_count + double_word_count;
147
148    // Compatible with GCC and Clang 24.
149    if let Some(Numeric::Int(Integer::I8 | Integer::I16, _)) = arg.layout.complex_number(cx) {
150        arg.cast_to(Reg { kind: RegKind::Integer, size: total });
151        return;
152    }
153
154    const ARGUMENT_REGISTERS: usize = 8;
155
156    let mut double_words = [DoubleWord::Words([Word::Integer; 2]); ARGUMENT_REGISTERS / 2];
157    classify(cx, &arg.layout, Size::ZERO, &mut double_words);
158
159    let mut regs = ArrayVec::new();
160    let mut attrs = ArgAttribute::empty();
161
162    for (index, double_word) in double_words.into_iter().enumerate() {
163        if arg.layout.size.bytes_usize() <= index * 8 {
164            break;
165        }
166        match double_word {
167            // `f128` must be aligned to be assigned a float register.
168            DoubleWord::F128Start if (start_double_word_count + index).is_multiple_of(2) => {
169                regs.push(Reg::f128());
170            }
171            DoubleWord::F128Start => {
172                // Clang currently handles this case nonsensically, always returning a packed
173                // `struct { long double x; }` in an aligned quad floating-point register even when
174                // the `long double` isn't aligned on the stack, which also makes all future
175                // arguments get passed in the wrong registers. This passes the `f128` in integer
176                // registers when it is unaligned, same as with `f32` and `f64`.
177                regs.push(Reg::i64());
178                regs.push(Reg::i64());
179            }
180            DoubleWord::F128End => {} // Already handled by `F128Start`
181            DoubleWord::F64 => regs.push(Reg::f64()),
182            DoubleWord::Words([Word::Integer, Word::Integer]) => regs.push(Reg::i64()),
183            DoubleWord::Words(words) => {
184                attrs |= ArgAttribute::InReg;
185                for word in words {
186                    match word {
187                        Word::F32 => regs.push(Reg::f32()),
188                        Word::Integer => regs.push(Reg::i32()),
189                    }
190                }
191            }
192        }
193    }
194
195    let cast_target = match regs.as_slice() {
196        // Just a single register is needed for this value.
197        [reg] => CastTarget::from(*reg),
198        _ => CastTarget::prefixed(regs, Uniform::new(Reg::i8(), Size::ZERO)),
199    };
200
201    arg.cast_to_and_pad_i32(cast_target.with_attrs(attrs.into()), u8::from(pad));
202}
203
204pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
205where
206    Ty: TyAbiInterface<'a, C> + Copy,
207    C: HasDataLayout + HasTargetSpec,
208{
209    if !fn_abi.ret.is_ignore() && fn_abi.ret.layout.is_sized() {
210        // A return value of 32 bytes or smaller is passed via registers.
211        classify_arg(cx, &mut fn_abi.ret, Size::from_bytes(32), &mut 0);
212    }
213
214    // sparc64-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs.
215    let passes_zsts = #[allow(non_exhaustive_omitted_patterns)] match cx.target_spec().os {
    Os::Linux => true,
    _ => false,
}matches!(cx.target_spec().os, Os::Linux);
216
217    let mut double_word_count = 0;
218    for arg in fn_abi.args.iter_mut() {
219        if !arg.layout.is_sized() {
220            continue;
221        }
222        if arg.is_ignore() {
223            if passes_zsts && arg.layout.is_repr_c() {
224                arg.make_indirect_from_ignore();
225                double_word_count += 1;
226            }
227            continue;
228        }
229        // An argument of 16 bytes or smaller is passed via registers.
230        classify_arg(cx, arg, Size::from_bytes(16), &mut double_word_count);
231    }
232}