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::ty::Ty;
10use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
11use rustc_span::bug;
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_span::macros::bug_impl(None,
format_args!("unexpected backend repr {0:?}", layout.backend_repr),
Location::caller());bug!("unexpected backend repr {:?}", layout.backend_repr);
88 };
89
90 match bx.cx.tcx.sess.target.arch {
91 Arch::PowerPC | 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::F16B | Float::F32 => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
99 Float::F64 | Float::F128 => { }
100 Float::PpcF128 => return Align::EIGHT,
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 layout: TyAndLayout<'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 (llty, size, align) = if indirect {
165 (
166 bx.cx.layout_of(Ty::new_imm_ptr(bx.cx.tcx, layout.ty)).llvm_type(bx.cx),
167 bx.cx.data_layout().pointer_size(),
168 bx.cx.data_layout().pointer_align().abi,
169 )
170 } else {
171 (layout.llvm_type(bx.cx), layout.size, get_param_type_alignment(bx, layout))
172 };
173 let (addr, addr_align) = emit_direct_ptr_va_arg(
174 bx,
175 list,
176 size,
177 align,
178 slot_size,
179 allow_higher_align,
180 force_right_adjust,
181 );
182 if indirect {
183 let tmp_ret = bx.load(llty, addr, addr_align);
184 bx.load(layout.llvm_type(bx.cx), tmp_ret, align)
185 } else {
186 bx.load(llty, addr, addr_align)
187 }
188}
189
190fn emit_aapcs_va_arg<'ll, 'tcx>(
191 bx: &mut Builder<'_, 'll, 'tcx>,
192 list: OperandRef<'tcx, &'ll Value>,
193 layout: TyAndLayout<'tcx>,
194) -> &'ll Value {
195 let dl = bx.cx.data_layout();
196
197 let va_list_addr = list.immediate();
208
209 let ptr_offset = 8;
214 let i32_offset = 4;
215 let gr_top = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(ptr_offset));
216 let vr_top = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(2 * ptr_offset));
217 let gr_offs = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(3 * ptr_offset));
218 let vr_offs = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(3 * ptr_offset + i32_offset));
219
220 let maybe_reg = bx.append_sibling_block("va_arg.maybe_reg");
221 let in_reg = bx.append_sibling_block("va_arg.in_reg");
222 let on_stack = bx.append_sibling_block("va_arg.on_stack");
223 let end = bx.append_sibling_block("va_arg.end");
224 let zero = bx.const_i32(0);
225 let offset_align = Align::from_bytes(4).unwrap();
226
227 let gr_type = layout.ty.is_any_ptr() || layout.ty.is_integral();
228 let (reg_off, reg_top, slot_size) = if gr_type {
229 let nreg = layout.size.bytes().div_ceil(8);
230 (gr_offs, gr_top, nreg * 8)
231 } else {
232 let nreg = layout.size.bytes().div_ceil(16);
233 (vr_offs, vr_top, nreg * 16)
234 };
235
236 let mut reg_off_v = bx.load(bx.type_i32(), reg_off, offset_align);
238 let use_stack = bx.icmp(IntPredicate::IntSGE, reg_off_v, zero);
239 bx.cond_br(use_stack, on_stack, maybe_reg);
240
241 bx.switch_to_block(maybe_reg);
246 if gr_type && layout.align.bytes() > 8 {
247 reg_off_v = bx.add(reg_off_v, bx.const_i32(15));
248 reg_off_v = bx.and(reg_off_v, bx.const_i32(-16));
249 }
250 let new_reg_off_v = bx.add(reg_off_v, bx.const_i32(slot_size as i32));
251
252 bx.store(new_reg_off_v, reg_off, offset_align);
253
254 let use_stack = bx.icmp(IntPredicate::IntSGT, new_reg_off_v, zero);
257 bx.cond_br(use_stack, on_stack, in_reg);
258
259 bx.switch_to_block(in_reg);
260 let top_type = bx.type_ptr();
261 let top = bx.load(top_type, reg_top, dl.pointer_align().abi);
262
263 let mut reg_addr = bx.ptradd(top, reg_off_v);
265 if bx.tcx().sess.target.endian == Endian::Big && layout.size.bytes() != slot_size {
266 let offset = bx.const_i32((slot_size - layout.size.bytes()) as i32);
268 reg_addr = bx.ptradd(reg_addr, offset);
269 }
270 let reg_type = layout.llvm_type(bx);
271 let reg_value = bx.load(reg_type, reg_addr, layout.align.abi);
272 bx.br(end);
273
274 bx.switch_to_block(on_stack);
276 let stack_value = emit_ptr_va_arg(
277 bx,
278 list,
279 layout,
280 PassMode::Direct,
281 SlotSize::Bytes8,
282 AllowHigherAlign::Yes,
283 ForceRightAdjust::No,
284 );
285 bx.br(end);
286
287 bx.switch_to_block(end);
288 let val =
289 bx.phi(layout.immediate_llvm_type(bx), &[reg_value, stack_value], &[in_reg, on_stack]);
290
291 val
292}
293
294fn emit_powerpc_va_arg<'ll, 'tcx>(
295 bx: &mut Builder<'_, 'll, 'tcx>,
296 list: OperandRef<'tcx, &'ll Value>,
297 layout: TyAndLayout<'tcx>,
298) -> &'ll Value {
299 let dl = bx.cx.data_layout();
300
301 let va_list_addr = list.immediate();
309
310 let layout = layout.peel_transparent_wrappers_from_non_1zst(bx.cx);
312
313 let target = &bx.cx.tcx.sess.target;
317 let is_soft_float_abi = target.rustc_abi == Some(RustcAbi::Softfloat);
318 if !!is_soft_float_abi {
::core::panicking::panic("assertion failed: !is_soft_float_abi")
};assert!(!is_soft_float_abi);
319
320 let is_indirect = false;
322
323 let (is_i64, is_int, is_f64) = match layout.layout.backend_repr() {
324 BackendRepr::Scalar(scalar) => match scalar.primitive() {
325 rustc_abi::Primitive::Int(integer, _) => (integer.size().bits() == 64, true, false),
326 rustc_abi::Primitive::Float(float) => (false, false, #[allow(non_exhaustive_omitted_patterns)] match float {
Float::F64 => true,
_ => false,
}matches!(float, Float::F64)),
327 rustc_abi::Primitive::Pointer(_) => (false, true, false),
328 },
329 _ => {
::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"),
330 };
331
332 let is_passed_in_fprs = !(is_int || is_soft_float_abi);
333 let num_regs_addr = if !is_passed_in_fprs {
334 va_list_addr } else {
336 bx.inbounds_ptradd(va_list_addr, bx.const_usize(1)) };
338
339 let mut num_regs = bx.load(bx.type_i8(), num_regs_addr, dl.i8_align);
340
341 if is_i64 || (is_f64 && is_soft_float_abi) {
343 num_regs = bx.add(num_regs, bx.const_u8(1));
344 num_regs = bx.and(num_regs, bx.const_u8(0b1111_1110));
345 }
346
347 let max_regs = 8u8;
348 let reg_size = if is_passed_in_fprs { 8 } else { 4 };
349 let reg_incr = layout.size.bytes().div_ceil(u64::from(reg_size)) as u8;
350 let use_regs = bx.icmp(IntPredicate::IntULE, num_regs, bx.const_u8(max_regs - reg_incr));
351
352 let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi;
353
354 let in_reg = bx.append_sibling_block("va_arg.in_reg");
355 let in_mem = bx.append_sibling_block("va_arg.in_mem");
356 let end = bx.append_sibling_block("va_arg.end");
357
358 bx.cond_br(use_regs, in_reg, in_mem);
359
360 let reg_addr = {
361 bx.switch_to_block(in_reg);
362
363 let reg_save_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(1 + 1 + 2 + 4));
364 let mut reg_addr = bx.load(bx.type_ptr(), reg_save_area_ptr, ptr_align_abi);
365
366 if is_passed_in_fprs {
368 reg_addr = bx.inbounds_ptradd(reg_addr, bx.cx.const_usize(32))
369 }
370
371 let reg_offset = bx.mul(num_regs, bx.cx().const_u8(reg_size));
374 let reg_addr = bx.inbounds_ptradd(reg_addr, reg_offset);
375
376 let new_num_regs = bx.add(num_regs, bx.cx.const_u8(reg_incr));
378 bx.store(new_num_regs, num_regs_addr, dl.i8_align);
379
380 bx.br(end);
381
382 reg_addr
383 };
384
385 let mem_addr = {
386 bx.switch_to_block(in_mem);
387
388 bx.store(bx.const_u8(max_regs), num_regs_addr, dl.i8_align);
389
390 let overflow_area_align = Align::from_bytes(4).unwrap();
392
393 let size = if !is_indirect {
394 layout.layout.size.align_to(overflow_area_align)
395 } else {
396 dl.pointer_size()
397 };
398
399 let overflow_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(1 + 1 + 2));
400 let mut overflow_area = bx.load(bx.type_ptr(), overflow_area_ptr, ptr_align_abi);
401
402 let align = get_param_type_alignment(bx, layout);
404 if align > overflow_area_align {
405 overflow_area = round_pointer_up_to_alignment(bx, overflow_area, align);
406 }
407
408 let mem_addr = overflow_area;
409
410 overflow_area = bx.inbounds_ptradd(overflow_area, bx.const_usize(size.bytes()));
412 bx.store(overflow_area, overflow_area_ptr, ptr_align_abi);
413
414 bx.br(end);
415
416 mem_addr
417 };
418
419 bx.switch_to_block(end);
421 let val_addr = bx.phi(bx.type_ptr(), &[reg_addr, mem_addr], &[in_reg, in_mem]);
422 let val_type = layout.llvm_type(bx);
423 let val_addr =
424 if is_indirect { bx.load(bx.cx.type_ptr(), val_addr, ptr_align_abi) } else { val_addr };
425 let align = get_param_type_alignment(bx, layout);
426 bx.load(val_type, val_addr, align)
427}
428
429fn emit_s390x_va_arg<'ll, 'tcx>(
430 bx: &mut Builder<'_, 'll, 'tcx>,
431 list: OperandRef<'tcx, &'ll Value>,
432 layout: TyAndLayout<'tcx>,
433) -> &'ll Value {
434 let dl = bx.cx.data_layout();
435
436 let va_list_addr = list.immediate();
446
447 let i64_offset = 8;
450 let ptr_offset = 8;
451 let gpr = va_list_addr;
452 let fpr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(i64_offset));
453 let overflow_arg_area = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(2 * i64_offset));
454 let reg_save_area =
455 bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(2 * i64_offset + ptr_offset));
456
457 let in_reg = bx.append_sibling_block("va_arg.in_reg");
458 let in_mem = bx.append_sibling_block("va_arg.in_mem");
459 let end = bx.append_sibling_block("va_arg.end");
460 let ptr_align_abi = dl.pointer_align().abi;
461
462 let target_ty_size = layout.size.bytes();
464 let indirect: bool = target_ty_size > 8 || !target_ty_size.is_power_of_two();
465 let unpadded_size = if indirect { 8 } else { target_ty_size };
466 let padded_size = 8;
467 let padding = padded_size - unpadded_size;
468
469 let is_single_fp_element = |layout: TyAndLayout<'_>| -> bool {
471 match layout.layout.backend_repr() {
472 BackendRepr::Scalar(scalar) => match scalar.primitive() {
473 Primitive::Float(Float::F16 | Float::F32 | Float::F64) => true,
474 Primitive::Float(Float::F128) => false,
475 Primitive::Int(_, _) | Primitive::Pointer(_) => false,
476 Primitive::Float(Float::F16B) => {
477 ::rustc_span::macros::bug_impl(None,
format_args!("`f16b` use in varadics unsupported on s390x"),
Location::caller())bug!("`f16b` use in varadics unsupported on s390x")
478 }
479 Primitive::Float(Float::PpcF128) => {
480 ::rustc_span::macros::bug_impl(None,
format_args!("`ppcf128` use in varadics unsupported on s390x"),
Location::caller())bug!("`ppcf128` use in varadics unsupported on s390x")
481 }
482 },
483
484 _ => false,
485 }
486 };
487
488 let gpr_type = indirect || !is_single_fp_element(layout);
489 let (max_regs, reg_count, reg_save_index, reg_padding) =
490 if gpr_type { (5, gpr, 2, padding) } else { (4, fpr, 16, 0) };
491
492 let reg_count_v = bx.load(bx.type_i64(), reg_count, Align::from_bytes(8).unwrap());
494 let use_regs = bx.icmp(IntPredicate::IntULT, reg_count_v, bx.const_u64(max_regs));
495 bx.cond_br(use_regs, in_reg, in_mem);
496
497 bx.switch_to_block(in_reg);
499
500 let reg_ptr_v = bx.load(bx.type_ptr(), reg_save_area, ptr_align_abi);
502 let scaled_reg_count = bx.mul(reg_count_v, bx.const_u64(8));
503 let reg_off = bx.add(scaled_reg_count, bx.const_u64(reg_save_index * 8 + reg_padding));
504 let reg_addr = bx.ptradd(reg_ptr_v, reg_off);
505
506 let new_reg_count_v = bx.add(reg_count_v, bx.const_u64(1));
508 bx.store(new_reg_count_v, reg_count, Align::from_bytes(8).unwrap());
509 bx.br(end);
510
511 bx.switch_to_block(in_mem);
513
514 let arg_ptr_v = bx.load(bx.type_ptr(), overflow_arg_area, ptr_align_abi);
516 let arg_off = bx.const_u64(padding);
517 let mem_addr = bx.ptradd(arg_ptr_v, arg_off);
518
519 let arg_size = bx.cx().const_u64(padded_size);
521 let new_arg_ptr_v = bx.inbounds_ptradd(arg_ptr_v, arg_size);
522 bx.store(new_arg_ptr_v, overflow_arg_area, ptr_align_abi);
523 bx.br(end);
524
525 bx.switch_to_block(end);
527 let val_addr = bx.phi(bx.type_ptr(), &[reg_addr, mem_addr], &[in_reg, in_mem]);
528 let val_type = layout.llvm_type(bx);
529 let val_addr =
530 if indirect { bx.load(bx.cx.type_ptr(), val_addr, ptr_align_abi) } else { val_addr };
531 bx.load(val_type, val_addr, layout.align.abi)
532}
533
534fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
535 bx: &mut Builder<'_, 'll, 'tcx>,
536 list: OperandRef<'tcx, &'ll Value>,
537 layout: TyAndLayout<'tcx>,
538) -> &'ll Value {
539 let dl = bx.cx.data_layout();
540
541 let va_list_addr = list.immediate();
555
556 let layout = layout.peel_transparent_wrappers_from_non_1zst(bx.cx);
573
574 let mut num_gp_registers = 0;
582 let mut num_fp_registers = 0;
583
584 let mut registers_for_primitive = |p| match p {
585 Primitive::Int(integer, _is_signed) => {
586 num_gp_registers += integer.size().bytes().div_ceil(8) as u32;
587 }
588 Primitive::Float(float) => {
589 num_fp_registers += float.size().bytes().div_ceil(16) as u32;
590 }
591 Primitive::Pointer(_) => {
592 num_gp_registers += 1;
593 }
594 };
595
596 match layout.layout.backend_repr() {
597 BackendRepr::Scalar(scalar) => {
598 registers_for_primitive(scalar.primitive());
599 }
600 BackendRepr::ScalarPair { a: scalar1, b: scalar2, b_offset: _ } => {
601 registers_for_primitive(scalar1.primitive());
602 registers_for_primitive(scalar2.primitive());
603 }
604 BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => {
605 {
::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!(
607 "No x86-64 SysV va_arg implementation for {:?}",
608 layout.layout.backend_repr()
609 )
610 }
611 BackendRepr::Memory { .. } => {
612 let mem_addr = x86_64_sysv64_va_arg_from_memory(bx, va_list_addr, layout);
613 return bx.load(layout.llvm_type(bx), mem_addr, layout.align.abi);
614 }
615 };
616
617 let unsigned_int_offset = 4;
623 let ptr_offset = bx.tcx().data_layout.pointer_size().bytes();
624
625 let gp_offset_ptr = va_list_addr;
626 let fp_offset_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(unsigned_int_offset));
627
628 let gp_offset_v = bx.load(bx.type_i32(), gp_offset_ptr, Align::from_bytes(8).unwrap());
629 let fp_offset_v = bx.load(bx.type_i32(), fp_offset_ptr, Align::from_bytes(4).unwrap());
630
631 let mut use_regs = bx.const_bool(false);
632
633 if num_gp_registers > 0 {
634 let max_offset_val = 48u32 - num_gp_registers * 8;
635 let fits_in_gp = bx.icmp(IntPredicate::IntULE, gp_offset_v, bx.const_u32(max_offset_val));
636 use_regs = fits_in_gp;
637 }
638
639 if num_fp_registers > 0 {
640 let max_offset_val = 176u32 - num_fp_registers * 16;
641 let fits_in_fp = bx.icmp(IntPredicate::IntULE, fp_offset_v, bx.const_u32(max_offset_val));
642 use_regs = if num_gp_registers > 0 { bx.and(use_regs, fits_in_fp) } else { fits_in_fp };
643 }
644
645 let in_reg = bx.append_sibling_block("va_arg.in_reg");
646 let in_mem = bx.append_sibling_block("va_arg.in_mem");
647 let end = bx.append_sibling_block("va_arg.end");
648
649 bx.cond_br(use_regs, in_reg, in_mem);
650
651 bx.switch_to_block(in_reg);
653
654 let reg_save_area_ptr =
665 bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(2 * unsigned_int_offset + ptr_offset));
666 let reg_save_area_v = bx.load(bx.type_ptr(), reg_save_area_ptr, dl.pointer_align().abi);
667
668 let reg_addr = match layout.layout.backend_repr() {
669 BackendRepr::Scalar(scalar) => match scalar.primitive() {
670 Primitive::Int(_, _) | Primitive::Pointer(_) => {
671 let reg_addr = bx.inbounds_ptradd(reg_save_area_v, gp_offset_v);
672
673 let gp_align = Align::from_bytes(8).unwrap();
675 copy_to_temporary_if_more_aligned(bx, reg_addr, layout, gp_align)
676 }
677 Primitive::Float(_) => bx.inbounds_ptradd(reg_save_area_v, fp_offset_v),
678 },
679 BackendRepr::ScalarPair { a: scalar1, b: scalar2, b_offset: offset } => {
680 let ty_lo = bx.cx().scalar_pair_element_backend_type(layout, 0, false);
681 let ty_hi = bx.cx().scalar_pair_element_backend_type(layout, 1, false);
682
683 let align_lo = layout.field(bx.cx, 0).layout.align().abi;
684 let align_hi = layout.field(bx.cx, 1).layout.align().abi;
685
686 match (scalar1.primitive(), scalar2.primitive()) {
687 (Primitive::Float(_), Primitive::Float(_)) => {
688 let reg_lo_addr = bx.inbounds_ptradd(reg_save_area_v, fp_offset_v);
695 let reg_hi_addr = bx.inbounds_ptradd(reg_lo_addr, bx.const_i32(16));
696
697 let align = layout.layout.align().abi;
698 let tmp = bx.alloca(layout.size, layout.align.abi);
699
700 let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo);
701 let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi);
702
703 let field0 = tmp;
704 let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset.bytes() as u32));
705
706 bx.store(reg_lo, field0, align);
707 bx.store(reg_hi, field1, align);
708
709 tmp
710 }
711 (Primitive::Float(_), _) | (_, Primitive::Float(_)) => {
712 let gp_addr = bx.inbounds_ptradd(reg_save_area_v, gp_offset_v);
713 let fp_addr = bx.inbounds_ptradd(reg_save_area_v, fp_offset_v);
714
715 let (reg_lo_addr, reg_hi_addr) = match scalar1.primitive() {
716 Primitive::Float(_) => (fp_addr, gp_addr),
717 Primitive::Int(_, _) | Primitive::Pointer(_) => (gp_addr, fp_addr),
718 };
719
720 let tmp = bx.alloca(layout.size, layout.align.abi);
721
722 let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo);
723 let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi);
724
725 let field0 = tmp;
726 let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset.bytes() as u32));
727
728 bx.store(reg_lo, field0, align_lo);
729 bx.store(reg_hi, field1, align_hi);
730
731 tmp
732 }
733 (_, _) => {
734 let reg_addr = bx.inbounds_ptradd(reg_save_area_v, gp_offset_v);
736
737 let gp_align = Align::from_bytes(8).unwrap();
739 copy_to_temporary_if_more_aligned(bx, reg_addr, layout, gp_align)
740 }
741 }
742 }
743 BackendRepr::SimdVector { .. }
745 | BackendRepr::SimdScalableVector { .. }
746 | BackendRepr::Memory { .. } => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
747 };
748
749 if num_gp_registers > 0 {
752 let offset = bx.const_u32(num_gp_registers * 8);
753 let sum = bx.add(gp_offset_v, offset);
754 bx.store(sum, gp_offset_ptr, Align::from_bytes(8).unwrap());
756 }
757
758 if num_fp_registers > 0 {
760 let offset = bx.const_u32(num_fp_registers * 16);
761 let sum = bx.add(fp_offset_v, offset);
762 bx.store(sum, fp_offset_ptr, Align::from_bytes(4).unwrap());
763 }
764
765 bx.br(end);
766
767 bx.switch_to_block(in_mem);
768 let mem_addr = x86_64_sysv64_va_arg_from_memory(bx, va_list_addr, layout);
769 bx.br(end);
770
771 bx.switch_to_block(end);
772
773 let val_type = layout.llvm_type(bx);
774 let val_addr = bx.phi(bx.type_ptr(), &[reg_addr, mem_addr], &[in_reg, in_mem]);
775
776 bx.load(val_type, val_addr, layout.align.abi)
777}
778
779fn copy_to_temporary_if_more_aligned<'ll, 'tcx>(
781 bx: &mut Builder<'_, 'll, 'tcx>,
782 reg_addr: &'ll Value,
783 layout: TyAndLayout<'tcx>,
784 src_align: Align,
785) -> &'ll Value {
786 if layout.layout.align.abi > src_align {
787 if !layout.ty.is_integral() {
::core::panicking::panic("assertion failed: layout.ty.is_integral()")
};assert!(layout.ty.is_integral());
788
789 let tmp = bx.alloca(layout.size, layout.align.abi);
791 let val = bx.load(layout.llvm_type(bx), reg_addr, src_align);
792 bx.store(val, tmp, layout.align.abi);
793 tmp
794 } else {
795 reg_addr
796 }
797}
798
799fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>(
800 bx: &mut Builder<'_, 'll, 'tcx>,
801 va_list_addr: &'ll Value,
802 layout: TyAndLayout<'tcx>,
803) -> &'ll Value {
804 let dl = bx.cx.data_layout();
805 let ptr_align_abi = dl.data_layout().pointer_align().abi;
806
807 let overflow_arg_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.const_usize(8));
808
809 let overflow_arg_area_v = bx.load(bx.type_ptr(), overflow_arg_area_ptr, ptr_align_abi);
810 let overflow_arg_area_v = if layout.layout.align.bytes() > 8 {
818 round_pointer_up_to_alignment(bx, overflow_arg_area_v, layout.layout.align.abi)
819 } else {
820 overflow_arg_area_v
821 };
822
823 let mem_addr = overflow_arg_area_v;
825
826 let size_in_bytes = layout.layout.size().bytes();
831 let offset = bx.const_i32(size_in_bytes.next_multiple_of(8) as i32);
832 let overflow_arg_area = bx.inbounds_ptradd(overflow_arg_area_v, offset);
833 bx.store(overflow_arg_area, overflow_arg_area_ptr, ptr_align_abi);
834
835 mem_addr
836}
837
838fn emit_hexagon_va_arg_musl<'ll, 'tcx>(
839 bx: &mut Builder<'_, 'll, 'tcx>,
840 list: OperandRef<'tcx, &'ll Value>,
841 layout: TyAndLayout<'tcx>,
842) -> &'ll Value {
843 let va_list_addr = list.immediate();
855 let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi;
856 let ptr_size = bx.tcx().data_layout.pointer_size().bytes();
857
858 let maybe_reg = bx.append_sibling_block("va_arg.maybe_reg");
860 let from_overflow = bx.append_sibling_block("va_arg.from_overflow");
861 let end = bx.append_sibling_block("va_arg.end");
862
863 let current_ptr_addr = va_list_addr;
865 let end_ptr_addr = bx.inbounds_ptradd(va_list_addr, bx.const_usize(ptr_size));
866 let overflow_ptr_addr = bx.inbounds_ptradd(va_list_addr, bx.const_usize(2 * ptr_size));
867
868 let current_ptr = bx.load(bx.type_ptr(), current_ptr_addr, ptr_align_abi);
869 let end_ptr = bx.load(bx.type_ptr(), end_ptr_addr, ptr_align_abi);
870 let overflow_ptr = bx.load(bx.type_ptr(), overflow_ptr_addr, ptr_align_abi);
871
872 let type_size_bits = layout.size.bits();
875 let arg_align = if type_size_bits > 32 {
876 Align::from_bytes(8).unwrap()
877 } else {
878 Align::from_bytes(4).unwrap()
879 };
880 let aligned_current = round_pointer_up_to_alignment(bx, current_ptr, arg_align);
881
882 let arg_size = if type_size_bits > 32 { 8 } else { 4 };
885 let next_ptr = bx.inbounds_ptradd(aligned_current, bx.const_usize(arg_size));
886
887 let fits_in_regs = bx.icmp(IntPredicate::IntULE, next_ptr, end_ptr);
889 bx.cond_br(fits_in_regs, maybe_reg, from_overflow);
890
891 bx.switch_to_block(maybe_reg);
893 let reg_value_addr = aligned_current;
894 bx.store(next_ptr, current_ptr_addr, ptr_align_abi);
896 bx.br(end);
897
898 bx.switch_to_block(from_overflow);
900
901 let aligned_overflow = round_pointer_up_to_alignment(bx, overflow_ptr, arg_align);
903
904 let overflow_value_addr = aligned_overflow;
905 let next_overflow = bx.inbounds_ptradd(aligned_overflow, bx.const_usize(arg_size));
907 bx.store(next_overflow, overflow_ptr_addr, ptr_align_abi);
908
909 bx.store(next_overflow, current_ptr_addr, ptr_align_abi);
912 bx.br(end);
913
914 bx.switch_to_block(end);
916 let value_addr =
917 bx.phi(bx.type_ptr(), &[reg_value_addr, overflow_value_addr], &[maybe_reg, from_overflow]);
918 bx.load(layout.llvm_type(bx), value_addr, layout.align.abi)
919}
920
921fn emit_hexagon_va_arg_bare_metal<'ll, 'tcx>(
922 bx: &mut Builder<'_, 'll, 'tcx>,
923 list: OperandRef<'tcx, &'ll Value>,
924 layout: TyAndLayout<'tcx>,
925) -> &'ll Value {
926 let va_list_addr = list.immediate();
931 let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi;
932
933 let current_ptr = bx.load(bx.type_ptr(), va_list_addr, ptr_align_abi);
935
936 let ty_align = layout.align.abi;
938 let aligned_ptr = if ty_align.bytes() > 4 {
939 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!");
941 round_pointer_up_to_alignment(bx, current_ptr, ty_align)
942 } else {
943 current_ptr
944 };
945
946 let type_size = layout.size.bytes();
948 let offset = type_size.next_multiple_of(4); let next_ptr = bx.inbounds_ptradd(aligned_ptr, bx.const_usize(offset));
952 bx.store(next_ptr, va_list_addr, ptr_align_abi);
953
954 bx.load(layout.llvm_type(bx), aligned_ptr, layout.align.abi)
956}
957
958fn emit_xtensa_va_arg<'ll, 'tcx>(
959 bx: &mut Builder<'_, 'll, 'tcx>,
960 list: OperandRef<'tcx, &'ll Value>,
961 layout: TyAndLayout<'tcx>,
962) -> &'ll Value {
963 let va_list_addr = list.immediate();
980 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 layout: TyAndLayout<'tcx>,
1063) -> &'ll Value {
1064 if !!layout.is_zst() {
::core::panicking::panic("assertion failed: !layout.is_zst()")
};assert!(!layout.is_zst());
1067
1068 let target = &bx.cx.tcx.sess.target;
1069 let stability = target.supports_c_variadic_definitions();
1070
1071 match target.arch {
1072 Arch::X86 => {
1073 let allow_higher_align = if layout.ty == bx.tcx().types.f128 {
1079 AllowHigherAlign::Yes
1080 } else {
1081 AllowHigherAlign::No
1082 };
1083
1084 emit_ptr_va_arg(
1085 bx,
1086 addr,
1087 layout,
1088 PassMode::Direct,
1089 SlotSize::Bytes4,
1090 allow_higher_align,
1091 ForceRightAdjust::No,
1092 )
1093 }
1094 Arch::Arm64EC => emit_ptr_va_arg(
1095 bx,
1096 addr,
1097 layout,
1098 if layout.size.bytes() > 8 || !layout.size.bytes().is_power_of_two() {
1101 PassMode::Indirect
1102 } else {
1103 PassMode::Direct
1104 },
1105 SlotSize::Bytes8,
1106 AllowHigherAlign::No,
1107 ForceRightAdjust::No,
1108 ),
1109 Arch::AArch64 if target.is_like_windows || target.is_like_darwin => emit_ptr_va_arg(
1110 bx,
1111 addr,
1112 layout,
1113 PassMode::Direct,
1114 SlotSize::Bytes8,
1115 AllowHigherAlign::Yes,
1116 ForceRightAdjust::No,
1117 ),
1118 Arch::AArch64 => emit_aapcs_va_arg(bx, addr, layout),
1119 Arch::Arm => {
1120 if !(layout.size.bytes() <= 16) {
::core::panicking::panic("assertion failed: layout.size.bytes() <= 16")
};assert!(layout.size.bytes() <= 16);
1124
1125 emit_ptr_va_arg(
1126 bx,
1127 addr,
1128 layout,
1129 PassMode::Direct,
1130 SlotSize::Bytes4,
1131 AllowHigherAlign::Yes,
1132 ForceRightAdjust::No,
1133 )
1134 }
1135 Arch::S390x => emit_s390x_va_arg(bx, addr, layout),
1136 Arch::PowerPC => emit_powerpc_va_arg(bx, addr, layout),
1137 Arch::PowerPC64 => emit_ptr_va_arg(
1138 bx,
1139 addr,
1140 layout,
1141 PassMode::Direct,
1142 SlotSize::Bytes8,
1143 AllowHigherAlign::Yes,
1144 ForceRightAdjust::Yes,
1146 ),
1147 Arch::RiscV32 if target.llvm_abiname == LlvmAbi::Ilp32e => {
1148 {
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 { .. });
1149 ::rustc_span::macros::bug_impl(None,
format_args!("c-variadic calls with ilp32e use a custom ABI and are not currently implemented"),
Location::caller());bug!("c-variadic calls with ilp32e use a custom ABI and are not currently implemented");
1156 }
1157 Arch::RiscV32 | Arch::LoongArch32 => emit_ptr_va_arg(
1158 bx,
1159 addr,
1160 layout,
1161 if layout.size.bytes() > 2 * 4 { PassMode::Indirect } else { PassMode::Direct },
1162 SlotSize::Bytes4,
1163 AllowHigherAlign::Yes,
1164 ForceRightAdjust::No,
1165 ),
1166 Arch::RiscV64 | Arch::LoongArch64 => emit_ptr_va_arg(
1167 bx,
1168 addr,
1169 layout,
1170 if layout.size.bytes() > 2 * 8 { PassMode::Indirect } else { PassMode::Direct },
1171 SlotSize::Bytes8,
1172 AllowHigherAlign::Yes,
1173 ForceRightAdjust::No,
1174 ),
1175 Arch::AmdGpu => emit_ptr_va_arg(
1176 bx,
1177 addr,
1178 layout,
1179 PassMode::Direct,
1180 SlotSize::Bytes4,
1181 AllowHigherAlign::No,
1182 ForceRightAdjust::No,
1183 ),
1184 Arch::Nvptx64 => emit_ptr_va_arg(
1185 bx,
1186 addr,
1187 layout,
1188 PassMode::Direct,
1189 SlotSize::Bytes1,
1190 AllowHigherAlign::Yes,
1191 ForceRightAdjust::No,
1192 ),
1193 Arch::Wasm32 | Arch::Wasm64 => emit_ptr_va_arg(
1194 bx,
1195 addr,
1196 layout,
1197 if layout.is_aggregate() || layout.is_zst() || layout.is_1zst() {
1198 PassMode::Indirect
1199 } else {
1200 PassMode::Direct
1201 },
1202 SlotSize::Bytes4,
1203 AllowHigherAlign::Yes,
1204 ForceRightAdjust::No,
1205 ),
1206 Arch::CSky => emit_ptr_va_arg(
1207 bx,
1208 addr,
1209 layout,
1210 PassMode::Direct,
1211 SlotSize::Bytes4,
1212 AllowHigherAlign::Yes,
1213 ForceRightAdjust::No,
1214 ),
1215 Arch::X86_64 if target.is_like_windows => emit_ptr_va_arg(
1217 bx,
1218 addr,
1219 layout,
1220 if layout.size.bytes() > 8 || !layout.size.bytes().is_power_of_two() {
1221 PassMode::Indirect
1222 } else {
1223 PassMode::Direct
1224 },
1225 SlotSize::Bytes8,
1226 AllowHigherAlign::No,
1227 ForceRightAdjust::No,
1228 ),
1229 Arch::X86_64 => emit_x86_64_sysv64_va_arg(bx, addr, layout),
1231 Arch::Xtensa => emit_xtensa_va_arg(bx, addr, layout),
1232 Arch::Hexagon => match target.env {
1233 Env::Musl => emit_hexagon_va_arg_musl(bx, addr, layout),
1234 _ => emit_hexagon_va_arg_bare_metal(bx, addr, layout),
1235 },
1236 Arch::Sparc64 => emit_ptr_va_arg(
1237 bx,
1238 addr,
1239 layout,
1240 if layout.size.bytes() > 2 * 8 { PassMode::Indirect } else { PassMode::Direct },
1241 SlotSize::Bytes8,
1242 AllowHigherAlign::Yes,
1243 ForceRightAdjust::Yes,
1245 ),
1246 Arch::Sparc => {
1247 {
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 { .. });
1248
1249 let pass_mode = match layout.layout.backend_repr() {
1251 BackendRepr::Scalar(scalar) => match scalar.primitive() {
1252 Primitive::Float(Float::F128) => PassMode::Indirect,
1253 _ => PassMode::Direct,
1254 },
1255 _ => PassMode::Direct,
1256 };
1257
1258 emit_ptr_va_arg(
1259 bx,
1260 addr,
1261 layout,
1262 pass_mode,
1263 SlotSize::Bytes4,
1264 AllowHigherAlign::No,
1265 ForceRightAdjust::Yes,
1266 )
1267 }
1268 Arch::Mips | Arch::Mips32r6 | Arch::Mips64 | Arch::Mips64r6 => emit_ptr_va_arg(
1269 bx,
1270 addr,
1271 layout,
1272 PassMode::Direct,
1273 match &target.llvm_abiname {
1274 LlvmAbi::N32 | LlvmAbi::N64 => SlotSize::Bytes8,
1275 LlvmAbi::O32 => SlotSize::Bytes4,
1276 other => ::rustc_span::macros::bug_impl(None,
format_args!("unexpected LLVM ABI {0}", other), Location::caller())bug!("unexpected LLVM ABI {other}"),
1277 },
1278 AllowHigherAlign::Yes,
1279 match bx.tcx().sess.target.endian {
1283 Endian::Big => ForceRightAdjust::Yes,
1284 Endian::Little => ForceRightAdjust::No,
1285 },
1286 ),
1287
1288 Arch::Bpf => ::rustc_span::macros::bug_impl(None,
format_args!("bpf does not support c-variadic functions"),
Location::caller())bug!("bpf does not support c-variadic functions"),
1289 Arch::SpirV => ::rustc_span::macros::bug_impl(None,
format_args!("spirv does not support c-variadic functions"),
Location::caller())bug!("spirv does not support c-variadic functions"),
1290
1291 Arch::Avr | Arch::M68k | Arch::Msp430 => {
1292 {
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 { .. });
1293
1294 bx.va_arg(addr.immediate(), layout.llvm_type(bx.cx))
1296 }
1297
1298 Arch::Other(ref arch) => {
1299 {
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 { .. });
1300
1301 ::rustc_span::macros::bug_impl(None,
format_args!("c-variadic functions are not currently implemented for custom target {0}",
arch), Location::caller())bug!("c-variadic functions are not currently implemented for custom target {arch}")
1304 }
1305 }
1306}