Rollup merge of #150831 - folkertdev:more-va-arg-2, r=workingjubilee

c-variadic: make `va_arg` match on `Arch` exhaustive

tracking issue: https://github.com/rust-lang/rust/issues/44930

Continuing from https://github.com/rust-lang/rust/pull/150094, the more annoying cases remain. These are mostly very niche targets without Clang `va_arg` implementations, and so it might just be easier to defer to LLVM instead of us getting the ABI subtly wrong. That does mean we cannot stabilize c-variadic on those targets I think.

Alternatively we could ask target maintainers to contribute an implementation. I'd honestly prefer they make that change to LVM though (likely by just using `CodeGen::emitVoidPtrVAArg`) that we can mirror.

r? @workingjubilee
This commit is contained in:
Jonathan Brouwer 2026-02-05 12:16:56 +01:00 committed by GitHub
commit 82b5849618
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -131,7 +131,7 @@ fn emit_ptr_va_arg<'ll, 'tcx>(
);
if indirect {
let tmp_ret = bx.load(llty, addr, addr_align);
bx.load(bx.cx.layout_of(target_ty).llvm_type(bx.cx), tmp_ret, align.abi)
bx.load(layout.llvm_type(bx.cx), tmp_ret, align.abi)
} else {
bx.load(llty, addr, addr_align)
}
@ -1007,6 +1007,8 @@ fn emit_xtensa_va_arg<'ll, 'tcx>(
/// Determine the va_arg implementation to use. The LLVM va_arg instruction
/// is lacking in some instances, so we should only use it as a fallback.
///
/// <https://llvm.org/docs/LangRef.html#va-arg-instruction>
pub(super) fn emit_va_arg<'ll, 'tcx>(
bx: &mut Builder<'_, 'll, 'tcx>,
addr: OperandRef<'tcx, &'ll Value>,
@ -1015,6 +1017,10 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
let layout = bx.cx.layout_of(target_ty);
let target_ty_size = layout.layout.size().bytes();
// Some ABIs have special behavior for zero-sized types. currently `VaArgSafe` is not
// implemented for any zero-sized types, so this assert should always hold.
assert!(!bx.layout_of(target_ty).is_zst());
let target = &bx.cx.tcx.sess.target;
match target.arch {
Arch::X86 => emit_ptr_va_arg(
@ -1026,17 +1032,24 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
if target.is_like_windows { AllowHigherAlign::No } else { AllowHigherAlign::Yes },
ForceRightAdjust::No,
),
Arch::AArch64 | Arch::Arm64EC if target.is_like_windows || target.is_like_darwin => {
emit_ptr_va_arg(
bx,
addr,
target_ty,
PassMode::Direct,
SlotSize::Bytes8,
if target.is_like_windows { AllowHigherAlign::No } else { AllowHigherAlign::Yes },
ForceRightAdjust::No,
)
}
Arch::Arm64EC => emit_ptr_va_arg(
bx,
addr,
target_ty,
PassMode::Direct,
SlotSize::Bytes8,
if target.is_like_windows { AllowHigherAlign::No } else { AllowHigherAlign::Yes },
ForceRightAdjust::No,
),
Arch::AArch64 if target.is_like_windows || target.is_like_darwin => emit_ptr_va_arg(
bx,
addr,
target_ty,
PassMode::Direct,
SlotSize::Bytes8,
if target.is_like_windows { AllowHigherAlign::No } else { AllowHigherAlign::Yes },
ForceRightAdjust::No,
),
Arch::AArch64 => emit_aapcs_va_arg(bx, addr, target_ty),
Arch::Arm => {
// Types wider than 16 bytes are not currently supported. Clang has special logic for
@ -1064,7 +1077,16 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
AllowHigherAlign::Yes,
ForceRightAdjust::Yes,
),
Arch::LoongArch32 => emit_ptr_va_arg(
Arch::RiscV32 if target.abi == Abi::Ilp32e => {
// FIXME: clang manually adjusts the alignment for this ABI. It notes:
//
// > To be compatible with GCC's behaviors, we force arguments with
// > 2×XLEN-bit alignment and size at most 2×XLEN bits like `long long`,
// > `unsigned long long` and `double` to have 4-byte alignment. This
// > behavior may be changed when RV32E/ILP32E is ratified.
bug!("c-variadic calls with ilp32e use a custom ABI and are not currently implemented");
}
Arch::RiscV32 | Arch::LoongArch32 => emit_ptr_va_arg(
bx,
addr,
target_ty,
@ -1073,7 +1095,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
AllowHigherAlign::Yes,
ForceRightAdjust::No,
),
Arch::LoongArch64 => emit_ptr_va_arg(
Arch::RiscV64 | Arch::LoongArch64 => emit_ptr_va_arg(
bx,
addr,
target_ty,
@ -1140,16 +1162,34 @@ pub(super) fn emit_va_arg<'ll, 'tcx>(
// This includes `target.is_like_darwin`, which on x86_64 targets is like sysv64.
Arch::X86_64 => emit_x86_64_sysv64_va_arg(bx, addr, target_ty),
Arch::Xtensa => emit_xtensa_va_arg(bx, addr, target_ty),
Arch::Hexagon => {
if target.env == Env::Musl {
emit_hexagon_va_arg_musl(bx, addr, target_ty)
} else {
emit_hexagon_va_arg_bare_metal(bx, addr, target_ty)
}
Arch::Hexagon => match target.env {
Env::Musl => emit_hexagon_va_arg_musl(bx, addr, target_ty),
_ => emit_hexagon_va_arg_bare_metal(bx, addr, target_ty),
},
Arch::Sparc64 => emit_ptr_va_arg(
bx,
addr,
target_ty,
if target_ty_size > 2 * 8 { PassMode::Indirect } else { PassMode::Direct },
SlotSize::Bytes8,
AllowHigherAlign::Yes,
ForceRightAdjust::No,
),
Arch::Bpf => bug!("bpf does not support c-variadic functions"),
Arch::SpirV => bug!("spirv does not support c-variadic functions"),
Arch::Mips | Arch::Mips32r6 | Arch::Mips64 | Arch::Mips64r6 => {
// FIXME: port MipsTargetLowering::lowerVAARG.
bx.va_arg(addr.immediate(), bx.cx.layout_of(target_ty).llvm_type(bx.cx))
}
Arch::Sparc | Arch::Avr | Arch::M68k | Arch::Msp430 => {
// Clang uses the LLVM implementation for these architectures.
bx.va_arg(addr.immediate(), bx.cx.layout_of(target_ty).llvm_type(bx.cx))
}
Arch::Other(_) => {
// For custom targets, use the LLVM va_arg instruction as a fallback.
bx.va_arg(addr.immediate(), bx.cx.layout_of(target_ty).llvm_type(bx.cx))
}
// For all other architecture/OS combinations fall back to using
// the LLVM va_arg instruction.
// https://llvm.org/docs/LangRef.html#va-arg-instruction
_ => bx.va_arg(addr.immediate(), bx.cx.layout_of(target_ty).llvm_type(bx.cx)),
}
}