implement va_arg for powerpc
This actually fixes a bug where before only 20 arguments could be passed. As far as I can tell, an arbitrary number of arguments is now supported
This commit is contained in:
@@ -237,6 +237,150 @@ fn emit_aapcs_va_arg<'ll, 'tcx>(
|
||||
val
|
||||
}
|
||||
|
||||
fn emit_powerpc_va_arg<'ll, 'tcx>(
|
||||
bx: &mut Builder<'_, 'll, 'tcx>,
|
||||
list: OperandRef<'tcx, &'ll Value>,
|
||||
target_ty: Ty<'tcx>,
|
||||
) -> &'ll Value {
|
||||
let dl = bx.cx.data_layout();
|
||||
|
||||
// struct __va_list_tag {
|
||||
// unsigned char gpr;
|
||||
// unsigned char fpr;
|
||||
// unsigned short reserved;
|
||||
// void *overflow_arg_area;
|
||||
// void *reg_save_area;
|
||||
// };
|
||||
let va_list_addr = list.immediate();
|
||||
|
||||
// Peel off any newtype wrappers.
|
||||
let layout = {
|
||||
let mut layout = bx.cx.layout_of(target_ty);
|
||||
|
||||
while let Some((_, inner)) = layout.non_1zst_field(bx.cx) {
|
||||
layout = inner;
|
||||
}
|
||||
|
||||
layout
|
||||
};
|
||||
|
||||
// Rust does not currently support any powerpc softfloat targets.
|
||||
let target = &bx.cx.tcx.sess.target;
|
||||
let is_soft_float_abi = target.abi == "softfloat";
|
||||
assert!(!is_soft_float_abi);
|
||||
|
||||
// All instances of VaArgSafe are passed directly.
|
||||
let is_indirect = false;
|
||||
|
||||
let (is_i64, is_int, is_f64) = match layout.layout.backend_repr() {
|
||||
BackendRepr::Scalar(scalar) => match scalar.primitive() {
|
||||
rustc_abi::Primitive::Int(integer, _) => (integer.size().bits() == 64, true, false),
|
||||
rustc_abi::Primitive::Float(float) => (false, false, float.size().bits() == 64),
|
||||
rustc_abi::Primitive::Pointer(_) => (false, true, false),
|
||||
},
|
||||
_ => unreachable!("all instances of VaArgSafe are represented as scalars"),
|
||||
};
|
||||
|
||||
let num_regs_addr = if is_int || is_soft_float_abi {
|
||||
va_list_addr // gpr
|
||||
} else {
|
||||
bx.inbounds_ptradd(va_list_addr, bx.const_usize(1)) // fpr
|
||||
};
|
||||
|
||||
let mut num_regs = bx.load(bx.type_i8(), num_regs_addr, dl.i8_align.abi);
|
||||
|
||||
// "Align" the register count when the type is passed as `i64`.
|
||||
if is_i64 || (is_f64 && is_soft_float_abi) {
|
||||
num_regs = bx.add(num_regs, bx.const_u8(1));
|
||||
num_regs = bx.and(num_regs, bx.const_u8(0b1111_1110));
|
||||
}
|
||||
|
||||
let max_regs = 8u8;
|
||||
let use_regs = bx.icmp(IntPredicate::IntULT, num_regs, bx.const_u8(max_regs));
|
||||
|
||||
let in_reg = bx.append_sibling_block("va_arg.in_reg");
|
||||
let in_mem = bx.append_sibling_block("va_arg.in_mem");
|
||||
let end = bx.append_sibling_block("va_arg.end");
|
||||
|
||||
bx.cond_br(use_regs, in_reg, in_mem);
|
||||
|
||||
let reg_addr = {
|
||||
bx.switch_to_block(in_reg);
|
||||
|
||||
let reg_safe_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(1 + 1 + 2 + 4));
|
||||
let mut reg_addr = bx.load(bx.type_ptr(), reg_safe_area_ptr, dl.pointer_align.abi);
|
||||
|
||||
// Floating-point registers start after the general-purpose registers.
|
||||
if !is_int && !is_soft_float_abi {
|
||||
reg_addr = bx.inbounds_ptradd(reg_addr, bx.cx.const_usize(32))
|
||||
}
|
||||
|
||||
// Get the address of the saved value by scaling the number of
|
||||
// registers we've used by the number of.
|
||||
let reg_size = if is_int || is_soft_float_abi { 4 } else { 8 };
|
||||
let reg_offset = bx.mul(num_regs, bx.cx().const_u8(reg_size));
|
||||
let reg_addr = bx.inbounds_ptradd(reg_addr, reg_offset);
|
||||
|
||||
// Increase the used-register count.
|
||||
let reg_incr = if is_i64 || (is_f64 && is_soft_float_abi) { 2 } else { 1 };
|
||||
let new_num_regs = bx.add(num_regs, bx.cx.const_u8(reg_incr));
|
||||
bx.store(new_num_regs, num_regs_addr, dl.i8_align.abi);
|
||||
|
||||
bx.br(end);
|
||||
|
||||
reg_addr
|
||||
};
|
||||
|
||||
let mem_addr = {
|
||||
bx.switch_to_block(in_mem);
|
||||
|
||||
bx.store(bx.const_u8(max_regs), num_regs_addr, dl.i8_align.abi);
|
||||
|
||||
// Everything in the overflow area is rounded up to a size of at least 4.
|
||||
let overflow_area_align = Align::from_bytes(4).unwrap();
|
||||
|
||||
let size = if !is_indirect {
|
||||
layout.layout.size.align_to(overflow_area_align)
|
||||
} else {
|
||||
dl.pointer_size
|
||||
};
|
||||
|
||||
let overflow_area_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(1 + 1 + 2));
|
||||
let mut overflow_area = bx.load(bx.type_ptr(), overflow_area_ptr, dl.pointer_align.abi);
|
||||
|
||||
// Round up address of argument to alignment
|
||||
if layout.layout.align.abi > overflow_area_align {
|
||||
overflow_area = round_pointer_up_to_alignment(
|
||||
bx,
|
||||
overflow_area,
|
||||
layout.layout.align.abi,
|
||||
bx.type_ptr(),
|
||||
);
|
||||
}
|
||||
|
||||
let mem_addr = overflow_area;
|
||||
|
||||
// Increase the overflow area.
|
||||
overflow_area = bx.inbounds_ptradd(overflow_area, bx.const_usize(size.bytes()));
|
||||
bx.store(overflow_area, overflow_area_ptr, dl.pointer_align.abi);
|
||||
|
||||
bx.br(end);
|
||||
|
||||
mem_addr
|
||||
};
|
||||
|
||||
// Return the appropriate result.
|
||||
bx.switch_to_block(end);
|
||||
let val_addr = bx.phi(bx.type_ptr(), &[reg_addr, mem_addr], &[in_reg, in_mem]);
|
||||
let val_type = layout.llvm_type(bx);
|
||||
let val_addr = if is_indirect {
|
||||
bx.load(bx.cx.type_ptr(), val_addr, dl.pointer_align.abi)
|
||||
} else {
|
||||
val_addr
|
||||
};
|
||||
bx.load(val_type, val_addr, layout.align.abi)
|
||||
}
|
||||
|
||||
fn emit_s390x_va_arg<'ll, 'tcx>(
|
||||
bx: &mut Builder<'_, 'll, 'tcx>,
|
||||
list: OperandRef<'tcx, &'ll Value>,
|
||||
@@ -740,17 +884,6 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
|
||||
let target = &bx.cx.tcx.sess.target;
|
||||
|
||||
match &*target.arch {
|
||||
// Windows x86
|
||||
"x86" if target.is_like_windows => emit_ptr_va_arg(
|
||||
bx,
|
||||
addr,
|
||||
target_ty,
|
||||
PassMode::Direct,
|
||||
SlotSize::Bytes4,
|
||||
AllowHigherAlign::No,
|
||||
ForceRightAdjust::No,
|
||||
),
|
||||
// Generic x86
|
||||
"x86" => emit_ptr_va_arg(
|
||||
bx,
|
||||
addr,
|
||||
@@ -773,6 +906,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
|
||||
}
|
||||
"aarch64" => emit_aapcs_va_arg(bx, addr, target_ty),
|
||||
"s390x" => emit_s390x_va_arg(bx, addr, target_ty),
|
||||
"powerpc" => emit_powerpc_va_arg(bx, addr, target_ty),
|
||||
"powerpc64" | "powerpc64le" => emit_ptr_va_arg(
|
||||
bx,
|
||||
addr,
|
||||
|
||||
Reference in New Issue
Block a user