1use rustc_abi::{
2 Align, BackendRepr, CVariadicStatus, Endian, Float, HasDataLayout, Integer, Primitive, Size,
3};
4use rustc_codegen_ssa::common::IntPredicate;
5use rustc_codegen_ssa::mir::operand::OperandRef;
6use rustc_codegen_ssa::traits::{
7 BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods, LayoutTypeCodegenMethods,
8};
9use rustc_middle::bug;
10use rustc_middle::ty::Ty;
11use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
12use rustc_target::spec::{Arch, Env, LlvmAbi, RustcAbi};
13
14use crate::builder::Builder;
15use crate::llvm::Value;
16use crate::type_of::LayoutLlvmExt;
17
18fn round_up_to_alignment<'ll>(
19 bx: &mut Builder<'_, 'll, '_>,
20 mut value: &'ll Value,
21 align: Align,
22) -> &'ll Value {
23 value = bx.add(value, bx.cx().const_i32(align.bytes() as i32 - 1));
24 return bx.and(value, bx.cx().const_i32(-(align.bytes() as i32)));
25}
26
27fn round_pointer_up_to_alignment<'ll>(
28 bx: &mut Builder<'_, 'll, '_>,
29 addr: &'ll Value,
30 align: Align,
31) -> &'ll Value {
32 let ptr = bx.inbounds_ptradd(addr, bx.const_i32(align.bytes() as i32 - 1));
33 let pointer_width = bx.tcx().sess.target.pointer_width;
34 let mask = align.bytes().wrapping_neg() & (u64::MAX >> (64 - pointer_width));
35 bx.call_intrinsic(
36 "llvm.ptrmask",
37 &[bx.type_ptr(), bx.type_isize()],
38 &[ptr, bx.const_usize(mask)],
39 )
40}
41
42fn emit_direct_ptr_va_arg<'ll, 'tcx>(
43 bx: &mut Builder<'_, 'll, 'tcx>,
44 list: OperandRef<'tcx, &'ll Value>,
45 size: Size,
46 align: Align,
47 slot_size: Align,
48 allow_higher_align: bool,
49 force_right_adjust: bool,
50) -> (&'ll Value, Align) {
51 let va_list_ty = bx.type_ptr();
52 let va_list_addr = list.immediate();
53
54 let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi;
55 let ptr = bx.load(va_list_ty, va_list_addr, ptr_align_abi);
56
57 let (addr, addr_align) = if allow_higher_align && align > slot_size {
58 (round_pointer_up_to_alignment(bx, ptr, align), align)
59 } else {
60 (ptr, slot_size)
61 };
62
63 let aligned_size = size.align_to(slot_size).bytes() as i32;
64 let full_direct_size = bx.cx().const_i32(aligned_size);
65 let next = bx.inbounds_ptradd(addr, full_direct_size);
66 bx.store(next, va_list_addr, ptr_align_abi);
67
68 if size.bytes() < slot_size.bytes()
69 && bx.tcx().sess.target.endian == Endian::Big
70 && force_right_adjust
71 {
72 let adjusted_size = bx.cx().const_i32((slot_size.bytes() - size.bytes()) as i32);
73 let adjusted = bx.inbounds_ptradd(addr, adjusted_size);
74 (adjusted, align)
76 } else {
77 (addr, addr_align)
78 }
79}
80
81fn get_param_type_alignment<'ll, 'tcx>(
83 bx: &mut Builder<'_, 'll, 'tcx>,
84 layout: TyAndLayout<'tcx>,
85) -> Align {
86 let BackendRepr::Scalar(scalar) = layout.backend_repr else {
87 ::rustc_middle::util::bug::bug_fmt(format_args!("unexpected backend repr {0:?}",
layout.backend_repr));bug!("unexpected backend repr {:?}", layout.backend_repr);
88 };
89
90 match bx.cx.tcx.sess.target.arch {
91 Arch::PowerPC64 => match scalar.primitive() {
92 Primitive::Int(integer, _) => match integer {
93 Integer::I8 | Integer::I16 => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
94 Integer::I32 | Integer::I64 => { }
95 Integer::I128 => return Align::EIGHT,
96 },
97 Primitive::Float(float) => match float {
98 Float::F16 | Float::F32 => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
99 Float::F64 => { }
100 Float::F128 => return Align::from_bytes(16).unwrap(),
101 },
102 Primitive::Pointer(_) => { }
103 },
104 _ => { }
105 }
106
107 layout.align.abi
108}
109
110enum PassMode {
111 Direct,
112 Indirect,
113}
114
115enum SlotSize {
116 Bytes8 = 8,
117 Bytes4 = 4,
118 Bytes1 = 1,
119}
120
121enum AllowHigherAlign {
126 No,
127 Yes,
128}
129
130enum ForceRightAdjust {
146 No,
147 Yes,
148}
149
150fn emit_ptr_va_arg<'ll, 'tcx>(
151 bx: &mut Builder<'_, 'll, 'tcx>,
152 list: OperandRef<'tcx, &'ll Value>,
153 target_ty: Ty<'tcx>,
154 pass_mode: PassMode,
155 slot_size: SlotSize,
156 allow_higher_align: AllowHigherAlign,
157 force_right_adjust: ForceRightAdjust,
158) -> &'ll Value {
159 let indirect = #[allow(non_exhaustive_omitted_patterns)] match pass_mode {
PassMode::Indirect => true,
_ => false,
}matches!(pass_mode, PassMode::Indirect);
160 let allow_higher_align = #[allow(non_exhaustive_omitted_patterns)] match allow_higher_align {
AllowHigherAlign::Yes => true,
_ => false,
}matches!(allow_higher_align, AllowHigherAlign::Yes);
161 let force_right_adjust = #[allow(non_exhaustive_omitted_patterns)] match force_right_adjust {
ForceRightAdjust::Yes => true,
_ => false,
}matches!(force_right_adjust, ForceRightAdjust::Yes);
162 let slot_size = Align::from_bytes(slot_size as u64).unwrap();
163
164 let layout = bx.cx.layout_of(target_ty);
165 let (llty, size, align) = if indirect {
166 (
167 bx.cx.layout_of(Ty::new_imm_ptr(bx.cx.tcx, target_ty)).llvm_type(bx.cx),
168 bx.cx.data_layout().pointer_size(),
169 bx.cx.data_layout().pointer_align().abi,
170 )
171 } else {
172 (layout.llvm_type(bx.cx), layout.size, get_param_type_alignment(bx, layout))
173 };
174 let (addr, addr_align) = emit_direct_ptr_va_arg(
175 bx,
176 list,
177 size,
178 align,
179 slot_size,
180 allow_higher_align,
181 force_right_adjust,
182 );
183 if indirect {
184 let tmp_ret = bx.load(llty, addr, addr_align);
185 bx.load(layout.llvm_type(bx.cx), tmp_ret, align)
186 } else {
187 bx.load(llty, addr, addr_align)
188 }
189}
190
191fn emit_aapcs_va_arg<'ll, 'tcx>(
192 bx: &mut Builder<'_, 'll, 'tcx>,
193 list: OperandRef<'tcx, &'ll Value>,
194 target_ty: Ty<'tcx>,
195) -> &'ll Value {
196 let dl = bx.cx.data_layout();
197
198 let va_list_addr = list.immediate();
209
210 let ptr_offset = 8;
215 let i32_offset = 4;
216 let gr_top = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(ptr_offset));
217 let vr_top = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(2 * ptr_offset));
218 let gr_offs = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(3 * ptr_offset));
219 let vr_offs = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(3 * ptr_offset + i32_offset));
220
221 let layout = bx.cx.layout_of(target_ty);
222
223 let maybe_reg = bx.append_sibling_block("va_arg.maybe_reg");
224 let in_reg = bx.append_sibling_block("va_arg.in_reg");
225 let on_stack = bx.append_sibling_block("va_arg.on_stack");
226 let end = bx.append_sibling_block("va_arg.end");
227 let zero = bx.const_i32(0);
228 let offset_align = Align::from_bytes(4).unwrap();
229
230 let gr_type = target_ty.is_any_ptr() || target_ty.is_integral();
231 let (reg_off, reg_top, slot_size) = if gr_type {
232 let nreg = layout.size.bytes().div_ceil(8);
233 (gr_offs, gr_top, nreg * 8)
234 } else {
235 let nreg = layout.size.bytes().div_ceil(16);
236 (vr_offs, vr_top, nreg * 16)
237 };
238
239 let mut reg_off_v = bx.load(bx.type_i32(), reg_off, offset_align);
241 let use_stack = bx.icmp(IntPredicate::IntSGE, reg_off_v, zero);
242 bx.cond_br(use_stack, on_stack, maybe_reg);
243
244 bx.switch_to_block(maybe_reg);
249 if gr_type && layout.align.bytes() > 8 {
250 reg_off_v = bx.add(reg_off_v, bx.const_i32(15));
251 reg_off_v = bx.and(reg_off_v, bx.const_i32(-16));
252 }
253 let new_reg_off_v = bx.add(reg_off_v, bx.const_i32(slot_size as i32));
254
255 bx.store(new_reg_off_v, reg_off, offset_align);
256
257 let use_stack = bx.icmp(IntPredicate::IntSGT, new_reg_off_v, zero);
260 bx.cond_br(use_stack, on_stack, in_reg);
261
262 bx.switch_to_block(in_reg);
263 let top_type = bx.type_ptr();
264 let top = bx.load(top_type, reg_top, dl.pointer_align().abi);
265
266 let mut reg_addr = bx.ptradd(top, reg_off_v);
268 if bx.tcx().sess.target.endian == Endian::Big && layout.size.bytes() != slot_size {
269 let offset = bx.const_i32((slot_size - layout.size.bytes()) as i32);
271 reg_addr = bx.ptradd(reg_addr, offset);
272 }
273 let reg_type = layout.llvm_type(bx);
274 let reg_value = bx.load(reg_type, reg_addr, layout.align.abi);
275 bx.br(end);
276
277 bx.switch_to_block(on_stack);
279 let stack_value = emit_ptr_va_arg(
280 bx,
281 list,
282 target_ty,
283 PassMode::Direct,
284 SlotSize::Bytes8,
285 AllowHigherAlign::Yes,
286 ForceRightAdjust::No,
287 );
288 bx.br(end);
289
290 bx.switch_to_block(end);
291 let val =
292 bx.phi(layout.immediate_llvm_type(bx), &[reg_value, stack_value], &[in_reg, on_stack]);
293
294 val
295}
296
297fn emit_powerpc_va_arg<'ll, 'tcx>(
298 bx: &mut Builder<'_, 'll, 'tcx>,
299 list: OperandRef<'tcx, &'ll Value>,
300 target_ty: Ty<'tcx>,
301) -> &'ll Value {
302 let dl = bx.cx.data_layout();
303
304 let va_list_addr = list.immediate();
312
313 let layout = {
315 let mut layout = bx.cx.layout_of(target_ty);
316
317 while let Some((_, inner)) = layout.non_1zst_field(bx.cx) {
318 layout = inner;
319 }
320
321 layout
322 };
323
324 let target = &bx.cx.tcx.sess.target;
326 let is_soft_float_abi = target.rustc_abi == Some(RustcAbi::Softfloat);
327 if !!is_soft_float_abi {
::core::panicking::panic("assertion failed: !is_soft_float_abi")
};assert!(!is_soft_float_abi);
328
329 let is_indirect = false;
331
332 let (is_i64, is_int, is_f64) = match layout.layout.backend_repr() {
333 BackendRepr::Scalar(scalar) => match scalar.primitive() {
334 rustc_abi::Primitive::Int(integer, _) => (integer.size().bits() == 64, true, false),
335 rustc_abi::Primitive::Float(float) => (false, false, float.size().bits() == 64),
336 rustc_abi::Primitive::Pointer(_) => (false, true, false),
337 },
338 _ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("all instances of VaArgSafe are represented as scalars")));
}unreachable!("all instances of VaArgSafe are represented as scalars"),
339 };
340
341 let num_regs_addr = if is_int || is_soft_float_abi {
342 va_list_addr } else {
344 bx.inbounds_ptradd(va_list_addr, bx.const_usize(1)) };
346
347 let mut num_regs = bx.load(bx.type_i8(), num_regs_addr, dl.i8_align);
348
349 if is_i64 || (is_f64 && is_soft_float_abi) {
351 num_regs = bx.add(num_regs, bx.const_u8(1));
352 num_regs = bx.and(num_regs, bx.const_u8(0b1111_1110));
353 }
354
355 let max_regs = 8u8;
356 let use_regs = bx.icmp(IntPredicate::IntULT, num_regs, bx.const_u8(max_regs));
357 let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi;
358
359 let in_reg = bx.append_sibling_block("va_arg.in_reg");
360 let in_mem = bx.append_sibling_block("va_arg.in_mem");
361 let end = bx.append_sibling_block("va_arg.end");
362
363 bx.cond_br(use_regs, in_reg, in_mem);
364
365 let reg_addr = {
366 bx.switch_to_block(in_reg);
367
368 let reg_safe_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(1 + 1 + 2 + 4));
369 let mut reg_addr = bx.load(bx.type_ptr(), reg_safe_area_ptr, ptr_align_abi);
370
371 if !is_int && !is_soft_float_abi {
373 reg_addr = bx.inbounds_ptradd(reg_addr, bx.cx.const_usize(32))
374 }
375
376 let reg_size = if is_int || is_soft_float_abi { 4 } else { 8 };
379 let reg_offset = bx.mul(num_regs, bx.cx().const_u8(reg_size));
380 let reg_addr = bx.inbounds_ptradd(reg_addr, reg_offset);
381
382 let reg_incr = if is_i64 || (is_f64 && is_soft_float_abi) { 2 } else { 1 };
384 let new_num_regs = bx.add(num_regs, bx.cx.const_u8(reg_incr));
385 bx.store(new_num_regs, num_regs_addr, dl.i8_align);
386
387 bx.br(end);
388
389 reg_addr
390 };
391
392 let mem_addr = {
393 bx.switch_to_block(in_mem);
394
395 bx.store(bx.const_u8(max_regs), num_regs_addr, dl.i8_align);
396
397 let overflow_area_align = Align::from_bytes(4).unwrap();
399
400 let size = if !is_indirect {
401 layout.layout.size.align_to(overflow_area_align)
402 } else {
403 dl.pointer_size()
404 };
405
406 let overflow_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(1 + 1 + 2));
407 let mut overflow_area = bx.load(bx.type_ptr(), overflow_area_ptr, ptr_align_abi);
408
409 if layout.layout.align.abi > overflow_area_align {
411 overflow_area =
412 round_pointer_up_to_alignment(bx, overflow_area, layout.layout.align.abi);
413 }
414
415 let mem_addr = overflow_area;
416
417 overflow_area = bx.inbounds_ptradd(overflow_area, bx.const_usize(size.bytes()));
419 bx.store(overflow_area, overflow_area_ptr, ptr_align_abi);
420
421 bx.br(end);
422
423 mem_addr
424 };
425
426 bx.switch_to_block(end);
428 let val_addr = bx.phi(bx.type_ptr(), &[reg_addr, mem_addr], &[in_reg, in_mem]);
429 let val_type = layout.llvm_type(bx);
430 let val_addr =
431 if is_indirect { bx.load(bx.cx.type_ptr(), val_addr, ptr_align_abi) } else { val_addr };
432 bx.load(val_type, val_addr, layout.align.abi)
433}
434
435fn emit_s390x_va_arg<'ll, 'tcx>(
436 bx: &mut Builder<'_, 'll, 'tcx>,
437 list: OperandRef<'tcx, &'ll Value>,
438 target_ty: Ty<'tcx>,
439) -> &'ll Value {
440 let dl = bx.cx.data_layout();
441
442 let va_list_addr = list.immediate();
452
453 let i64_offset = 8;
456 let ptr_offset = 8;
457 let gpr = va_list_addr;
458 let fpr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(i64_offset));
459 let overflow_arg_area = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(2 * i64_offset));
460 let reg_save_area =
461 bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(2 * i64_offset + ptr_offset));
462
463 let layout = bx.cx.layout_of(target_ty);
464
465 let in_reg = bx.append_sibling_block("va_arg.in_reg");
466 let in_mem = bx.append_sibling_block("va_arg.in_mem");
467 let end = bx.append_sibling_block("va_arg.end");
468 let ptr_align_abi = dl.pointer_align().abi;
469
470 let target_ty_size = bx.cx.size_of(target_ty).bytes();
472 let indirect: bool = target_ty_size > 8 || !target_ty_size.is_power_of_two();
473 let unpadded_size = if indirect { 8 } else { target_ty_size };
474 let padded_size = 8;
475 let padding = padded_size - unpadded_size;
476
477 let gpr_type = indirect || !layout.is_single_fp_element(bx.cx);
478 let (max_regs, reg_count, reg_save_index, reg_padding) =
479 if gpr_type { (5, gpr, 2, padding) } else { (4, fpr, 16, 0) };
480
481 let reg_count_v = bx.load(bx.type_i64(), reg_count, Align::from_bytes(8).unwrap());
483 let use_regs = bx.icmp(IntPredicate::IntULT, reg_count_v, bx.const_u64(max_regs));
484 bx.cond_br(use_regs, in_reg, in_mem);
485
486 bx.switch_to_block(in_reg);
488
489 let reg_ptr_v = bx.load(bx.type_ptr(), reg_save_area, ptr_align_abi);
491 let scaled_reg_count = bx.mul(reg_count_v, bx.const_u64(8));
492 let reg_off = bx.add(scaled_reg_count, bx.const_u64(reg_save_index * 8 + reg_padding));
493 let reg_addr = bx.ptradd(reg_ptr_v, reg_off);
494
495 let new_reg_count_v = bx.add(reg_count_v, bx.const_u64(1));
497 bx.store(new_reg_count_v, reg_count, Align::from_bytes(8).unwrap());
498 bx.br(end);
499
500 bx.switch_to_block(in_mem);
502
503 let arg_ptr_v = bx.load(bx.type_ptr(), overflow_arg_area, ptr_align_abi);
505 let arg_off = bx.const_u64(padding);
506 let mem_addr = bx.ptradd(arg_ptr_v, arg_off);
507
508 let arg_size = bx.cx().const_u64(padded_size);
510 let new_arg_ptr_v = bx.inbounds_ptradd(arg_ptr_v, arg_size);
511 bx.store(new_arg_ptr_v, overflow_arg_area, ptr_align_abi);
512 bx.br(end);
513
514 bx.switch_to_block(end);
516 let val_addr = bx.phi(bx.type_ptr(), &[reg_addr, mem_addr], &[in_reg, in_mem]);
517 let val_type = layout.llvm_type(bx);
518 let val_addr =
519 if indirect { bx.load(bx.cx.type_ptr(), val_addr, ptr_align_abi) } else { val_addr };
520 bx.load(val_type, val_addr, layout.align.abi)
521}
522
523fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
524 bx: &mut Builder<'_, 'll, 'tcx>,
525 list: OperandRef<'tcx, &'ll Value>,
526 target_ty: Ty<'tcx>,
527) -> &'ll Value {
528 let dl = bx.cx.data_layout();
529
530 let va_list_addr = list.immediate();
544
545 let layout = {
562 let mut layout = bx.cx.layout_of(target_ty);
563
564 while let Some((_, inner)) = layout.non_1zst_field(bx.cx) {
565 layout = inner;
566 }
567
568 layout
569 };
570
571 let mut num_gp_registers = 0;
579 let mut num_fp_registers = 0;
580
581 let mut registers_for_primitive = |p| match p {
582 Primitive::Int(integer, _is_signed) => {
583 num_gp_registers += integer.size().bytes().div_ceil(8) as u32;
584 }
585 Primitive::Float(float) => {
586 num_fp_registers += float.size().bytes().div_ceil(16) as u32;
587 }
588 Primitive::Pointer(_) => {
589 num_gp_registers += 1;
590 }
591 };
592
593 match layout.layout.backend_repr() {
594 BackendRepr::Scalar(scalar) => {
595 registers_for_primitive(scalar.primitive());
596 }
597 BackendRepr::ScalarPair { a: scalar1, b: scalar2, b_offset: _ } => {
598 registers_for_primitive(scalar1.primitive());
599 registers_for_primitive(scalar2.primitive());
600 }
601 BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => {
602 {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("No x86-64 SysV va_arg implementation for {0:?}",
layout.layout.backend_repr())));
}unreachable!(
604 "No x86-64 SysV va_arg implementation for {:?}",
605 layout.layout.backend_repr()
606 )
607 }
608 BackendRepr::Memory { .. } => {
609 let mem_addr = x86_64_sysv64_va_arg_from_memory(bx, va_list_addr, layout);
610 return bx.load(layout.llvm_type(bx), mem_addr, layout.align.abi);
611 }
612 };
613
614 let unsigned_int_offset = 4;
620 let ptr_offset = bx.tcx().data_layout.pointer_size().bytes();
621
622 let gp_offset_ptr = va_list_addr;
623 let fp_offset_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(unsigned_int_offset));
624
625 let gp_offset_v = bx.load(bx.type_i32(), gp_offset_ptr, Align::from_bytes(8).unwrap());
626 let fp_offset_v = bx.load(bx.type_i32(), fp_offset_ptr, Align::from_bytes(4).unwrap());
627
628 let mut use_regs = bx.const_bool(false);
629
630 if num_gp_registers > 0 {
631 let max_offset_val = 48u32 - num_gp_registers * 8;
632 let fits_in_gp = bx.icmp(IntPredicate::IntULE, gp_offset_v, bx.const_u32(max_offset_val));
633 use_regs = fits_in_gp;
634 }
635
636 if num_fp_registers > 0 {
637 let max_offset_val = 176u32 - num_fp_registers * 16;
638 let fits_in_fp = bx.icmp(IntPredicate::IntULE, fp_offset_v, bx.const_u32(max_offset_val));
639 use_regs = if num_gp_registers > 0 { bx.and(use_regs, fits_in_fp) } else { fits_in_fp };
640 }
641
642 let in_reg = bx.append_sibling_block("va_arg.in_reg");
643 let in_mem = bx.append_sibling_block("va_arg.in_mem");
644 let end = bx.append_sibling_block("va_arg.end");
645
646 bx.cond_br(use_regs, in_reg, in_mem);
647
648 bx.switch_to_block(in_reg);
650
651 let reg_save_area_ptr =
662 bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(2 * unsigned_int_offset + ptr_offset));
663 let reg_save_area_v = bx.load(bx.type_ptr(), reg_save_area_ptr, dl.pointer_align().abi);
664
665 let reg_addr = match layout.layout.backend_repr() {
666 BackendRepr::Scalar(scalar) => match scalar.primitive() {
667 Primitive::Int(_, _) | Primitive::Pointer(_) => {
668 let reg_addr = bx.inbounds_ptradd(reg_save_area_v, gp_offset_v);
669
670 let gp_align = Align::from_bytes(8).unwrap();
672 copy_to_temporary_if_more_aligned(bx, reg_addr, layout, gp_align)
673 }
674 Primitive::Float(_) => bx.inbounds_ptradd(reg_save_area_v, fp_offset_v),
675 },
676 BackendRepr::ScalarPair { a: scalar1, b: scalar2, b_offset: offset } => {
677 let ty_lo = bx.cx().scalar_pair_element_backend_type(layout, 0, false);
678 let ty_hi = bx.cx().scalar_pair_element_backend_type(layout, 1, false);
679
680 let align_lo = layout.field(bx.cx, 0).layout.align().abi;
681 let align_hi = layout.field(bx.cx, 1).layout.align().abi;
682
683 match (scalar1.primitive(), scalar2.primitive()) {
684 (Primitive::Float(_), Primitive::Float(_)) => {
685 let reg_lo_addr = bx.inbounds_ptradd(reg_save_area_v, fp_offset_v);
692 let reg_hi_addr = bx.inbounds_ptradd(reg_lo_addr, bx.const_i32(16));
693
694 let align = layout.layout.align().abi;
695 let tmp = bx.alloca(layout.size, layout.align.abi);
696
697 let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo);
698 let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi);
699
700 let field0 = tmp;
701 let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset.bytes() as u32));
702
703 bx.store(reg_lo, field0, align);
704 bx.store(reg_hi, field1, align);
705
706 tmp
707 }
708 (Primitive::Float(_), _) | (_, Primitive::Float(_)) => {
709 let gp_addr = bx.inbounds_ptradd(reg_save_area_v, gp_offset_v);
710 let fp_addr = bx.inbounds_ptradd(reg_save_area_v, fp_offset_v);
711
712 let (reg_lo_addr, reg_hi_addr) = match scalar1.primitive() {
713 Primitive::Float(_) => (fp_addr, gp_addr),
714 Primitive::Int(_, _) | Primitive::Pointer(_) => (gp_addr, fp_addr),
715 };
716
717 let tmp = bx.alloca(layout.size, layout.align.abi);
718
719 let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo);
720 let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi);
721
722 let field0 = tmp;
723 let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset.bytes() as u32));
724
725 bx.store(reg_lo, field0, align_lo);
726 bx.store(reg_hi, field1, align_hi);
727
728 tmp
729 }
730 (_, _) => {
731 let reg_addr = bx.inbounds_ptradd(reg_save_area_v, gp_offset_v);
733
734 let gp_align = Align::from_bytes(8).unwrap();
736 copy_to_temporary_if_more_aligned(bx, reg_addr, layout, gp_align)
737 }
738 }
739 }
740 BackendRepr::SimdVector { .. }
742 | BackendRepr::SimdScalableVector { .. }
743 | BackendRepr::Memory { .. } => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
744 };
745
746 if num_gp_registers > 0 {
749 let offset = bx.const_u32(num_gp_registers * 8);
750 let sum = bx.add(gp_offset_v, offset);
751 bx.store(sum, gp_offset_ptr, Align::from_bytes(8).unwrap());
753 }
754
755 if num_fp_registers > 0 {
757 let offset = bx.const_u32(num_fp_registers * 16);
758 let sum = bx.add(fp_offset_v, offset);
759 bx.store(sum, fp_offset_ptr, Align::from_bytes(4).unwrap());
760 }
761
762 bx.br(end);
763
764 bx.switch_to_block(in_mem);
765 let mem_addr = x86_64_sysv64_va_arg_from_memory(bx, va_list_addr, layout);
766 bx.br(end);
767
768 bx.switch_to_block(end);
769
770 let val_type = layout.llvm_type(bx);
771 let val_addr = bx.phi(bx.type_ptr(), &[reg_addr, mem_addr], &[in_reg, in_mem]);
772
773 bx.load(val_type, val_addr, layout.align.abi)
774}
775
776fn copy_to_temporary_if_more_aligned<'ll, 'tcx>(
778 bx: &mut Builder<'_, 'll, 'tcx>,
779 reg_addr: &'ll Value,
780 layout: TyAndLayout<'tcx>,
781 src_align: Align,
782) -> &'ll Value {
783 if layout.layout.align.abi > src_align {
784 if !layout.ty.is_integral() {
::core::panicking::panic("assertion failed: layout.ty.is_integral()")
};assert!(layout.ty.is_integral());
785
786 let tmp = bx.alloca(layout.size, layout.align.abi);
788 let val = bx.load(layout.llvm_type(bx), reg_addr, src_align);
789 bx.store(val, tmp, layout.align.abi);
790 tmp
791 } else {
792 reg_addr
793 }
794}
795
796fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>(
797 bx: &mut Builder<'_, 'll, 'tcx>,
798 va_list_addr: &'ll Value,
799 layout: TyAndLayout<'tcx>,
800) -> &'ll Value {
801 let dl = bx.cx.data_layout();
802 let ptr_align_abi = dl.data_layout().pointer_align().abi;
803
804 let overflow_arg_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.const_usize(8));
805
806 let overflow_arg_area_v = bx.load(bx.type_ptr(), overflow_arg_area_ptr, ptr_align_abi);
807 let overflow_arg_area_v = if layout.layout.align.bytes() > 8 {
815 round_pointer_up_to_alignment(bx, overflow_arg_area_v, layout.layout.align.abi)
816 } else {
817 overflow_arg_area_v
818 };
819
820 let mem_addr = overflow_arg_area_v;
822
823 let size_in_bytes = layout.layout.size().bytes();
828 let offset = bx.const_i32(size_in_bytes.next_multiple_of(8) as i32);
829 let overflow_arg_area = bx.inbounds_ptradd(overflow_arg_area_v, offset);
830 bx.store(overflow_arg_area, overflow_arg_area_ptr, ptr_align_abi);
831
832 mem_addr
833}
834
835fn emit_hexagon_va_arg_musl<'ll, 'tcx>(
836 bx: &mut Builder<'_, 'll, 'tcx>,
837 list: OperandRef<'tcx, &'ll Value>,
838 target_ty: Ty<'tcx>,
839) -> &'ll Value {
840 let va_list_addr = list.immediate();
852 let layout = bx.cx.layout_of(target_ty);
853 let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi;
854 let ptr_size = bx.tcx().data_layout.pointer_size().bytes();
855
856 let maybe_reg = bx.append_sibling_block("va_arg.maybe_reg");
858 let from_overflow = bx.append_sibling_block("va_arg.from_overflow");
859 let end = bx.append_sibling_block("va_arg.end");
860
861 let current_ptr_addr = va_list_addr;
863 let end_ptr_addr = bx.inbounds_ptradd(va_list_addr, bx.const_usize(ptr_size));
864 let overflow_ptr_addr = bx.inbounds_ptradd(va_list_addr, bx.const_usize(2 * ptr_size));
865
866 let current_ptr = bx.load(bx.type_ptr(), current_ptr_addr, ptr_align_abi);
867 let end_ptr = bx.load(bx.type_ptr(), end_ptr_addr, ptr_align_abi);
868 let overflow_ptr = bx.load(bx.type_ptr(), overflow_ptr_addr, ptr_align_abi);
869
870 let type_size_bits = bx.cx.size_of(target_ty).bits();
873 let arg_align = if type_size_bits > 32 {
874 Align::from_bytes(8).unwrap()
875 } else {
876 Align::from_bytes(4).unwrap()
877 };
878 let aligned_current = round_pointer_up_to_alignment(bx, current_ptr, arg_align);
879
880 let arg_size = if type_size_bits > 32 { 8 } else { 4 };
883 let next_ptr = bx.inbounds_ptradd(aligned_current, bx.const_usize(arg_size));
884
885 let fits_in_regs = bx.icmp(IntPredicate::IntULE, next_ptr, end_ptr);
887 bx.cond_br(fits_in_regs, maybe_reg, from_overflow);
888
889 bx.switch_to_block(maybe_reg);
891 let reg_value_addr = aligned_current;
892 bx.store(next_ptr, current_ptr_addr, ptr_align_abi);
894 bx.br(end);
895
896 bx.switch_to_block(from_overflow);
898
899 let aligned_overflow = round_pointer_up_to_alignment(bx, overflow_ptr, arg_align);
901
902 let overflow_value_addr = aligned_overflow;
903 let next_overflow = bx.inbounds_ptradd(aligned_overflow, bx.const_usize(arg_size));
905 bx.store(next_overflow, overflow_ptr_addr, ptr_align_abi);
906
907 bx.store(next_overflow, current_ptr_addr, ptr_align_abi);
910 bx.br(end);
911
912 bx.switch_to_block(end);
914 let value_addr =
915 bx.phi(bx.type_ptr(), &[reg_value_addr, overflow_value_addr], &[maybe_reg, from_overflow]);
916 bx.load(layout.llvm_type(bx), value_addr, layout.align.abi)
917}
918
919fn emit_hexagon_va_arg_bare_metal<'ll, 'tcx>(
920 bx: &mut Builder<'_, 'll, 'tcx>,
921 list: OperandRef<'tcx, &'ll Value>,
922 target_ty: Ty<'tcx>,
923) -> &'ll Value {
924 let va_list_addr = list.immediate();
929 let layout = bx.cx.layout_of(target_ty);
930 let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi;
931
932 let current_ptr = bx.load(bx.type_ptr(), va_list_addr, ptr_align_abi);
934
935 let ty_align = layout.align.abi;
937 let aligned_ptr = if ty_align.bytes() > 4 {
938 if true {
if !ty_align.bytes().is_power_of_two() {
{
::core::panicking::panic_fmt(format_args!("Alignment is not power of 2!"));
}
};
};debug_assert!(ty_align.bytes().is_power_of_two(), "Alignment is not power of 2!");
940 round_pointer_up_to_alignment(bx, current_ptr, ty_align)
941 } else {
942 current_ptr
943 };
944
945 let type_size = layout.size.bytes();
947 let offset = type_size.next_multiple_of(4); let next_ptr = bx.inbounds_ptradd(aligned_ptr, bx.const_usize(offset));
951 bx.store(next_ptr, va_list_addr, ptr_align_abi);
952
953 bx.load(layout.llvm_type(bx), aligned_ptr, layout.align.abi)
955}
956
957fn emit_xtensa_va_arg<'ll, 'tcx>(
958 bx: &mut Builder<'_, 'll, 'tcx>,
959 list: OperandRef<'tcx, &'ll Value>,
960 target_ty: Ty<'tcx>,
961) -> &'ll Value {
962 let va_list_addr = list.immediate();
979 let layout = bx.cx.layout_of(target_ty);
981 let from_stack = bx.append_sibling_block("va_arg.from_stack");
982 let from_regsave = bx.append_sibling_block("va_arg.from_regsave");
983 let end = bx.append_sibling_block("va_arg.end");
984 let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi;
985
986 let va_reg_offset = 4;
988 let va_ndx_offset = va_reg_offset + 4;
989 let offset_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(va_ndx_offset));
990
991 let offset = bx.load(bx.type_i32(), offset_ptr, bx.tcx().data_layout.i32_align);
992 let offset = round_up_to_alignment(bx, offset, layout.align.abi);
993
994 let slot_size = layout.size.align_to(Align::from_bytes(4).unwrap()).bytes() as i32;
995
996 let offset_next = bx.add(offset, bx.const_i32(slot_size));
998
999 let regsave_size = bx.const_i32(24);
1002 let use_regsave = bx.icmp(IntPredicate::IntULE, offset_next, regsave_size);
1003 bx.cond_br(use_regsave, from_regsave, from_stack);
1004
1005 bx.switch_to_block(from_regsave);
1006 bx.store(offset_next, offset_ptr, ptr_align_abi);
1008
1009 let regsave_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(va_reg_offset));
1011 let regsave_area = bx.load(bx.type_ptr(), regsave_area_ptr, ptr_align_abi);
1012 let regsave_value_ptr = bx.inbounds_ptradd(regsave_area, offset);
1013 bx.br(end);
1014
1015 bx.switch_to_block(from_stack);
1016
1017 let stack_offset_start = bx.const_i32(32);
1023 let needs_correction = bx.icmp(IntPredicate::IntULE, offset, stack_offset_start);
1024 let offset_corrected = bx.select(needs_correction, stack_offset_start, offset);
1025
1026 let offset_next_corrected = bx.add(offset_corrected, bx.const_i32(slot_size));
1029 bx.store(offset_next_corrected, offset_ptr, ptr_align_abi);
1031
1032 let stack_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(0));
1034 let stack_area = bx.load(bx.type_ptr(), stack_area_ptr, ptr_align_abi);
1035 let stack_value_ptr = bx.inbounds_ptradd(stack_area, offset_corrected);
1036 bx.br(end);
1037
1038 bx.switch_to_block(end);
1039
1040 if !(bx.tcx().sess.target.endian == Endian::Little) {
::core::panicking::panic("assertion failed: bx.tcx().sess.target.endian == Endian::Little")
};assert!(bx.tcx().sess.target.endian == Endian::Little);
1050 let value_ptr =
1051 bx.phi(bx.type_ptr(), &[regsave_value_ptr, stack_value_ptr], &[from_regsave, from_stack]);
1052 return bx.load(layout.llvm_type(bx), value_ptr, layout.align.abi);
1053}
1054
1055pub(super) fn emit_va_arg<'ll, 'tcx>(
1060 bx: &mut Builder<'_, 'll, 'tcx>,
1061 addr: OperandRef<'tcx, &'ll Value>,
1062 target_ty: Ty<'tcx>,
1063) -> &'ll Value {
1064 let layout = bx.cx.layout_of(target_ty);
1065 let target_ty_size = layout.layout.size().bytes();
1066
1067 if !!bx.layout_of(target_ty).is_zst() {
::core::panicking::panic("assertion failed: !bx.layout_of(target_ty).is_zst()")
};assert!(!bx.layout_of(target_ty).is_zst());
1070
1071 let target = &bx.cx.tcx.sess.target;
1072 let stability = target.supports_c_variadic_definitions();
1073
1074 match target.arch {
1075 Arch::X86 => emit_ptr_va_arg(
1076 bx,
1077 addr,
1078 target_ty,
1079 PassMode::Direct,
1080 SlotSize::Bytes4,
1081 if target.is_like_windows { AllowHigherAlign::No } else { AllowHigherAlign::Yes },
1082 ForceRightAdjust::No,
1083 ),
1084 Arch::Arm64EC => emit_ptr_va_arg(
1085 bx,
1086 addr,
1087 target_ty,
1088 if target_ty_size > 8 || !target_ty_size.is_power_of_two() {
1091 PassMode::Indirect
1092 } else {
1093 PassMode::Direct
1094 },
1095 SlotSize::Bytes8,
1096 AllowHigherAlign::No,
1097 ForceRightAdjust::No,
1098 ),
1099 Arch::AArch64 if target.is_like_windows || target.is_like_darwin => emit_ptr_va_arg(
1100 bx,
1101 addr,
1102 target_ty,
1103 PassMode::Direct,
1104 SlotSize::Bytes8,
1105 AllowHigherAlign::Yes,
1106 ForceRightAdjust::No,
1107 ),
1108 Arch::AArch64 => emit_aapcs_va_arg(bx, addr, target_ty),
1109 Arch::Arm => {
1110 if !(bx.cx.size_of(target_ty).bytes() <= 16) {
::core::panicking::panic("assertion failed: bx.cx.size_of(target_ty).bytes() <= 16")
};assert!(bx.cx.size_of(target_ty).bytes() <= 16);
1114
1115 emit_ptr_va_arg(
1116 bx,
1117 addr,
1118 target_ty,
1119 PassMode::Direct,
1120 SlotSize::Bytes4,
1121 AllowHigherAlign::Yes,
1122 ForceRightAdjust::No,
1123 )
1124 }
1125 Arch::S390x => emit_s390x_va_arg(bx, addr, target_ty),
1126 Arch::PowerPC => emit_powerpc_va_arg(bx, addr, target_ty),
1127 Arch::PowerPC64 => emit_ptr_va_arg(
1128 bx,
1129 addr,
1130 target_ty,
1131 PassMode::Direct,
1132 SlotSize::Bytes8,
1133 AllowHigherAlign::Yes,
1134 ForceRightAdjust::Yes,
1136 ),
1137 Arch::RiscV32 if target.llvm_abiname == LlvmAbi::Ilp32e => {
1138 {
match stability {
CVariadicStatus::Unstable { .. } => {}
ref left_val => {
::core::panicking::assert_matches_failed(left_val,
"CVariadicStatus::Unstable { .. }",
::core::option::Option::None);
}
}
};std::assert_matches!(stability, CVariadicStatus::Unstable { .. });
1139 ::rustc_middle::util::bug::bug_fmt(format_args!("c-variadic calls with ilp32e use a custom ABI and are not currently implemented"));bug!("c-variadic calls with ilp32e use a custom ABI and are not currently implemented");
1146 }
1147 Arch::RiscV32 | Arch::LoongArch32 => emit_ptr_va_arg(
1148 bx,
1149 addr,
1150 target_ty,
1151 if target_ty_size > 2 * 4 { PassMode::Indirect } else { PassMode::Direct },
1152 SlotSize::Bytes4,
1153 AllowHigherAlign::Yes,
1154 ForceRightAdjust::No,
1155 ),
1156 Arch::RiscV64 | Arch::LoongArch64 => emit_ptr_va_arg(
1157 bx,
1158 addr,
1159 target_ty,
1160 if target_ty_size > 2 * 8 { PassMode::Indirect } else { PassMode::Direct },
1161 SlotSize::Bytes8,
1162 AllowHigherAlign::Yes,
1163 ForceRightAdjust::No,
1164 ),
1165 Arch::AmdGpu => emit_ptr_va_arg(
1166 bx,
1167 addr,
1168 target_ty,
1169 PassMode::Direct,
1170 SlotSize::Bytes4,
1171 AllowHigherAlign::No,
1172 ForceRightAdjust::No,
1173 ),
1174 Arch::Nvptx64 => emit_ptr_va_arg(
1175 bx,
1176 addr,
1177 target_ty,
1178 PassMode::Direct,
1179 SlotSize::Bytes1,
1180 AllowHigherAlign::Yes,
1181 ForceRightAdjust::No,
1182 ),
1183 Arch::Wasm32 | Arch::Wasm64 => emit_ptr_va_arg(
1184 bx,
1185 addr,
1186 target_ty,
1187 if layout.is_aggregate() || layout.is_zst() || layout.is_1zst() {
1188 PassMode::Indirect
1189 } else {
1190 PassMode::Direct
1191 },
1192 SlotSize::Bytes4,
1193 AllowHigherAlign::Yes,
1194 ForceRightAdjust::No,
1195 ),
1196 Arch::CSky => emit_ptr_va_arg(
1197 bx,
1198 addr,
1199 target_ty,
1200 PassMode::Direct,
1201 SlotSize::Bytes4,
1202 AllowHigherAlign::Yes,
1203 ForceRightAdjust::No,
1204 ),
1205 Arch::X86_64 if target.is_like_windows => emit_ptr_va_arg(
1207 bx,
1208 addr,
1209 target_ty,
1210 if target_ty_size > 8 || !target_ty_size.is_power_of_two() {
1211 PassMode::Indirect
1212 } else {
1213 PassMode::Direct
1214 },
1215 SlotSize::Bytes8,
1216 AllowHigherAlign::No,
1217 ForceRightAdjust::No,
1218 ),
1219 Arch::X86_64 => emit_x86_64_sysv64_va_arg(bx, addr, target_ty),
1221 Arch::Xtensa => emit_xtensa_va_arg(bx, addr, target_ty),
1222 Arch::Hexagon => match target.env {
1223 Env::Musl => emit_hexagon_va_arg_musl(bx, addr, target_ty),
1224 _ => emit_hexagon_va_arg_bare_metal(bx, addr, target_ty),
1225 },
1226 Arch::Sparc64 => emit_ptr_va_arg(
1227 bx,
1228 addr,
1229 target_ty,
1230 if target_ty_size > 2 * 8 { PassMode::Indirect } else { PassMode::Direct },
1231 SlotSize::Bytes8,
1232 AllowHigherAlign::Yes,
1233 ForceRightAdjust::Yes,
1235 ),
1236 Arch::Mips | Arch::Mips32r6 | Arch::Mips64 | Arch::Mips64r6 => emit_ptr_va_arg(
1237 bx,
1238 addr,
1239 target_ty,
1240 PassMode::Direct,
1241 match &target.llvm_abiname {
1242 LlvmAbi::N32 | LlvmAbi::N64 => SlotSize::Bytes8,
1243 LlvmAbi::O32 => SlotSize::Bytes4,
1244 other => ::rustc_middle::util::bug::bug_fmt(format_args!("unexpected LLVM ABI {0}",
other))bug!("unexpected LLVM ABI {other}"),
1245 },
1246 AllowHigherAlign::Yes,
1247 match bx.tcx().sess.target.endian {
1251 Endian::Big => ForceRightAdjust::Yes,
1252 Endian::Little => ForceRightAdjust::No,
1253 },
1254 ),
1255
1256 Arch::Bpf => ::rustc_middle::util::bug::bug_fmt(format_args!("bpf does not support c-variadic functions"))bug!("bpf does not support c-variadic functions"),
1257 Arch::SpirV => ::rustc_middle::util::bug::bug_fmt(format_args!("spirv does not support c-variadic functions"))bug!("spirv does not support c-variadic functions"),
1258
1259 Arch::Sparc | Arch::Avr | Arch::M68k | Arch::Msp430 => {
1260 {
match stability {
CVariadicStatus::Unstable { .. } => {}
ref left_val => {
::core::panicking::assert_matches_failed(left_val,
"CVariadicStatus::Unstable { .. }",
::core::option::Option::None);
}
}
};std::assert_matches!(stability, CVariadicStatus::Unstable { .. });
1261
1262 bx.va_arg(addr.immediate(), bx.cx.layout_of(target_ty).llvm_type(bx.cx))
1264 }
1265
1266 Arch::Other(ref arch) => {
1267 {
match stability {
CVariadicStatus::Unstable { .. } => {}
ref left_val => {
::core::panicking::assert_matches_failed(left_val,
"CVariadicStatus::Unstable { .. }",
::core::option::Option::None);
}
}
};std::assert_matches!(stability, CVariadicStatus::Unstable { .. });
1268
1269 ::rustc_middle::util::bug::bug_fmt(format_args!("c-variadic functions are not currently implemented for custom target {0}",
arch))bug!("c-variadic functions are not currently implemented for custom target {arch}")
1272 }
1273 }
1274}