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