@@ -237,6 +237,150 @@ fn emit_aapcs_va_arg<'ll, 'tcx>(
237237 val
238238}
239239
240+ fn emit_powerpc_va_arg<'ll, 'tcx>(
241+ bx: &mut Builder<'_, 'll, 'tcx>,
242+ list: OperandRef<'tcx, &'ll Value>,
243+ target_ty: Ty<'tcx>,
244+ ) -> &'ll Value {
245+ let dl = bx.cx.data_layout();
246+
247+ // struct __va_list_tag {
248+ // unsigned char gpr;
249+ // unsigned char fpr;
250+ // unsigned short reserved;
251+ // void *overflow_arg_area;
252+ // void *reg_save_area;
253+ // };
254+ let va_list_addr = list.immediate();
255+
256+ // Peel off any newtype wrappers.
257+ let layout = {
258+ let mut layout = bx.cx.layout_of(target_ty);
259+
260+ while let Some((_, inner)) = layout.non_1zst_field(bx.cx) {
261+ layout = inner;
262+ }
263+
264+ layout
265+ };
266+
267+ // Rust does not currently support any powerpc softfloat targets.
268+ let target = &bx.cx.tcx.sess.target;
269+ let is_soft_float_abi = target.abi == "softfloat";
270+ assert!(!is_soft_float_abi);
271+
272+ // All instances of VaArgSafe are passed directly.
273+ let is_indirect = false;
274+
275+ let (is_i64, is_int, is_f64) = match layout.layout.backend_repr() {
276+ BackendRepr::Scalar(scalar) => match scalar.primitive() {
277+ rustc_abi::Primitive::Int(integer, _) => (integer.size().bits() == 64, true, false),
278+ rustc_abi::Primitive::Float(float) => (false, false, float.size().bits() == 64),
279+ rustc_abi::Primitive::Pointer(_) => (false, true, false),
280+ },
281+ _ => unreachable!("all instances of VaArgSafe are represented as scalars"),
282+ };
283+
284+ let num_regs_addr = if is_int || is_soft_float_abi {
285+ va_list_addr // gpr
286+ } else {
287+ bx.inbounds_ptradd(va_list_addr, bx.const_usize(1)) // fpr
288+ };
289+
290+ let mut num_regs = bx.load(bx.type_i8(), num_regs_addr, dl.i8_align.abi);
291+
292+ // "Align" the register count when the type is passed as `i64`.
293+ if is_i64 || (is_f64 && is_soft_float_abi) {
294+ num_regs = bx.add(num_regs, bx.const_u8(1));
295+ num_regs = bx.and(num_regs, bx.const_u8(0b1111_1110));
296+ }
297+
298+ let max_regs = 8u8;
299+ let use_regs = bx.icmp(IntPredicate::IntULT, num_regs, bx.const_u8(max_regs));
300+
301+ let in_reg = bx.append_sibling_block("va_arg.in_reg");
302+ let in_mem = bx.append_sibling_block("va_arg.in_mem");
303+ let end = bx.append_sibling_block("va_arg.end");
304+
305+ bx.cond_br(use_regs, in_reg, in_mem);
306+
307+ let reg_addr = {
308+ bx.switch_to_block(in_reg);
309+
310+ let reg_safe_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(1 + 1 + 2 + 4));
311+ let mut reg_addr = bx.load(bx.type_ptr(), reg_safe_area_ptr, dl.pointer_align.abi);
312+
313+ // Floating-point registers start after the general-purpose registers.
314+ if !is_int && !is_soft_float_abi {
315+ reg_addr = bx.inbounds_ptradd(reg_addr, bx.cx.const_usize(32))
316+ }
317+
318+ // Get the address of the saved value by scaling the number of
319+ // registers we've used by the number of.
320+ let reg_size = if is_int || is_soft_float_abi { 4 } else { 8 };
321+ let reg_offset = bx.mul(num_regs, bx.cx().const_u8(reg_size));
322+ let reg_addr = bx.inbounds_ptradd(reg_addr, reg_offset);
323+
324+ // Increase the used-register count.
325+ let reg_incr = if is_i64 || (is_f64 && is_soft_float_abi) { 2 } else { 1 };
326+ let new_num_regs = bx.add(num_regs, bx.cx.const_u8(reg_incr));
327+ bx.store(new_num_regs, num_regs_addr, dl.i8_align.abi);
328+
329+ bx.br(end);
330+
331+ reg_addr
332+ };
333+
334+ let mem_addr = {
335+ bx.switch_to_block(in_mem);
336+
337+ bx.store(bx.const_u8(max_regs), num_regs_addr, dl.i8_align.abi);
338+
339+ // Everything in the overflow area is rounded up to a size of at least 4.
340+ let overflow_area_align = Align::from_bytes(4).unwrap();
341+
342+ let size = if !is_indirect {
343+ layout.layout.size.align_to(overflow_area_align)
344+ } else {
345+ dl.pointer_size
346+ };
347+
348+ let overflow_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(1 + 1 + 2));
349+ let mut overflow_area = bx.load(bx.type_ptr(), overflow_area_ptr, dl.pointer_align.abi);
350+
351+ // Round up address of argument to alignment
352+ if layout.layout.align.abi > overflow_area_align {
353+ overflow_area = round_pointer_up_to_alignment(
354+ bx,
355+ overflow_area,
356+ layout.layout.align.abi,
357+ bx.type_ptr(),
358+ );
359+ }
360+
361+ let mem_addr = overflow_area;
362+
363+ // Increase the overflow area.
364+ overflow_area = bx.inbounds_ptradd(overflow_area, bx.const_usize(size.bytes()));
365+ bx.store(overflow_area, overflow_area_ptr, dl.pointer_align.abi);
366+
367+ bx.br(end);
368+
369+ mem_addr
370+ };
371+
372+ // Return the appropriate result.
373+ bx.switch_to_block(end);
374+ let val_addr = bx.phi(bx.type_ptr(), &[reg_addr, mem_addr], &[in_reg, in_mem]);
375+ let val_type = layout.llvm_type(bx);
376+ let val_addr = if is_indirect {
377+ bx.load(bx.cx.type_ptr(), val_addr, dl.pointer_align.abi)
378+ } else {
379+ val_addr
380+ };
381+ bx.load(val_type, val_addr, layout.align.abi)
382+ }
383+
240384fn emit_s390x_va_arg<'ll, 'tcx>(
241385 bx: &mut Builder<'_, 'll, 'tcx>,
242386 list: OperandRef<'tcx, &'ll Value>,
@@ -740,17 +884,6 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
740884 let target = &bx.cx.tcx.sess.target;
741885
742886 match &*target.arch {
743- // Windows x86
744- "x86" if target.is_like_windows => emit_ptr_va_arg(
745- bx,
746- addr,
747- target_ty,
748- PassMode::Direct,
749- SlotSize::Bytes4,
750- AllowHigherAlign::No,
751- ForceRightAdjust::No,
752- ),
753- // Generic x86
754887 "x86" => emit_ptr_va_arg(
755888 bx,
756889 addr,
@@ -773,6 +906,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
773906 }
774907 "aarch64" => emit_aapcs_va_arg(bx, addr, target_ty),
775908 "s390x" => emit_s390x_va_arg(bx, addr, target_ty),
909+ "powerpc" => emit_powerpc_va_arg(bx, addr, target_ty),
776910 "powerpc64" | "powerpc64le" => emit_ptr_va_arg(
777911 bx,
778912 addr,
0 commit comments