1use 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 arg.make_indirect();
161 return;
162 }
163 FieldsShape::Union(_) => {
164 }
166 FieldsShape::Arbitrary { .. } => {
167 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 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 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}