rustc_target/callconv/
sparc64.rs

1// FIXME: This needs an audit for correctness and completeness.
2
3use rustc_abi::{
4    BackendRepr, FieldsShape, Float, HasDataLayout, Primitive, Reg, Scalar, Size, TyAbiInterface,
5    TyAndLayout,
6};
7
8use crate::callconv::{
9    ArgAbi, ArgAttribute, ArgAttributes, ArgExtension, CastTarget, FnAbi, Uniform,
10};
11use crate::spec::HasTargetSpec;
12
13#[derive(Clone, Debug)]
14struct Sdata {
15    pub prefix: [Option<Reg>; 8],
16    pub prefix_index: usize,
17    pub last_offset: Size,
18    pub has_float: bool,
19    pub arg_attribute: ArgAttribute,
20}
21
22fn arg_scalar<C>(cx: &C, scalar: &Scalar, offset: Size, mut data: Sdata) -> Sdata
23where
24    C: HasDataLayout,
25{
26    let dl = cx.data_layout();
27
28    if !matches!(scalar.primitive(), Primitive::Float(Float::F32 | Float::F64)) {
29        return data;
30    }
31
32    data.has_float = true;
33
34    if !data.last_offset.is_aligned(dl.f64_align.abi) && data.last_offset < offset {
35        if data.prefix_index == data.prefix.len() {
36            return data;
37        }
38        data.prefix[data.prefix_index] = Some(Reg::i32());
39        data.prefix_index += 1;
40        data.last_offset = data.last_offset + Reg::i32().size;
41    }
42
43    for _ in 0..((offset - data.last_offset).bits() / 64)
44        .min((data.prefix.len() - data.prefix_index) as u64)
45    {
46        data.prefix[data.prefix_index] = Some(Reg::i64());
47        data.prefix_index += 1;
48        data.last_offset = data.last_offset + Reg::i64().size;
49    }
50
51    if data.last_offset < offset {
52        if data.prefix_index == data.prefix.len() {
53            return data;
54        }
55        data.prefix[data.prefix_index] = Some(Reg::i32());
56        data.prefix_index += 1;
57        data.last_offset = data.last_offset + Reg::i32().size;
58    }
59
60    if data.prefix_index == data.prefix.len() {
61        return data;
62    }
63
64    if scalar.primitive() == Primitive::Float(Float::F32) {
65        data.arg_attribute = ArgAttribute::InReg;
66        data.prefix[data.prefix_index] = Some(Reg::f32());
67        data.last_offset = offset + Reg::f32().size;
68    } else {
69        data.prefix[data.prefix_index] = Some(Reg::f64());
70        data.last_offset = offset + Reg::f64().size;
71    }
72    data.prefix_index += 1;
73    data
74}
75
76fn arg_scalar_pair<C>(
77    cx: &C,
78    scalar1: &Scalar,
79    scalar2: &Scalar,
80    mut offset: Size,
81    mut data: Sdata,
82) -> Sdata
83where
84    C: HasDataLayout,
85{
86    data = arg_scalar(cx, scalar1, offset, data);
87    match (scalar1.primitive(), scalar2.primitive()) {
88        (Primitive::Float(Float::F32), _) => offset += Reg::f32().size,
89        (_, Primitive::Float(Float::F64)) => offset += Reg::f64().size,
90        (Primitive::Int(i, _signed), _) => offset += i.size(),
91        (Primitive::Pointer(_), _) => offset += Reg::i64().size,
92        _ => {}
93    }
94
95    if (offset.bytes() % 4) != 0
96        && matches!(scalar2.primitive(), Primitive::Float(Float::F32 | Float::F64))
97    {
98        offset += Size::from_bytes(4 - (offset.bytes() % 4));
99    }
100    data = arg_scalar(cx, scalar2, offset, data);
101    data
102}
103
104fn parse_structure<'a, Ty, C>(
105    cx: &C,
106    layout: TyAndLayout<'a, Ty>,
107    mut data: Sdata,
108    mut offset: Size,
109) -> Sdata
110where
111    Ty: TyAbiInterface<'a, C> + Copy,
112    C: HasDataLayout,
113{
114    if let FieldsShape::Union(_) = layout.fields {
115        return data;
116    }
117
118    match layout.backend_repr {
119        BackendRepr::Scalar(scalar) => {
120            data = arg_scalar(cx, &scalar, offset, data);
121        }
122        BackendRepr::Memory { .. } => {
123            for i in 0..layout.fields.count() {
124                if offset < layout.fields.offset(i) {
125                    offset = layout.fields.offset(i);
126                }
127                data = parse_structure(cx, layout.field(cx, i), data.clone(), offset);
128            }
129        }
130        _ => {
131            if let BackendRepr::ScalarPair(scalar1, scalar2) = &layout.backend_repr {
132                data = arg_scalar_pair(cx, scalar1, scalar2, offset, data);
133            }
134        }
135    }
136
137    data
138}
139
140fn classify_arg<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, in_registers_max: Size)
141where
142    Ty: TyAbiInterface<'a, C> + Copy,
143    C: HasDataLayout,
144{
145    if !arg.layout.is_aggregate() {
146        arg.extend_integer_width_to(64);
147        return;
148    }
149
150    let total = arg.layout.size;
151    if total > in_registers_max {
152        arg.make_indirect();
153        return;
154    }
155
156    match arg.layout.fields {
157        FieldsShape::Primitive => unreachable!(),
158        FieldsShape::Array { .. } => {
159            // Arrays are passed indirectly
160            arg.make_indirect();
161            return;
162        }
163        FieldsShape::Union(_) => {
164            // Unions and are always treated as a series of 64-bit integer chunks
165        }
166        FieldsShape::Arbitrary { .. } => {
167            // Structures with floating point numbers need special care.
168
169            let mut data = parse_structure(
170                cx,
171                arg.layout,
172                Sdata {
173                    prefix: [None; 8],
174                    prefix_index: 0,
175                    last_offset: Size::ZERO,
176                    has_float: false,
177                    arg_attribute: ArgAttribute::default(),
178                },
179                Size::ZERO,
180            );
181
182            if data.has_float {
183                // Structure { float, int, int } doesn't like to be handled like
184                // { float, long int }. Other way around it doesn't mind.
185                if data.last_offset < arg.layout.size
186                    && (data.last_offset.bytes() % 8) != 0
187                    && data.prefix_index < data.prefix.len()
188                {
189                    data.prefix[data.prefix_index] = Some(Reg::i32());
190                    data.prefix_index += 1;
191                    data.last_offset += Reg::i32().size;
192                }
193
194                let mut rest_size = arg.layout.size - data.last_offset;
195                if (rest_size.bytes() % 8) != 0 && data.prefix_index < data.prefix.len() {
196                    data.prefix[data.prefix_index] = Some(Reg::i32());
197                    rest_size = rest_size - Reg::i32().size;
198                }
199
200                arg.cast_to(CastTarget {
201                    prefix: data.prefix,
202                    rest: Uniform::new(Reg::i64(), rest_size),
203                    attrs: ArgAttributes {
204                        regular: data.arg_attribute,
205                        arg_ext: ArgExtension::None,
206                        pointee_size: Size::ZERO,
207                        pointee_align: None,
208                    },
209                });
210                return;
211            }
212        }
213    }
214
215    arg.cast_to(Uniform::new(Reg::i64(), total));
216}
217
218pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
219where
220    Ty: TyAbiInterface<'a, C> + Copy,
221    C: HasDataLayout + HasTargetSpec,
222{
223    if !fn_abi.ret.is_ignore() {
224        classify_arg(cx, &mut fn_abi.ret, Size::from_bytes(32));
225    }
226
227    for arg in fn_abi.args.iter_mut() {
228        if arg.is_ignore() {
229            // sparc64-unknown-linux-{gnu,musl,uclibc} doesn't ignore ZSTs.
230            if cx.target_spec().os == "linux"
231                && matches!(&*cx.target_spec().env, "gnu" | "musl" | "uclibc")
232                && arg.layout.is_zst()
233            {
234                arg.make_indirect_from_ignore();
235            }
236            return;
237        }
238        classify_arg(cx, arg, Size::from_bytes(16));
239    }
240}