1use rustc_abi::{
2 BackendRepr, FieldsShape, HasDataLayout, Primitive, Reg, RegKind, Size, TyAbiInterface,
3 TyAndLayout, Variants,
4};
5
6use crate::callconv::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Uniform};
7use crate::spec::HasTargetSpec;
8
9#[derive(Copy, Clone)]
10enum RegPassKind {
11 Float { offset_from_start: Size, ty: Reg },
12 Integer { offset_from_start: Size, ty: Reg },
13 Unknown,
14}
15
16#[derive(Copy, Clone)]
17enum FloatConv {
18 FloatPair { first_ty: Reg, second_ty_offset_from_start: Size, second_ty: Reg },
19 Float(Reg),
20 MixedPair { first_ty: Reg, second_ty_offset_from_start: Size, second_ty: Reg },
21}
22
23#[derive(Copy, Clone)]
24struct CannotUseFpConv;
25
26fn is_loongarch_aggregate<Ty>(arg: &ArgAbi<'_, Ty>) -> bool {
27 match arg.layout.backend_repr {
28 BackendRepr::SimdVector { .. } => true,
29 _ => arg.layout.is_aggregate(),
30 }
31}
32
33fn should_use_fp_conv_helper<'a, Ty, C>(
34 cx: &C,
35 arg_layout: &TyAndLayout<'a, Ty>,
36 xlen: u64,
37 flen: u64,
38 field1_kind: &mut RegPassKind,
39 field2_kind: &mut RegPassKind,
40 offset_from_start: Size,
41) -> Result<(), CannotUseFpConv>
42where
43 Ty: TyAbiInterface<'a, C> + Copy,
44{
45 match arg_layout.backend_repr {
46 BackendRepr::Scalar(scalar) => match scalar.primitive() {
47 Primitive::Int(..) | Primitive::Pointer(_) => {
48 if arg_layout.size.bits() > xlen {
49 return Err(CannotUseFpConv);
50 }
51 match (*field1_kind, *field2_kind) {
52 (RegPassKind::Unknown, _) => {
53 *field1_kind = RegPassKind::Integer {
54 offset_from_start,
55 ty: Reg { kind: RegKind::Integer, size: arg_layout.size },
56 };
57 }
58 (RegPassKind::Float { .. }, RegPassKind::Unknown) => {
59 *field2_kind = RegPassKind::Integer {
60 offset_from_start,
61 ty: Reg { kind: RegKind::Integer, size: arg_layout.size },
62 };
63 }
64 _ => return Err(CannotUseFpConv),
65 }
66 }
67 Primitive::Float(_) => {
68 if arg_layout.size.bits() > flen {
69 return Err(CannotUseFpConv);
70 }
71 match (*field1_kind, *field2_kind) {
72 (RegPassKind::Unknown, _) => {
73 *field1_kind = RegPassKind::Float {
74 offset_from_start,
75 ty: Reg { kind: RegKind::Float, size: arg_layout.size },
76 };
77 }
78 (_, RegPassKind::Unknown) => {
79 *field2_kind = RegPassKind::Float {
80 offset_from_start,
81 ty: Reg { kind: RegKind::Float, size: arg_layout.size },
82 };
83 }
84 _ => return Err(CannotUseFpConv),
85 }
86 }
87 },
88 BackendRepr::SimdVector { .. } => return Err(CannotUseFpConv),
89 BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => match arg_layout.fields {
90 FieldsShape::Primitive => {
91 unreachable!("aggregates can't have `FieldsShape::Primitive`")
92 }
93 FieldsShape::Union(_) => {
94 if !arg_layout.is_zst() {
95 if arg_layout.is_transparent() {
96 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;
97 return should_use_fp_conv_helper(
98 cx,
99 &non_1zst_elem,
100 xlen,
101 flen,
102 field1_kind,
103 field2_kind,
104 offset_from_start,
105 );
106 }
107 return Err(CannotUseFpConv);
108 }
109 }
110 FieldsShape::Array { count, .. } => {
111 for i in 0..count {
112 let elem_layout = arg_layout.field(cx, 0);
113 should_use_fp_conv_helper(
114 cx,
115 &elem_layout,
116 xlen,
117 flen,
118 field1_kind,
119 field2_kind,
120 offset_from_start + elem_layout.size * i,
121 )?;
122 }
123 }
124 FieldsShape::Arbitrary { .. } => {
125 match arg_layout.variants {
126 Variants::Multiple { .. } => return Err(CannotUseFpConv),
127 Variants::Single { .. } | Variants::Empty => (),
128 }
129 for i in arg_layout.fields.index_by_increasing_offset() {
130 let field = arg_layout.field(cx, i);
131 should_use_fp_conv_helper(
132 cx,
133 &field,
134 xlen,
135 flen,
136 field1_kind,
137 field2_kind,
138 offset_from_start + arg_layout.fields.offset(i),
139 )?;
140 }
141 }
142 },
143 }
144 Ok(())
145}
146
147fn should_use_fp_conv<'a, Ty, C>(
148 cx: &C,
149 arg: &TyAndLayout<'a, Ty>,
150 xlen: u64,
151 flen: u64,
152) -> Option<FloatConv>
153where
154 Ty: TyAbiInterface<'a, C> + Copy,
155{
156 let mut field1_kind = RegPassKind::Unknown;
157 let mut field2_kind = RegPassKind::Unknown;
158 if should_use_fp_conv_helper(
159 cx,
160 arg,
161 xlen,
162 flen,
163 &mut field1_kind,
164 &mut field2_kind,
165 Size::ZERO,
166 )
167 .is_err()
168 {
169 return None;
170 }
171 match (field1_kind, field2_kind) {
172 (
173 RegPassKind::Integer { offset_from_start, .. }
174 | RegPassKind::Float { offset_from_start, .. },
175 _,
176 ) if offset_from_start != Size::ZERO => {
177 panic!("type {:?} has a first field with non-zero offset {offset_from_start:?}", arg.ty)
178 }
179 (
180 RegPassKind::Integer { ty: first_ty, .. },
181 RegPassKind::Float { offset_from_start, ty: second_ty },
182 ) => Some(FloatConv::MixedPair {
183 first_ty,
184 second_ty_offset_from_start: offset_from_start,
185 second_ty,
186 }),
187 (
188 RegPassKind::Float { ty: first_ty, .. },
189 RegPassKind::Integer { offset_from_start, ty: second_ty },
190 ) => Some(FloatConv::MixedPair {
191 first_ty,
192 second_ty_offset_from_start: offset_from_start,
193 second_ty,
194 }),
195 (
196 RegPassKind::Float { ty: first_ty, .. },
197 RegPassKind::Float { offset_from_start, ty: second_ty },
198 ) => Some(FloatConv::FloatPair {
199 first_ty,
200 second_ty_offset_from_start: offset_from_start,
201 second_ty,
202 }),
203 (RegPassKind::Float { ty, .. }, RegPassKind::Unknown) => Some(FloatConv::Float(ty)),
204 _ => None,
205 }
206}
207
208fn classify_ret<'a, Ty, C>(cx: &C, arg: &mut ArgAbi<'a, Ty>, xlen: u64, flen: u64) -> bool
209where
210 Ty: TyAbiInterface<'a, C> + Copy,
211{
212 if !arg.layout.is_sized() {
213 return false; }
216 if let Some(conv) = should_use_fp_conv(cx, &arg.layout, xlen, flen) {
217 match conv {
218 FloatConv::Float(f) => {
219 arg.cast_to(f);
220 }
221 FloatConv::FloatPair { first_ty, second_ty_offset_from_start, second_ty } => {
222 arg.cast_to(CastTarget::offset_pair(
223 first_ty,
224 second_ty_offset_from_start,
225 second_ty,
226 ));
227 }
228 FloatConv::MixedPair { first_ty, second_ty_offset_from_start, second_ty } => {
229 arg.cast_to(CastTarget::offset_pair(
230 first_ty,
231 second_ty_offset_from_start,
232 second_ty,
233 ));
234 }
235 }
236 return false;
237 }
238
239 let total = arg.layout.size;
240
241 if total.bits() > 2 * xlen {
247 if is_loongarch_aggregate(arg) {
249 arg.make_indirect();
250 }
251 return true;
252 }
253
254 let xlen_reg = match xlen {
255 32 => Reg::i32(),
256 64 => Reg::i64(),
257 _ => unreachable!("Unsupported XLEN: {}", xlen),
258 };
259 if is_loongarch_aggregate(arg) {
260 if total.bits() <= xlen {
261 arg.cast_to(xlen_reg);
262 } else {
263 arg.cast_to(Uniform::new(xlen_reg, Size::from_bits(xlen * 2)));
264 }
265 return false;
266 }
267
268 extend_integer_width(arg, xlen);
272 false
273}
274
275fn classify_arg<'a, Ty, C>(
276 cx: &C,
277 arg: &mut ArgAbi<'a, Ty>,
278 xlen: u64,
279 flen: u64,
280 is_vararg: bool,
281 avail_gprs: &mut u64,
282 avail_fprs: &mut u64,
283) where
284 Ty: TyAbiInterface<'a, C> + Copy,
285{
286 if !arg.layout.is_sized() {
287 return;
289 }
290 if !is_vararg {
291 match should_use_fp_conv(cx, &arg.layout, xlen, flen) {
292 Some(FloatConv::Float(f)) if *avail_fprs >= 1 => {
293 *avail_fprs -= 1;
294 arg.cast_to(f);
295 return;
296 }
297 Some(FloatConv::FloatPair { first_ty, second_ty_offset_from_start, second_ty })
298 if *avail_fprs >= 2 =>
299 {
300 *avail_fprs -= 2;
301 arg.cast_to(CastTarget::offset_pair(
302 first_ty,
303 second_ty_offset_from_start,
304 second_ty,
305 ));
306 return;
307 }
308 Some(FloatConv::MixedPair { first_ty, second_ty_offset_from_start, second_ty })
309 if *avail_fprs >= 1 && *avail_gprs >= 1 =>
310 {
311 *avail_gprs -= 1;
312 *avail_fprs -= 1;
313 arg.cast_to(CastTarget::offset_pair(
314 first_ty,
315 second_ty_offset_from_start,
316 second_ty,
317 ));
318 return;
319 }
320 _ => (),
321 }
322 }
323
324 let total = arg.layout.size;
325 let align = arg.layout.align.abi.bits();
326
327 if total.bits() > 2 * xlen {
333 if is_loongarch_aggregate(arg) {
335 arg.make_indirect();
336 }
337 if *avail_gprs >= 1 {
338 *avail_gprs -= 1;
339 }
340 return;
341 }
342
343 let double_xlen_reg = match xlen {
344 32 => Reg::i64(),
345 64 => Reg::i128(),
346 _ => unreachable!("Unsupported XLEN: {}", xlen),
347 };
348
349 let xlen_reg = match xlen {
350 32 => Reg::i32(),
351 64 => Reg::i64(),
352 _ => unreachable!("Unsupported XLEN: {}", xlen),
353 };
354
355 if total.bits() > xlen {
356 let align_regs = align > xlen;
357 if is_loongarch_aggregate(arg) {
358 arg.cast_to(Uniform::new(
359 if align_regs { double_xlen_reg } else { xlen_reg },
360 Size::from_bits(xlen * 2),
361 ));
362 }
363 if align_regs && is_vararg {
364 *avail_gprs -= *avail_gprs % 2;
365 }
366 if *avail_gprs >= 2 {
367 *avail_gprs -= 2;
368 } else {
369 *avail_gprs = 0;
370 }
371 return;
372 } else if is_loongarch_aggregate(arg) {
373 arg.cast_to(xlen_reg);
374 if *avail_gprs >= 1 {
375 *avail_gprs -= 1;
376 }
377 return;
378 }
379
380 if *avail_gprs >= 1 {
384 extend_integer_width(arg, xlen);
385 *avail_gprs -= 1;
386 }
387}
388
389fn extend_integer_width<Ty>(arg: &mut ArgAbi<'_, Ty>, xlen: u64) {
390 if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr
391 && let Primitive::Int(i, _) = scalar.primitive()
392 && i.size().bits() == 32
393 && xlen > 32
394 && let PassMode::Direct(ref mut attrs) = arg.mode
395 {
396 attrs.ext(ArgExtension::Sext);
398 return;
399 }
400
401 arg.extend_integer_width_to(xlen);
402}
403
404pub(crate) fn compute_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
405where
406 Ty: TyAbiInterface<'a, C> + Copy,
407 C: HasDataLayout + HasTargetSpec,
408{
409 let xlen = cx.data_layout().pointer_size().bits();
410 let flen = match &cx.target_spec().llvm_abiname[..] {
411 "ilp32f" | "lp64f" => 32,
412 "ilp32d" | "lp64d" => 64,
413 _ => 0,
414 };
415
416 let mut avail_gprs = 8;
417 let mut avail_fprs = 8;
418
419 if !fn_abi.ret.is_ignore() && classify_ret(cx, &mut fn_abi.ret, xlen, flen) {
420 avail_gprs -= 1;
421 }
422
423 for (i, arg) in fn_abi.args.iter_mut().enumerate() {
424 if arg.is_ignore() {
425 continue;
426 }
427 classify_arg(
428 cx,
429 arg,
430 xlen,
431 flen,
432 i >= fn_abi.fixed_count as usize,
433 &mut avail_gprs,
434 &mut avail_fprs,
435 );
436 }
437}
438
439pub(crate) fn compute_rust_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
440where
441 Ty: TyAbiInterface<'a, C> + Copy,
442 C: HasDataLayout + HasTargetSpec,
443{
444 let grlen = cx.data_layout().pointer_size().bits();
445
446 for arg in fn_abi.args.iter_mut() {
447 if arg.is_ignore() {
448 continue;
449 }
450
451 extend_integer_width(arg, grlen);
459 }
460}