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, Target};
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::unreachable_default_msg()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::unreachable_default_msg()unreachable!(),
99 Float::F64 | Float::F128 => { }
100 Float::PpcF128 => {
101 return if is_powerpc_softfloat_abi(&bx.cx.tcx.sess.target) {
102 Align::FOUR
103 } else {
104 Align::EIGHT
105 };
106 }
107 },
108 Primitive::Pointer(_) => { }
109 },
110 _ => { }
111 }
112
113 layout.align.abi
114}
115
116enum PassMode {
117 Direct,
118 Indirect,
119}
120
121enum SlotSize {
122 Bytes8 = 8,
123 Bytes4 = 4,
124 Bytes1 = 1,
125}
126
127enum AllowHigherAlign {
132 No,
133 Yes,
134}
135
136enum ForceRightAdjust {
152 No,
153 Yes,
154}
155
156fn emit_ptr_va_arg<'ll, 'tcx>(
157 bx: &mut Builder<'_, 'll, 'tcx>,
158 list: OperandRef<'tcx, &'ll Value>,
159 layout: TyAndLayout<'tcx>,
160 pass_mode: PassMode,
161 slot_size: SlotSize,
162 allow_higher_align: AllowHigherAlign,
163 force_right_adjust: ForceRightAdjust,
164) -> &'ll Value {
165 let indirect = #[allow(non_exhaustive_omitted_patterns)] match pass_mode {
PassMode::Indirect => true,
_ => false,
}matches!(pass_mode, PassMode::Indirect);
166 let allow_higher_align = #[allow(non_exhaustive_omitted_patterns)] match allow_higher_align {
AllowHigherAlign::Yes => true,
_ => false,
}matches!(allow_higher_align, AllowHigherAlign::Yes);
167 let force_right_adjust = #[allow(non_exhaustive_omitted_patterns)] match force_right_adjust {
ForceRightAdjust::Yes => true,
_ => false,
}matches!(force_right_adjust, ForceRightAdjust::Yes);
168 let slot_size = Align::from_bytes(slot_size as u64).unwrap();
169
170 let (llty, size, align) = if indirect {
171 (
172 bx.cx.layout_of(Ty::new_imm_ptr(bx.cx.tcx, layout.ty)).llvm_type(bx.cx),
173 bx.cx.data_layout().pointer_size(),
174 bx.cx.data_layout().pointer_align().abi,
175 )
176 } else {
177 (layout.llvm_type(bx.cx), layout.size, get_param_type_alignment(bx, layout))
178 };
179 let (addr, addr_align) = emit_direct_ptr_va_arg(
180 bx,
181 list,
182 size,
183 align,
184 slot_size,
185 allow_higher_align,
186 force_right_adjust,
187 );
188 if indirect {
189 let tmp_ret = bx.load(llty, addr, addr_align);
190 bx.load(layout.llvm_type(bx.cx), tmp_ret, align)
191 } else {
192 bx.load(llty, addr, addr_align)
193 }
194}
195
196fn emit_aapcs_va_arg<'ll, 'tcx>(
197 bx: &mut Builder<'_, 'll, 'tcx>,
198 list: OperandRef<'tcx, &'ll Value>,
199 layout: TyAndLayout<'tcx>,
200) -> &'ll Value {
201 let dl = bx.cx.data_layout();
202
203 let va_list_addr = list.immediate();
214
215 let ptr_offset = 8;
220 let i32_offset = 4;
221 let gr_top = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(ptr_offset));
222 let vr_top = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(2 * ptr_offset));
223 let gr_offs = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(3 * ptr_offset));
224 let vr_offs = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(3 * ptr_offset + i32_offset));
225
226 let maybe_reg = bx.append_sibling_block("va_arg.maybe_reg");
227 let in_reg = bx.append_sibling_block("va_arg.in_reg");
228 let on_stack = bx.append_sibling_block("va_arg.on_stack");
229 let end = bx.append_sibling_block("va_arg.end");
230 let zero = bx.const_i32(0);
231 let offset_align = Align::FOUR;
232
233 let gr_type = layout.ty.is_any_ptr() || layout.ty.is_integral();
234 let (reg_off, reg_top, slot_size) = if gr_type {
235 let nreg = layout.size.bytes().div_ceil(8);
236 (gr_offs, gr_top, nreg * 8)
237 } else {
238 let nreg = layout.size.bytes().div_ceil(16);
239 (vr_offs, vr_top, nreg * 16)
240 };
241
242 let mut reg_off_v = bx.load(bx.type_i32(), reg_off, offset_align);
244 let use_stack = bx.icmp(IntPredicate::IntSGE, reg_off_v, zero);
245 bx.cond_br(use_stack, on_stack, maybe_reg);
246
247 bx.switch_to_block(maybe_reg);
252 if gr_type && layout.align.bytes() > 8 {
253 reg_off_v = bx.add(reg_off_v, bx.const_i32(15));
254 reg_off_v = bx.and(reg_off_v, bx.const_i32(-16));
255 }
256 let new_reg_off_v = bx.add(reg_off_v, bx.const_i32(slot_size as i32));
257
258 bx.store(new_reg_off_v, reg_off, offset_align);
259
260 let use_stack = bx.icmp(IntPredicate::IntSGT, new_reg_off_v, zero);
263 bx.cond_br(use_stack, on_stack, in_reg);
264
265 bx.switch_to_block(in_reg);
266 let top_type = bx.type_ptr();
267 let top = bx.load(top_type, reg_top, dl.pointer_align().abi);
268
269 let mut reg_addr = bx.ptradd(top, reg_off_v);
271 if bx.tcx().sess.target.endian == Endian::Big && layout.size.bytes() != slot_size {
272 let offset = bx.const_i32((slot_size - layout.size.bytes()) as i32);
274 reg_addr = bx.ptradd(reg_addr, offset);
275 }
276 let reg_type = layout.llvm_type(bx);
277 let reg_value = bx.load(reg_type, reg_addr, layout.align.abi);
278 bx.br(end);
279
280 bx.switch_to_block(on_stack);
282 let stack_value = emit_ptr_va_arg(
283 bx,
284 list,
285 layout,
286 PassMode::Direct,
287 SlotSize::Bytes8,
288 AllowHigherAlign::Yes,
289 ForceRightAdjust::No,
290 );
291 bx.br(end);
292
293 bx.switch_to_block(end);
294 let val =
295 bx.phi(layout.immediate_llvm_type(bx), &[reg_value, stack_value], &[in_reg, on_stack]);
296
297 val
298}
299
300fn is_powerpc_softfloat_abi(target: &Target) -> bool {
301 if target.arch != Arch::PowerPC {
302 return false;
303 }
304
305 match target.rustc_abi {
306 Some(RustcAbi::Softfloat) => ::rustc_span::macros::bug_impl(None,
format_args!("there are currently no powerpc softfloat targets"),
Location::caller())bug!("there are currently no powerpc softfloat targets"),
307 Some(RustcAbi::PowerPcSpe) => true, None => false,
309 Some(other) => ::rustc_span::macros::bug_impl(None,
format_args!("unexpected powerpc abi {0:?}", other), Location::caller())bug!("unexpected powerpc abi {other:?}"),
310 }
311}
312
313fn emit_powerpc_va_arg<'ll, 'tcx>(
314 bx: &mut Builder<'_, 'll, 'tcx>,
315 list: OperandRef<'tcx, &'ll Value>,
316 layout: TyAndLayout<'tcx>,
317) -> &'ll Value {
318 let dl = bx.cx.data_layout();
319
320 let va_list_addr = list.immediate();
328
329 let layout = layout.peel_transparent_wrappers_from_non_1zst(bx.cx);
331
332 let is_indirect = false;
334
335 let is_int = match layout.layout.backend_repr() {
336 BackendRepr::Scalar(scalar) => match scalar.primitive() {
337 rustc_abi::Primitive::Int(_, _) | rustc_abi::Primitive::Pointer(_) => true,
338 rustc_abi::Primitive::Float(_) => false,
339 },
340 _ => {
::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"),
341 };
342
343 let is_passed_in_fprs = !(is_int || is_powerpc_softfloat_abi(&bx.cx.tcx.sess.target));
344
345 let num_regs_addr = if !is_passed_in_fprs {
346 va_list_addr } else {
348 bx.inbounds_ptradd(va_list_addr, bx.const_usize(1)) };
350
351 let mut num_regs = bx.load(bx.type_i8(), num_regs_addr, dl.i8_align);
352
353 if layout.size.bytes() == 8 && !is_passed_in_fprs {
355 num_regs = bx.add(num_regs, bx.const_u8(1));
356 num_regs = bx.and(num_regs, bx.const_u8(0b1111_1110));
357 }
358
359 let max_regs = 8u8;
360 let reg_size = if is_passed_in_fprs { 8 } else { 4 };
361 let reg_incr = layout.size.bytes().div_ceil(u64::from(reg_size)) as u8;
362 let use_regs = bx.icmp(IntPredicate::IntULE, num_regs, bx.const_u8(max_regs - reg_incr));
363
364 let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi;
365
366 let in_reg = bx.append_sibling_block("va_arg.in_reg");
367 let in_mem = bx.append_sibling_block("va_arg.in_mem");
368 let end = bx.append_sibling_block("va_arg.end");
369
370 bx.cond_br(use_regs, in_reg, in_mem);
371
372 let reg_addr = {
373 bx.switch_to_block(in_reg);
374
375 let reg_save_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(1 + 1 + 2 + 4));
376 let mut reg_addr = bx.load(bx.type_ptr(), reg_save_area_ptr, ptr_align_abi);
377
378 if is_passed_in_fprs {
380 reg_addr = bx.inbounds_ptradd(reg_addr, bx.cx.const_usize(32))
381 }
382
383 let reg_offset = bx.mul(num_regs, bx.cx().const_u8(reg_size));
386 let reg_addr = bx.inbounds_ptradd(reg_addr, reg_offset);
387
388 let new_num_regs = bx.add(num_regs, bx.cx.const_u8(reg_incr));
390 bx.store(new_num_regs, num_regs_addr, dl.i8_align);
391
392 bx.br(end);
393
394 reg_addr
395 };
396
397 let mem_addr = {
398 bx.switch_to_block(in_mem);
399
400 bx.store(bx.const_u8(max_regs), num_regs_addr, dl.i8_align);
401
402 let overflow_area_align = Align::FOUR;
404
405 let size = if !is_indirect {
406 layout.layout.size.align_to(overflow_area_align)
407 } else {
408 dl.pointer_size()
409 };
410
411 let overflow_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(1 + 1 + 2));
412 let mut overflow_area = bx.load(bx.type_ptr(), overflow_area_ptr, ptr_align_abi);
413
414 let align = get_param_type_alignment(bx, layout);
416 if align > overflow_area_align {
417 overflow_area = round_pointer_up_to_alignment(bx, overflow_area, align);
418 }
419
420 let mem_addr = overflow_area;
421
422 overflow_area = bx.inbounds_ptradd(overflow_area, bx.const_usize(size.bytes()));
424 bx.store(overflow_area, overflow_area_ptr, ptr_align_abi);
425
426 bx.br(end);
427
428 mem_addr
429 };
430
431 bx.switch_to_block(end);
433 let val_addr = bx.phi(bx.type_ptr(), &[reg_addr, mem_addr], &[in_reg, in_mem]);
434 let val_type = layout.llvm_type(bx);
435 let val_addr =
436 if is_indirect { bx.load(bx.cx.type_ptr(), val_addr, ptr_align_abi) } else { val_addr };
437 let align = get_param_type_alignment(bx, layout);
438 bx.load(val_type, val_addr, align)
439}
440
441fn emit_s390x_va_arg<'ll, 'tcx>(
442 bx: &mut Builder<'_, 'll, 'tcx>,
443 list: OperandRef<'tcx, &'ll Value>,
444 layout: TyAndLayout<'tcx>,
445) -> &'ll Value {
446 let dl = bx.cx.data_layout();
447
448 let va_list_addr = list.immediate();
458
459 let i64_offset = 8;
462 let ptr_offset = 8;
463 let gpr = va_list_addr;
464 let fpr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(i64_offset));
465 let overflow_arg_area = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(2 * i64_offset));
466 let reg_save_area =
467 bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(2 * i64_offset + ptr_offset));
468
469 let in_reg = bx.append_sibling_block("va_arg.in_reg");
470 let in_mem = bx.append_sibling_block("va_arg.in_mem");
471 let end = bx.append_sibling_block("va_arg.end");
472 let ptr_align_abi = dl.pointer_align().abi;
473
474 let target_ty_size = layout.size.bytes();
476 let indirect: bool = target_ty_size > 8 || !target_ty_size.is_power_of_two();
477 let unpadded_size = if indirect { 8 } else { target_ty_size };
478 let padded_size = 8;
479 let padding = padded_size - unpadded_size;
480
481 let is_single_fp_element = |layout: TyAndLayout<'_>| -> bool {
483 match layout.layout.backend_repr() {
484 BackendRepr::Scalar(scalar) => match scalar.primitive() {
485 Primitive::Float(Float::F16 | Float::F32 | Float::F64) => true,
486 Primitive::Float(Float::F128) => false,
487 Primitive::Int(_, _) | Primitive::Pointer(_) => false,
488 Primitive::Float(Float::F16B) => {
489 ::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")
490 }
491 Primitive::Float(Float::PpcF128) => {
492 ::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")
493 }
494 },
495
496 _ => false,
497 }
498 };
499
500 let gpr_type = indirect || !is_single_fp_element(layout);
501 let (max_regs, reg_count, reg_save_index, reg_padding) =
502 if gpr_type { (5, gpr, 2, padding) } else { (4, fpr, 16, 0) };
503
504 let reg_count_v = bx.load(bx.type_i64(), reg_count, Align::from_bytes(8).unwrap());
506 let use_regs = bx.icmp(IntPredicate::IntULT, reg_count_v, bx.const_u64(max_regs));
507 bx.cond_br(use_regs, in_reg, in_mem);
508
509 bx.switch_to_block(in_reg);
511
512 let reg_ptr_v = bx.load(bx.type_ptr(), reg_save_area, ptr_align_abi);
514 let scaled_reg_count = bx.mul(reg_count_v, bx.const_u64(8));
515 let reg_off = bx.add(scaled_reg_count, bx.const_u64(reg_save_index * 8 + reg_padding));
516 let reg_addr = bx.ptradd(reg_ptr_v, reg_off);
517
518 let new_reg_count_v = bx.add(reg_count_v, bx.const_u64(1));
520 bx.store(new_reg_count_v, reg_count, Align::from_bytes(8).unwrap());
521 bx.br(end);
522
523 bx.switch_to_block(in_mem);
525
526 let arg_ptr_v = bx.load(bx.type_ptr(), overflow_arg_area, ptr_align_abi);
528 let arg_off = bx.const_u64(padding);
529 let mem_addr = bx.ptradd(arg_ptr_v, arg_off);
530
531 let arg_size = bx.cx().const_u64(padded_size);
533 let new_arg_ptr_v = bx.inbounds_ptradd(arg_ptr_v, arg_size);
534 bx.store(new_arg_ptr_v, overflow_arg_area, ptr_align_abi);
535 bx.br(end);
536
537 bx.switch_to_block(end);
539 let val_addr = bx.phi(bx.type_ptr(), &[reg_addr, mem_addr], &[in_reg, in_mem]);
540 let val_type = layout.llvm_type(bx);
541 let val_addr =
542 if indirect { bx.load(bx.cx.type_ptr(), val_addr, ptr_align_abi) } else { val_addr };
543 bx.load(val_type, val_addr, layout.align.abi)
544}
545
546fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>(
547 bx: &mut Builder<'_, 'll, 'tcx>,
548 list: OperandRef<'tcx, &'ll Value>,
549 layout: TyAndLayout<'tcx>,
550) -> &'ll Value {
551 let dl = bx.cx.data_layout();
552
553 let va_list_addr = list.immediate();
567
568 let layout = layout.peel_transparent_wrappers_from_non_1zst(bx.cx);
585
586 let mut num_gp_registers = 0;
594 let mut num_fp_registers = 0;
595
596 let mut registers_for_primitive = |p| match p {
597 Primitive::Int(integer, _is_signed) => {
598 num_gp_registers += integer.size().bytes().div_ceil(8) as u32;
599 }
600 Primitive::Float(float) => {
601 num_fp_registers += float.size().bytes().div_ceil(16) as u32;
602 }
603 Primitive::Pointer(_) => {
604 num_gp_registers += 1;
605 }
606 };
607
608 match layout.layout.backend_repr() {
609 BackendRepr::Scalar(scalar) => {
610 registers_for_primitive(scalar.primitive());
611 }
612 BackendRepr::ScalarPair { a: scalar1, b: scalar2, b_offset: _ } => {
613 registers_for_primitive(scalar1.primitive());
614 registers_for_primitive(scalar2.primitive());
615 }
616 BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => {
617 {
::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!(
619 "No x86-64 SysV va_arg implementation for {:?}",
620 layout.layout.backend_repr()
621 )
622 }
623 BackendRepr::Memory { .. } => {
624 let mem_addr = x86_64_sysv64_va_arg_from_memory(bx, va_list_addr, layout);
625 return bx.load(layout.llvm_type(bx), mem_addr, layout.align.abi);
626 }
627 };
628
629 let unsigned_int_offset = 4;
635 let ptr_offset = bx.tcx().data_layout.pointer_size().bytes();
636
637 let gp_offset_ptr = va_list_addr;
638 let fp_offset_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(unsigned_int_offset));
639
640 let gp_offset_v = bx.load(bx.type_i32(), gp_offset_ptr, Align::EIGHT);
641 let fp_offset_v = bx.load(bx.type_i32(), fp_offset_ptr, Align::FOUR);
642
643 let mut use_regs = bx.const_bool(false);
644
645 if num_gp_registers > 0 {
646 let max_offset_val = 48u32 - num_gp_registers * 8;
647 let fits_in_gp = bx.icmp(IntPredicate::IntULE, gp_offset_v, bx.const_u32(max_offset_val));
648 use_regs = fits_in_gp;
649 }
650
651 if num_fp_registers > 0 {
652 let max_offset_val = 176u32 - num_fp_registers * 16;
653 let fits_in_fp = bx.icmp(IntPredicate::IntULE, fp_offset_v, bx.const_u32(max_offset_val));
654 use_regs = if num_gp_registers > 0 { bx.and(use_regs, fits_in_fp) } else { fits_in_fp };
655 }
656
657 let in_reg = bx.append_sibling_block("va_arg.in_reg");
658 let in_mem = bx.append_sibling_block("va_arg.in_mem");
659 let end = bx.append_sibling_block("va_arg.end");
660
661 bx.cond_br(use_regs, in_reg, in_mem);
662
663 bx.switch_to_block(in_reg);
665
666 let reg_save_area_ptr =
677 bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(2 * unsigned_int_offset + ptr_offset));
678 let reg_save_area_v = bx.load(bx.type_ptr(), reg_save_area_ptr, dl.pointer_align().abi);
679
680 let reg_addr = match layout.layout.backend_repr() {
681 BackendRepr::Scalar(scalar) => match scalar.primitive() {
682 Primitive::Int(_, _) | Primitive::Pointer(_) => {
683 let reg_addr = bx.inbounds_ptradd(reg_save_area_v, gp_offset_v);
684
685 let gp_align = Align::from_bytes(8).unwrap();
687 copy_to_temporary_if_more_aligned(bx, reg_addr, layout, gp_align)
688 }
689 Primitive::Float(_) => bx.inbounds_ptradd(reg_save_area_v, fp_offset_v),
690 },
691 BackendRepr::ScalarPair { a: scalar1, b: scalar2, b_offset: offset } => {
692 let ty_lo = bx.cx().scalar_pair_element_backend_type(layout, 0, false);
693 let ty_hi = bx.cx().scalar_pair_element_backend_type(layout, 1, false);
694
695 let align_lo = layout.field(bx.cx, 0).layout.align().abi;
696 let align_hi = layout.field(bx.cx, 1).layout.align().abi;
697
698 match (scalar1.primitive(), scalar2.primitive()) {
699 (Primitive::Float(_), Primitive::Float(_)) => {
700 let reg_lo_addr = bx.inbounds_ptradd(reg_save_area_v, fp_offset_v);
707 let reg_hi_addr = bx.inbounds_ptradd(reg_lo_addr, bx.const_i32(16));
708
709 let align = layout.layout.align().abi;
710 let tmp = bx.alloca(layout.size, layout.align.abi);
711
712 let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo);
713 let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi);
714
715 let field0 = tmp;
716 let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset.bytes() as u32));
717
718 bx.store(reg_lo, field0, align);
719 bx.store(reg_hi, field1, align);
720
721 tmp
722 }
723 (Primitive::Float(_), _) | (_, Primitive::Float(_)) => {
724 let gp_addr = bx.inbounds_ptradd(reg_save_area_v, gp_offset_v);
725 let fp_addr = bx.inbounds_ptradd(reg_save_area_v, fp_offset_v);
726
727 let (reg_lo_addr, reg_hi_addr) = match scalar1.primitive() {
728 Primitive::Float(_) => (fp_addr, gp_addr),
729 Primitive::Int(_, _) | Primitive::Pointer(_) => (gp_addr, fp_addr),
730 };
731
732 let tmp = bx.alloca(layout.size, layout.align.abi);
733
734 let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo);
735 let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi);
736
737 let field0 = tmp;
738 let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset.bytes() as u32));
739
740 bx.store(reg_lo, field0, align_lo);
741 bx.store(reg_hi, field1, align_hi);
742
743 tmp
744 }
745 (_, _) => {
746 let reg_addr = bx.inbounds_ptradd(reg_save_area_v, gp_offset_v);
748
749 let gp_align = Align::from_bytes(8).unwrap();
751 copy_to_temporary_if_more_aligned(bx, reg_addr, layout, gp_align)
752 }
753 }
754 }
755 BackendRepr::SimdVector { .. }
757 | BackendRepr::SimdScalableVector { .. }
758 | BackendRepr::Memory { .. } => ::core::panicking::unreachable_default_msg()unreachable!(),
759 };
760
761 if num_gp_registers > 0 {
764 let offset = bx.const_u32(num_gp_registers * 8);
765 let sum = bx.add(gp_offset_v, offset);
766 bx.store(sum, gp_offset_ptr, Align::from_bytes(8).unwrap());
768 }
769
770 if num_fp_registers > 0 {
772 let offset = bx.const_u32(num_fp_registers * 16);
773 let sum = bx.add(fp_offset_v, offset);
774 bx.store(sum, fp_offset_ptr, Align::FOUR);
775 }
776
777 bx.br(end);
778
779 bx.switch_to_block(in_mem);
780 let mem_addr = x86_64_sysv64_va_arg_from_memory(bx, va_list_addr, layout);
781 bx.br(end);
782
783 bx.switch_to_block(end);
784
785 let val_type = layout.llvm_type(bx);
786 let val_addr = bx.phi(bx.type_ptr(), &[reg_addr, mem_addr], &[in_reg, in_mem]);
787
788 bx.load(val_type, val_addr, layout.align.abi)
789}
790
791fn copy_to_temporary_if_more_aligned<'ll, 'tcx>(
793 bx: &mut Builder<'_, 'll, 'tcx>,
794 reg_addr: &'ll Value,
795 layout: TyAndLayout<'tcx>,
796 src_align: Align,
797) -> &'ll Value {
798 if layout.layout.align.abi > src_align {
799 if !layout.ty.is_integral() {
::core::panicking::panic("assertion failed: layout.ty.is_integral()")
};assert!(layout.ty.is_integral());
800
801 let tmp = bx.alloca(layout.size, layout.align.abi);
803 let val = bx.load(layout.llvm_type(bx), reg_addr, src_align);
804 bx.store(val, tmp, layout.align.abi);
805 tmp
806 } else {
807 reg_addr
808 }
809}
810
811fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>(
812 bx: &mut Builder<'_, 'll, 'tcx>,
813 va_list_addr: &'ll Value,
814 layout: TyAndLayout<'tcx>,
815) -> &'ll Value {
816 let dl = bx.cx.data_layout();
817 let ptr_align_abi = dl.data_layout().pointer_align().abi;
818
819 let overflow_arg_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.const_usize(8));
820
821 let overflow_arg_area_v = bx.load(bx.type_ptr(), overflow_arg_area_ptr, ptr_align_abi);
822 let overflow_arg_area_v = if layout.layout.align.bytes() > 8 {
830 round_pointer_up_to_alignment(bx, overflow_arg_area_v, layout.layout.align.abi)
831 } else {
832 overflow_arg_area_v
833 };
834
835 let mem_addr = overflow_arg_area_v;
837
838 let size_in_bytes = layout.layout.size().bytes();
843 let offset = bx.const_i32(size_in_bytes.next_multiple_of(8) as i32);
844 let overflow_arg_area = bx.inbounds_ptradd(overflow_arg_area_v, offset);
845 bx.store(overflow_arg_area, overflow_arg_area_ptr, ptr_align_abi);
846
847 mem_addr
848}
849
850fn emit_hexagon_va_arg_musl<'ll, 'tcx>(
851 bx: &mut Builder<'_, 'll, 'tcx>,
852 list: OperandRef<'tcx, &'ll Value>,
853 layout: TyAndLayout<'tcx>,
854) -> &'ll Value {
855 let va_list_addr = list.immediate();
867 let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi;
868 let ptr_size = bx.tcx().data_layout.pointer_size().bytes();
869
870 let maybe_reg = bx.append_sibling_block("va_arg.maybe_reg");
872 let from_overflow = bx.append_sibling_block("va_arg.from_overflow");
873 let end = bx.append_sibling_block("va_arg.end");
874
875 let current_ptr_addr = va_list_addr;
877 let end_ptr_addr = bx.inbounds_ptradd(va_list_addr, bx.const_usize(ptr_size));
878 let overflow_ptr_addr = bx.inbounds_ptradd(va_list_addr, bx.const_usize(2 * ptr_size));
879
880 let current_ptr = bx.load(bx.type_ptr(), current_ptr_addr, ptr_align_abi);
881 let end_ptr = bx.load(bx.type_ptr(), end_ptr_addr, ptr_align_abi);
882 let overflow_ptr = bx.load(bx.type_ptr(), overflow_ptr_addr, ptr_align_abi);
883
884 let type_size_bits = layout.size.bits();
887 let arg_align = if type_size_bits > 32 { Align::EIGHT } else { Align::FOUR };
888 let aligned_current = round_pointer_up_to_alignment(bx, current_ptr, arg_align);
889
890 let arg_size = if type_size_bits > 32 { 8 } else { 4 };
893 let next_ptr = bx.inbounds_ptradd(aligned_current, bx.const_usize(arg_size));
894
895 let fits_in_regs = bx.icmp(IntPredicate::IntULE, next_ptr, end_ptr);
897 bx.cond_br(fits_in_regs, maybe_reg, from_overflow);
898
899 bx.switch_to_block(maybe_reg);
901 let reg_value_addr = aligned_current;
902 bx.store(next_ptr, current_ptr_addr, ptr_align_abi);
904 bx.br(end);
905
906 bx.switch_to_block(from_overflow);
908
909 let aligned_overflow = round_pointer_up_to_alignment(bx, overflow_ptr, arg_align);
911
912 let overflow_value_addr = aligned_overflow;
913 let next_overflow = bx.inbounds_ptradd(aligned_overflow, bx.const_usize(arg_size));
915 bx.store(next_overflow, overflow_ptr_addr, ptr_align_abi);
916
917 bx.store(next_overflow, current_ptr_addr, ptr_align_abi);
920 bx.br(end);
921
922 bx.switch_to_block(end);
924 let value_addr =
925 bx.phi(bx.type_ptr(), &[reg_value_addr, overflow_value_addr], &[maybe_reg, from_overflow]);
926 bx.load(layout.llvm_type(bx), value_addr, layout.align.abi)
927}
928
929fn emit_hexagon_va_arg_bare_metal<'ll, 'tcx>(
930 bx: &mut Builder<'_, 'll, 'tcx>,
931 list: OperandRef<'tcx, &'ll Value>,
932 layout: TyAndLayout<'tcx>,
933) -> &'ll Value {
934 let va_list_addr = list.immediate();
939 let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi;
940
941 let current_ptr = bx.load(bx.type_ptr(), va_list_addr, ptr_align_abi);
943
944 let ty_align = layout.align.abi;
946 let aligned_ptr = if ty_align.bytes() > 4 {
947 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!");
949 round_pointer_up_to_alignment(bx, current_ptr, ty_align)
950 } else {
951 current_ptr
952 };
953
954 let type_size = layout.size.bytes();
956 let offset = type_size.next_multiple_of(4); let next_ptr = bx.inbounds_ptradd(aligned_ptr, bx.const_usize(offset));
960 bx.store(next_ptr, va_list_addr, ptr_align_abi);
961
962 bx.load(layout.llvm_type(bx), aligned_ptr, layout.align.abi)
964}
965
966fn emit_xtensa_va_arg<'ll, 'tcx>(
967 bx: &mut Builder<'_, 'll, 'tcx>,
968 list: OperandRef<'tcx, &'ll Value>,
969 layout: TyAndLayout<'tcx>,
970) -> &'ll Value {
971 let va_list_addr = list.immediate();
988 let from_stack = bx.append_sibling_block("va_arg.from_stack");
990 let from_regsave = bx.append_sibling_block("va_arg.from_regsave");
991 let end = bx.append_sibling_block("va_arg.end");
992 let ptr_align_abi = bx.tcx().data_layout.pointer_align().abi;
993
994 let va_reg_offset = 4;
996 let va_ndx_offset = va_reg_offset + 4;
997 let offset_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(va_ndx_offset));
998
999 let offset = bx.load(bx.type_i32(), offset_ptr, bx.tcx().data_layout.i32_align);
1000 let offset = round_up_to_alignment(bx, offset, layout.align.abi);
1001
1002 let slot_size = layout.size.align_to(Align::FOUR).bytes() as i32;
1003
1004 let offset_next = bx.add(offset, bx.const_i32(slot_size));
1006
1007 let regsave_size = bx.const_i32(24);
1010 let use_regsave = bx.icmp(IntPredicate::IntULE, offset_next, regsave_size);
1011 bx.cond_br(use_regsave, from_regsave, from_stack);
1012
1013 bx.switch_to_block(from_regsave);
1014 bx.store(offset_next, offset_ptr, ptr_align_abi);
1016
1017 let regsave_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(va_reg_offset));
1019 let regsave_area = bx.load(bx.type_ptr(), regsave_area_ptr, ptr_align_abi);
1020 let regsave_value_ptr = bx.inbounds_ptradd(regsave_area, offset);
1021 bx.br(end);
1022
1023 bx.switch_to_block(from_stack);
1024
1025 let stack_offset_start = bx.const_i32(32);
1031 let needs_correction = bx.icmp(IntPredicate::IntULE, offset, stack_offset_start);
1032 let offset_corrected = bx.select(needs_correction, stack_offset_start, offset);
1033
1034 let offset_next_corrected = bx.add(offset_corrected, bx.const_i32(slot_size));
1037 bx.store(offset_next_corrected, offset_ptr, ptr_align_abi);
1039
1040 let stack_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(0));
1042 let stack_area = bx.load(bx.type_ptr(), stack_area_ptr, ptr_align_abi);
1043 let stack_value_ptr = bx.inbounds_ptradd(stack_area, offset_corrected);
1044 bx.br(end);
1045
1046 bx.switch_to_block(end);
1047
1048 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);
1058 let value_ptr =
1059 bx.phi(bx.type_ptr(), &[regsave_value_ptr, stack_value_ptr], &[from_regsave, from_stack]);
1060 return bx.load(layout.llvm_type(bx), value_ptr, layout.align.abi);
1061}
1062
1063pub(super) fn emit_va_arg<'ll, 'tcx>(
1068 bx: &mut Builder<'_, 'll, 'tcx>,
1069 addr: OperandRef<'tcx, &'ll Value>,
1070 layout: TyAndLayout<'tcx>,
1071) -> &'ll Value {
1072 if !!layout.is_zst() {
::core::panicking::panic("assertion failed: !layout.is_zst()")
};assert!(!layout.is_zst());
1075
1076 let target = &bx.cx.tcx.sess.target;
1077 let stability = target.supports_c_variadic_definitions();
1078
1079 match target.arch {
1080 Arch::X86 => {
1081 let allow_higher_align = if layout.ty == bx.tcx().types.f128 {
1087 AllowHigherAlign::Yes
1088 } else {
1089 AllowHigherAlign::No
1090 };
1091
1092 emit_ptr_va_arg(
1093 bx,
1094 addr,
1095 layout,
1096 PassMode::Direct,
1097 SlotSize::Bytes4,
1098 allow_higher_align,
1099 ForceRightAdjust::No,
1100 )
1101 }
1102 Arch::Arm64EC => emit_ptr_va_arg(
1103 bx,
1104 addr,
1105 layout,
1106 if layout.size.bytes() > 8 || !layout.size.bytes().is_power_of_two() {
1109 PassMode::Indirect
1110 } else {
1111 PassMode::Direct
1112 },
1113 SlotSize::Bytes8,
1114 AllowHigherAlign::No,
1115 ForceRightAdjust::No,
1116 ),
1117 Arch::AArch64 if target.is_like_windows || target.is_like_darwin => emit_ptr_va_arg(
1118 bx,
1119 addr,
1120 layout,
1121 PassMode::Direct,
1122 SlotSize::Bytes8,
1123 AllowHigherAlign::Yes,
1124 ForceRightAdjust::No,
1125 ),
1126 Arch::AArch64 => emit_aapcs_va_arg(bx, addr, layout),
1127 Arch::Arm => {
1128 if !(layout.size.bytes() <= 16) {
::core::panicking::panic("assertion failed: layout.size.bytes() <= 16")
};assert!(layout.size.bytes() <= 16);
1132
1133 emit_ptr_va_arg(
1134 bx,
1135 addr,
1136 layout,
1137 PassMode::Direct,
1138 SlotSize::Bytes4,
1139 AllowHigherAlign::Yes,
1140 ForceRightAdjust::No,
1141 )
1142 }
1143 Arch::S390x => emit_s390x_va_arg(bx, addr, layout),
1144 Arch::PowerPC => emit_powerpc_va_arg(bx, addr, layout),
1145 Arch::PowerPC64 => emit_ptr_va_arg(
1146 bx,
1147 addr,
1148 layout,
1149 PassMode::Direct,
1150 SlotSize::Bytes8,
1151 AllowHigherAlign::Yes,
1152 ForceRightAdjust::Yes,
1154 ),
1155 Arch::RiscV32 if target.llvm_abiname == LlvmAbi::Ilp32e => {
1156 {
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 { .. });
1157 ::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");
1164 }
1165 Arch::RiscV32 | Arch::LoongArch32 => emit_ptr_va_arg(
1166 bx,
1167 addr,
1168 layout,
1169 if layout.size.bytes() > 2 * 4 { PassMode::Indirect } else { PassMode::Direct },
1170 SlotSize::Bytes4,
1171 AllowHigherAlign::Yes,
1172 ForceRightAdjust::No,
1173 ),
1174 Arch::RiscV64 | Arch::LoongArch64 => emit_ptr_va_arg(
1175 bx,
1176 addr,
1177 layout,
1178 if layout.size.bytes() > 2 * 8 { PassMode::Indirect } else { PassMode::Direct },
1179 SlotSize::Bytes8,
1180 AllowHigherAlign::Yes,
1181 ForceRightAdjust::No,
1182 ),
1183 Arch::AmdGpu => emit_ptr_va_arg(
1184 bx,
1185 addr,
1186 layout,
1187 PassMode::Direct,
1188 SlotSize::Bytes4,
1189 AllowHigherAlign::No,
1190 ForceRightAdjust::No,
1191 ),
1192 Arch::Nvptx64 => emit_ptr_va_arg(
1193 bx,
1194 addr,
1195 layout,
1196 PassMode::Direct,
1197 SlotSize::Bytes1,
1198 AllowHigherAlign::Yes,
1199 ForceRightAdjust::No,
1200 ),
1201 Arch::Wasm32 | Arch::Wasm64 => emit_ptr_va_arg(
1202 bx,
1203 addr,
1204 layout,
1205 if layout.is_aggregate() || layout.is_zst() || layout.is_1zst() {
1206 PassMode::Indirect
1207 } else {
1208 PassMode::Direct
1209 },
1210 SlotSize::Bytes4,
1211 AllowHigherAlign::Yes,
1212 ForceRightAdjust::No,
1213 ),
1214 Arch::CSky => emit_ptr_va_arg(
1215 bx,
1216 addr,
1217 layout,
1218 PassMode::Direct,
1219 SlotSize::Bytes4,
1220 AllowHigherAlign::Yes,
1221 ForceRightAdjust::No,
1222 ),
1223 Arch::X86_64 if target.is_like_windows => emit_ptr_va_arg(
1225 bx,
1226 addr,
1227 layout,
1228 if layout.size.bytes() > 8 || !layout.size.bytes().is_power_of_two() {
1229 PassMode::Indirect
1230 } else {
1231 PassMode::Direct
1232 },
1233 SlotSize::Bytes8,
1234 AllowHigherAlign::No,
1235 ForceRightAdjust::No,
1236 ),
1237 Arch::X86_64 => emit_x86_64_sysv64_va_arg(bx, addr, layout),
1239 Arch::Xtensa => emit_xtensa_va_arg(bx, addr, layout),
1240 Arch::Hexagon => match target.env {
1241 Env::Musl => emit_hexagon_va_arg_musl(bx, addr, layout),
1242 _ => emit_hexagon_va_arg_bare_metal(bx, addr, layout),
1243 },
1244 Arch::Sparc64 => emit_ptr_va_arg(
1245 bx,
1246 addr,
1247 layout,
1248 if layout.size.bytes() > 2 * 8 { PassMode::Indirect } else { PassMode::Direct },
1249 SlotSize::Bytes8,
1250 AllowHigherAlign::Yes,
1251 ForceRightAdjust::Yes,
1253 ),
1254 Arch::Sparc => {
1255 {
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 { .. });
1256
1257 let pass_mode = match layout.layout.backend_repr() {
1259 BackendRepr::Scalar(scalar) => match scalar.primitive() {
1260 Primitive::Float(Float::F128) => PassMode::Indirect,
1261 _ => PassMode::Direct,
1262 },
1263 _ => PassMode::Direct,
1264 };
1265
1266 emit_ptr_va_arg(
1267 bx,
1268 addr,
1269 layout,
1270 pass_mode,
1271 SlotSize::Bytes4,
1272 AllowHigherAlign::No,
1273 ForceRightAdjust::Yes,
1274 )
1275 }
1276 Arch::Mips | Arch::Mips32r6 | Arch::Mips64 | Arch::Mips64r6 => emit_ptr_va_arg(
1277 bx,
1278 addr,
1279 layout,
1280 PassMode::Direct,
1281 match &target.llvm_abiname {
1282 LlvmAbi::N32 | LlvmAbi::N64 => SlotSize::Bytes8,
1283 LlvmAbi::O32 => SlotSize::Bytes4,
1284 other => ::rustc_span::macros::bug_impl(None,
format_args!("unexpected LLVM ABI {0}", other), Location::caller())bug!("unexpected LLVM ABI {other}"),
1285 },
1286 AllowHigherAlign::Yes,
1287 match bx.tcx().sess.target.endian {
1291 Endian::Big => ForceRightAdjust::Yes,
1292 Endian::Little => ForceRightAdjust::No,
1293 },
1294 ),
1295
1296 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"),
1297 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"),
1298
1299 Arch::Avr | Arch::M68k | Arch::Msp430 => {
1300 {
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 { .. });
1301
1302 bx.va_arg(addr.immediate(), layout.llvm_type(bx.cx))
1304 }
1305
1306 Arch::Other(ref arch) => {
1307 {
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 { .. });
1308
1309 ::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}")
1312 }
1313 }
1314}