Skip to main content

rustc_target/callconv/
sparc64.rs

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