diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 94bd4a6ef76ef..c50f1f2e26b4e 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -3,8 +3,8 @@ use std::ffi::c_uint; use std::{assert_matches, iter, ptr}; use rustc_abi::{ - AddressSpace, Align, BackendRepr, Float, HasDataLayout, Integer, NumScalableVectors, Primitive, - Size, WrappingRange, + AddressSpace, Align, BackendRepr, Float, HasDataLayout, NumScalableVectors, Primitive, Size, + WrappingRange, }; use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh}; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; @@ -299,11 +299,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { Primitive::Pointer(_) => { // Pointers are always OK. } - Primitive::Int(Integer::I128, _) => { - // FIXME: maybe we should support these? At least on 32-bit powerpc - // the logic in LLVM does not handle i128 correctly though. - bug!("the va_arg intrinsic does not support `i128`/`u128`") - } Primitive::Int(..) => { let int_width = self.cx().size_of(result.layout.ty).bits(); let target_c_int_width = self.cx().sess().target.options.c_int_width; diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index cfdd47b3a8c8c..3db18f45677e5 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -1,5 +1,4 @@ -use rustc_abi::{Align, BackendRepr, Endian, HasDataLayout, Primitive, Size}; -use rustc_codegen_ssa::MemFlags; +use rustc_abi::{Align, BackendRepr, Endian, Float, HasDataLayout, Integer, Primitive, Size}; use rustc_codegen_ssa::common::IntPredicate; use rustc_codegen_ssa::mir::operand::OperandRef; use rustc_codegen_ssa::traits::{ @@ -77,6 +76,35 @@ fn emit_direct_ptr_va_arg<'ll, 'tcx>( } } +/// Some backends apply special alignment rules to c-variadic arguments. +fn get_param_type_alignment<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, + layout: TyAndLayout<'tcx>, +) -> Align { + let BackendRepr::Scalar(scalar) = layout.backend_repr else { + bug!("unexpected backend repr {:?}", layout.backend_repr); + }; + + match bx.cx.tcx.sess.target.arch { + Arch::PowerPC64 => match scalar.primitive() { + Primitive::Int(integer, _) => match integer { + Integer::I8 | Integer::I16 => unreachable!(), + Integer::I32 | Integer::I64 => { /* fall through */ } + Integer::I128 => return Align::EIGHT, + }, + Primitive::Float(float) => match float { + Float::F16 | Float::F32 => unreachable!(), + Float::F64 => { /* fall through */ } + Float::F128 => return Align::from_bytes(16).unwrap(), + }, + Primitive::Pointer(_) => { /* fall through */ } + }, + _ => { /* fall through */ } + } + + layout.align.abi +} + enum PassMode { Direct, Indirect, @@ -136,23 +164,23 @@ fn emit_ptr_va_arg<'ll, 'tcx>( ( bx.cx.layout_of(Ty::new_imm_ptr(bx.cx.tcx, target_ty)).llvm_type(bx.cx), bx.cx.data_layout().pointer_size(), - bx.cx.data_layout().pointer_align(), + bx.cx.data_layout().pointer_align().abi, ) } else { - (layout.llvm_type(bx.cx), layout.size, layout.align) + (layout.llvm_type(bx.cx), layout.size, get_param_type_alignment(bx, layout)) }; let (addr, addr_align) = emit_direct_ptr_va_arg( bx, list, size, - align.abi, + align, slot_size, allow_higher_align, force_right_adjust, ); if indirect { let tmp_ret = bx.load(llty, addr, addr_align); - bx.load(layout.llvm_type(bx.cx), tmp_ret, align.abi) + bx.load(layout.llvm_type(bx.cx), tmp_ret, align) } else { bx.load(llty, addr, addr_align) } @@ -585,8 +613,10 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( // registers. In the case: l->gp_offset > 48 - num_gp * 8 or // l->fp_offset > 176 - num_fp * 16 go to step 7. + // We support x86_64-unknown-linux-gnux32 which uses 4-byte pointers. let unsigned_int_offset = 4; - let ptr_offset = 8; + let ptr_offset = bx.tcx().data_layout.pointer_size().bytes(); + let gp_offset_ptr = va_list_addr; let fp_offset_ptr = bx.inbounds_ptradd(va_list_addr, bx.cx.const_usize(unsigned_int_offset)); @@ -660,7 +690,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( let reg_hi_addr = bx.inbounds_ptradd(reg_lo_addr, bx.const_i32(16)); let align = layout.layout.align().abi; - let tmp = bx.alloca(layout.layout.size(), align); + let tmp = bx.alloca(layout.size, layout.align.abi); let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo); let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi); @@ -683,7 +713,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( Primitive::Int(_, _) | Primitive::Pointer(_) => (gp_addr, fp_addr), }; - let tmp = bx.alloca(layout.layout.size(), layout.layout.align().abi); + let tmp = bx.alloca(layout.size, layout.align.abi); let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo); let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi); @@ -751,16 +781,12 @@ fn copy_to_temporary_if_more_aligned<'ll, 'tcx>( src_align: Align, ) -> &'ll Value { if layout.layout.align.abi > src_align { - let tmp = bx.alloca(layout.layout.size(), layout.layout.align().abi); - bx.memcpy( - tmp, - layout.layout.align.abi, - reg_addr, - src_align, - bx.const_u32(layout.layout.size().bytes() as u32), - MemFlags::empty(), - None, - ); + assert!(layout.ty.is_integral()); + + // A memcpy below optimizes poorly for 128-bit integers. + let tmp = bx.alloca(layout.size, layout.align.abi); + let val = bx.load(layout.llvm_type(bx), reg_addr, src_align); + bx.store(val, tmp, layout.align.abi); tmp } else { reg_addr @@ -782,9 +808,11 @@ fn x86_64_sysv64_va_arg_from_memory<'ll, 'tcx>( // byte boundary if alignment needed by type exceeds 8 byte boundary. // It isn't stated explicitly in the standard, but in practice we use // alignment greater than 16 where necessary. - if layout.layout.align.bytes() > 8 { - unreachable!("all instances of VaArgSafe have an alignment <= 8"); - } + let overflow_arg_area_v = if layout.layout.align.bytes() > 8 { + round_pointer_up_to_alignment(bx, overflow_arg_area_v, layout.layout.align.abi) + } else { + overflow_arg_area_v + }; // AMD64-ABI 3.5.7p5: Step 8. Fetch type from l->overflow_arg_area. let mem_addr = overflow_arg_area_v; @@ -1069,7 +1097,8 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( 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 - // such types, but `VaArgSafe` is not implemented for any type that is this large. + // such types, but `VaArgSafe` is not implemented for any type that is this large on + // arm (i.e. 32-bit) targets. assert!(bx.cx.size_of(target_ty).bytes() <= 16); emit_ptr_va_arg( @@ -1091,6 +1120,7 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( PassMode::Direct, SlotSize::Bytes8, AllowHigherAlign::Yes, + // ForceRightAdjust only takes effect on big-endian architectures. ForceRightAdjust::Yes, ), Arch::RiscV32 if target.llvm_abiname == LlvmAbi::Ilp32e => { diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 0a35dc32ef19c..1455725058aa1 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -286,6 +286,15 @@ mod sealed { impl Sealed for f32 {} impl Sealed for f64 {} + // The unstable annotation here is not needed, but makes sure that the feature gate is defined + // on all targets, even though the actual instances of VaArgSafe for i128/u128 are not. + #[unstable_feature_bound(c_variadic_int128)] + #[unstable(feature = "c_variadic_int128", issue = "155752")] + impl Sealed for i128 {} + #[unstable_feature_bound(c_variadic_int128)] + #[unstable(feature = "c_variadic_int128", issue = "155752")] + impl Sealed for u128 {} + impl Sealed for *mut T {} impl Sealed for *const T {} } @@ -310,6 +319,9 @@ mod sealed { /// and [`c_float`] is promoted to [`c_double`]. Implementing this trait for types that are /// subject to this promotion rule is invalid. /// +/// This trait is only implemented for 128-bit integers when the platform defines the `__int128` +/// type. +/// /// [`c_int`]: core::ffi::c_int /// [`c_long`]: core::ffi::c_long /// [`c_longlong`]: core::ffi::c_longlong @@ -321,8 +333,8 @@ mod sealed { /// [`c_float`]: core::ffi::c_float /// [`c_double`]: core::ffi::c_double // We may unseal this trait in the future, but currently our `va_arg` implementations don't support -// types with an alignment larger than 8, or with a non-scalar layout. Inline assembly can be used -// to accept unsupported types in the meantime. +// types with a non-scalar layout. Inline assembly can be used to accept unsupported types in the +// meantime. #[lang = "va_arg_safe"] pub unsafe trait VaArgSafe: sealed::Sealed {} @@ -363,6 +375,51 @@ unsafe impl VaArgSafe for u32 {} unsafe impl VaArgSafe for u64 {} unsafe impl VaArgSafe for usize {} +// Implement `VaArgSafe` for 128-bit integers on targets where clang provides `__int128`. +// +// GCC does not implement `__int128` for any 16-bit/32-bit target: +// +// https://gcc.gnu.org/onlinedocs/gcc-15.2.0/gcc/_005f_005fint128.html +// +// > There is no support in GCC for expressing an integer constant of type __int128 for targets +// > with long long integer less than 128 bits wide. +// +// Per https://learn.microsoft.com/en-us/cpp/cpp/data-type-ranges?view=msvc-170, MSVC does not +// define `__int128`. +// +// Clang is slightly more permissive: it defines `__int128` on wasm32 (a 32-bit target) and also +// does provide `__int128` on 64-bit `*-pc-windows-msvc`, and we follow suit. +cfg_select! { + any( + target_arch = "aarch64", + target_arch = "amdgpu", + target_arch = "arm64ec", + target_arch = "bpf", + target_arch = "loongarch64", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "nvptx64", + target_arch = "powerpc64", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "sparc64", + target_arch = "wasm32", + target_arch = "wasm64", + target_arch = "x86_64", + ) => { + #[cfg(not(any(target_arch = "wasm32", target_abi = "x32", target_pointer_width = "64")))] + compile_error!("unexpected target architecture for 128-bit c-variadic"); + + #[unstable_feature_bound(c_variadic_int128)] + #[unstable(feature = "c_variadic_int128", issue = "155752")] + unsafe impl VaArgSafe for i128 {} + #[unstable_feature_bound(c_variadic_int128)] + #[unstable(feature = "c_variadic_int128", issue = "155752")] + unsafe impl VaArgSafe for u128 {} + } + _ => {} +} + unsafe impl VaArgSafe for f64 {} unsafe impl VaArgSafe for *mut T {} diff --git a/tests/assembly-llvm/c-variadic/aarch64.rs b/tests/assembly-llvm/c-variadic/aarch64.rs new file mode 100644 index 0000000000000..eb242a1a41e21 --- /dev/null +++ b/tests/assembly-llvm/c-variadic/aarch64.rs @@ -0,0 +1,294 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: AARCH64_LINUX AARCH64_DARWIN AARCH64_BE ARM64EC_MSVC +//@ [AARCH64_LINUX] compile-flags: -Copt-level=3 --target aarch64-unknown-linux-gnu +//@ [AARCH64_LINUX] needs-llvm-components: aarch64 +//@ [AARCH64_BE] compile-flags: -Copt-level=3 --target aarch64_be-unknown-linux-gnu +//@ [AARCH64_BE] needs-llvm-components: aarch64 +//@ [AARCH64_DARWIN] compile-flags: -Copt-level=3 --target aarch64-apple-darwin +//@ [AARCH64_DARWIN] needs-llvm-components: aarch64 +//@ [ARM64EC_MSVC] compile-flags: -Copt-level=3 --target arm64ec-pc-windows-msvc +//@ [ARM64EC_MSVC] needs-llvm-components: aarch64 +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +// Check that the assembly that rustc generates matches what clang emits. + +// For aarch64-unknown-linux-gnu LLVM canonicalizes a comparison, leading to slightly different +// assembly. +// +// For aarch64-apple-darwin LLVM is able to optimize our output better, because we effectively +// desugar va_arg early, hence we don't actually match Clang there. + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +unsafe impl VaArgSafe for i128 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // AARCH64_LINUX-LABEL: read_f64: + // AARCH64_LINUX: ldrsw x8, [x0, #28] + // AARCH64_LINUX-NEXT: tbz w8, #31, .LBB0_2 + // AARCH64_LINUX-NEXT: add w9, w8, #16 + // AARCH64_LINUX-NEXT: cmn w8, #16 + // AARCH64_LINUX-NEXT: str w9, [x0, #28] + // AARCH64_LINUX-NEXT: b.ls .LBB0_3 + // AARCH64_LINUX-NEXT: .LBB0_2: + // AARCH64_LINUX-NEXT: ldr x8, [x0] + // AARCH64_LINUX-NEXT: ldr d0, [x8] + // AARCH64_LINUX-NEXT: add x9, x8, #8 + // AARCH64_LINUX-NEXT: str x9, [x0] + // AARCH64_LINUX-NEXT: ret + // AARCH64_LINUX-NEXT: .LBB0_3 + // AARCH64_LINUX-NEXT: ldr x9, [x0, #16] + // AARCH64_LINUX-NEXT: add x8, x9, x8 + // AARCH64_LINUX-NEXT: ldr d0, [x8] + // AARCH64_LINUX-NEXT: ret + + // AARCH64_BE-LABEL: read_f64: + // AARCH64_BE: ldrsw x8, [x0, #28] + // AARCH64_BE-NEXT: tbz w8, #31, .LBB0_2 + // AARCH64_BE-NEXT: add w9, w8, #16 + // AARCH64_BE-NEXT: cmn w8, #16 + // AARCH64_BE-NEXT: str w9, [x0, #28] + // AARCH64_BE-NEXT: b.ls .LBB0_3 + // AARCH64_BE-NEXT: .LBB0_2: + // AARCH64_BE-NEXT: ldr x8, [x0] + // AARCH64_BE-NEXT: ldr d0, [x8] + // AARCH64_BE-NEXT: add x9, x8, #8 + // AARCH64_BE-NEXT: str x9, [x0] + // AARCH64_BE-NEXT: ret + // AARCH64_BE-NEXT: .LBB0_3: + // AARCH64_BE-NEXT: ldr x9, [x0, #16] + // AARCH64_BE-NEXT: add x8, x9, x8 + // AARCH64_BE-NEXT: ldr d0, [x8, #8]! + // AARCH64_BE-NEXT: ret + + // ARM64EC_MSVC-LABEL: read_f64 = "#read_f64" + // ARM64EC_MSVC: ldr x8, [x0] + // ARM64EC_MSVC-NEXT: ldr d0, [x8], #8 + // ARM64EC_MSVC-NEXT: str x8, [x0] + // ARM64EC_MSVC-NEXT: ret + + // AARCH64_DARWIN-LABEL: _read_f64: + // AARCH64_DARWIN: ldr x8, [x0] + // AARCH64_DARWIN-NEXT: ldr d0, [x8], #8 + // AARCH64_DARWIN-NEXT: str x8, [x0] + // AARCH64_DARWIN-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // AARCH64_LINUX-LABEL: read_i32: + // AARCH64_LINUX: ldrsw x8, [x0, #24] + // AARCH64_LINUX-NEXT: tbz w8, #31, .LBB1_2 + // AARCH64_LINUX-NEXT: add w9, w8, #8 + // AARCH64_LINUX-NEXT: cmn w8, #8 + // AARCH64_LINUX-NEXT: str w9, [x0, #24] + // AARCH64_LINUX-NEXT: b.ls .LBB1_3 + // AARCH64_LINUX-NEXT: .LBB1_2: + // AARCH64_LINUX-NEXT: ldr x8, [x0] + // AARCH64_LINUX-NEXT: add x9, x8, #8 + // AARCH64_LINUX-NEXT: str x9, [x0] + // AARCH64_LINUX-NEXT: ldr w0, [x8] + // AARCH64_LINUX-NEXT: ret + // AARCH64_LINUX-NEXT: .LBB1_3 + // AARCH64_LINUX-NEXT: ldr x9, [x0, #8] + // AARCH64_LINUX-NEXT: add x8, x9, x8 + // AARCH64_LINUX-NEXT: ldr w0, [x8] + // AARCH64_LINUX-NEXT: ret + + // AARCH64_BE-LABEL: read_i32: + // AARCH64_BE: ldrsw x8, [x0, #24] + // AARCH64_BE-NEXT: tbz w8, #31, .LBB1_2 + // AARCH64_BE-NEXT: add w9, w8, #8 + // AARCH64_BE-NEXT: cmn w8, #8 + // AARCH64_BE-NEXT: str w9, [x0, #24] + // AARCH64_BE-NEXT: b.ls .LBB1_3 + // AARCH64_BE-NEXT: .LBB1_2: + // AARCH64_BE-NEXT: ldr x8, [x0] + // AARCH64_BE-NEXT: add x9, x8, #8 + // AARCH64_BE-NEXT: str x9, [x0] + // AARCH64_BE-NEXT: ldr w0, [x8] + // AARCH64_BE-NEXT: ret + // AARCH64_BE-NEXT: .LBB1_3: + // AARCH64_BE-NEXT: ldr x9, [x0, #8] + // AARCH64_BE-NEXT: add x8, x9, x8 + // AARCH64_BE-NEXT: ldr w0, [x8, #4]! + // AARCH64_BE-NEXT: ret + + // ARM64EC_MSVC-LABEL: read_i32 = "#read_i32" + // ARM64EC_MSVC: ldr x9, [x0] + // ARM64EC_MSVC-NEXT: mov x8, x0 + // ARM64EC_MSVC-NEXT: ldr w0, [x9], #8 + // ARM64EC_MSVC-NEXT: str x9, [x8] + // ARM64EC_MSVC-NEXT: ret + + // AARCH64_DARWIN-LABEL: _read_i32: + // AARCH64_DARWIN: ldr x9, [x0] + // AARCH64_DARWIN-NEXT: ldr w8, [x9], #8 + // AARCH64_DARWIN-NEXT: str x9, [x0] + // AARCH64_DARWIN-NEXT: mov x0, x8 + // AARCH64_DARWIN-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // AARCH64_LINUX-LABEL: read_i64: + // AARCH64_LINUX: ldrsw x8, [x0, #24] + // AARCH64_LINUX-NEXT: tbz w8, #31, .LBB2_2 + // AARCH64_LINUX-NEXT: add w9, w8, #8 + // AARCH64_LINUX-NEXT: cmn w8, #8 + // AARCH64_LINUX-NEXT: str w9, [x0, #24] + // AARCH64_LINUX-NEXT: b.ls .LBB2_3 + // AARCH64_LINUX-NEXT: .LBB2_2: + // AARCH64_LINUX-NEXT: ldr x8, [x0] + // AARCH64_LINUX-NEXT: add x9, x8, #8 + // AARCH64_LINUX-NEXT: str x9, [x0] + // AARCH64_LINUX-NEXT: ldr x0, [x8] + // AARCH64_LINUX-NEXT: ret + // AARCH64_LINUX-NEXT: .LBB2_3 + // AARCH64_LINUX-NEXT: ldr x9, [x0, #8] + // AARCH64_LINUX-NEXT: add x8, x9, x8 + // AARCH64_LINUX-NEXT: ldr x0, [x8] + // AARCH64_LINUX-NEXT: ret + + // AARCH64_BE-LABEL: read_i64: + // AARCH64_BE: ldrsw x8, [x0, #24] + // AARCH64_BE-NEXT: tbz w8, #31, .LBB2_2 + // AARCH64_BE-NEXT: add w9, w8, #8 + // AARCH64_BE-NEXT: cmn w8, #8 + // AARCH64_BE-NEXT: str w9, [x0, #24] + // AARCH64_BE-NEXT: b.ls .LBB2_3 + // AARCH64_BE-NEXT: .LBB2_2: + // AARCH64_BE-NEXT: ldr x8, [x0] + // AARCH64_BE-NEXT: add x9, x8, #8 + // AARCH64_BE-NEXT: str x9, [x0] + // AARCH64_BE-NEXT: ldr x0, [x8] + // AARCH64_BE-NEXT: ret + // AARCH64_BE-NEXT: .LBB2_3: + // AARCH64_BE-NEXT: ldr x9, [x0, #8] + // AARCH64_BE-NEXT: add x8, x9, x8 + // AARCH64_BE-NEXT: ldr x0, [x8] + // AARCH64_BE-NEXT: ret + + // ARM64EC_MSVC-LABEL: read_ptr = "#read_ptr" + // ARM64EC_MSVC-LABEL: read_i64 = "#read_i64" + // ARM64EC_MSVC: ldr x9, [x0] + // ARM64EC_MSVC-NEXT: mov x8, x0 + // ARM64EC_MSVC-NEXT: ldr x0, [x9], #8 + // ARM64EC_MSVC-NEXT: str x9, [x8] + // ARM64EC_MSVC-NEXT: ret + + // AARCH64_DARWIN-LABEL: _read_i64: + // AARCH64_DARWIN: ldr x9, [x0] + // AARCH64_DARWIN-NEXT: ldr x8, [x9], #8 + // AARCH64_DARWIN-NEXT: str x9, [x0] + // AARCH64_DARWIN-NEXT: mov x0, x8 + // AARCH64_DARWIN-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { + // AARCH64_LINUX-LABEL: read_i128: + // AARCH64_LINUX: ldrsw x8, [x0, #24] + // AARCH64_LINUX-NEXT: tbz w8, #31, .LBB3_2 + // AARCH64_LINUX-NEXT: add x8, x8, #15 + // AARCH64_LINUX-NEXT: and x8, x8, #0xfffffffffffffff0 + // AARCH64_LINUX-NEXT: add w9, w8, #16 + // AARCH64_LINUX-NEXT: cmp w9, #0 + // AARCH64_LINUX-NEXT: str w9, [x0, #24] + // AARCH64_LINUX-NEXT: b.le .LBB3_3 + // AARCH64_LINUX-NEXT: .LBB3_2: + // AARCH64_LINUX-NEXT: ldr x8, [x0] + // AARCH64_LINUX-NEXT: add x8, x8, #15 + // AARCH64_LINUX-NEXT: and x8, x8, #0xfffffffffffffff0 + // AARCH64_LINUX-NEXT: add x9, x8, #16 + // AARCH64_LINUX-NEXT: str x9, [x0] + // AARCH64_LINUX-NEXT: ldp x0, x1, [x8] + // AARCH64_LINUX-NEXT: ret + // AARCH64_LINUX-NEXT: .LBB3_3 + // AARCH64_LINUX-NEXT: ldr x9, [x0, #8] + // AARCH64_LINUX-NEXT: add x8, x9, x8 + // AARCH64_LINUX-NEXT: ldp x0, x1, [x8] + // AARCH64_LINUX-NEXT: ret + + // AARCH64_BE-LABEL: read_i128: + // AARCH64_BE: ldrsw x8, [x0, #24] + // AARCH64_BE-NEXT: tbz w8, #31, .LBB3_2 + // AARCH64_BE-NEXT: add x8, x8, #15 + // AARCH64_BE-NEXT: and x8, x8, #0xfffffffffffffff0 + // AARCH64_BE-NEXT: add w9, w8, #16 + // AARCH64_BE-NEXT: cmp w9, #0 + // AARCH64_BE-NEXT: str w9, [x0, #24] + // AARCH64_BE-NEXT: b.le .LBB3_3 + // AARCH64_BE-NEXT: .LBB3_2: + // AARCH64_BE-NEXT: ldr x8, [x0] + // AARCH64_BE-NEXT: add x8, x8, #15 + // AARCH64_BE-NEXT: and x8, x8, #0xfffffffffffffff0 + // AARCH64_BE-NEXT: add x9, x8, #16 + // AARCH64_BE-NEXT: str x9, [x0] + // AARCH64_BE-NEXT: ldp x0, x1, [x8] + // AARCH64_BE-NEXT: ret + // AARCH64_BE-NEXT: .LBB3_3: + // AARCH64_BE-NEXT: ldr x9, [x0, #8] + // AARCH64_BE-NEXT: add x8, x9, x8 + // AARCH64_BE-NEXT: ldp x0, x1, [x8] + // AARCH64_BE-NEXT: ret + + // ARM64EC_MSVC-LABEL: read_i128 = "#read_i128" + // ARM64EC_MSVC: ldr x9, [x0] + // ARM64EC_MSVC-NEXT: mov x8, x0 + // ARM64EC_MSVC-NEXT: ldp x0, x1, [x9], #16 + // ARM64EC_MSVC-NEXT: str x9, [x8] + // ARM64EC_MSVC-NEXT: ret + + // AARCH64_DARWIN-LABEL: _read_i128: + // AARCH64_DARWIN: ldr x8, [x0] + // AARCH64_DARWIN-NEXT: add x8, x8, #15 + // AARCH64_DARWIN-NEXT: and x9, x8, #0xfffffffffffffff0 + // AARCH64_DARWIN-NEXT: ldr x1, [x9, #8] + // AARCH64_DARWIN-NEXT: ldr x8, [x9], #16 + // AARCH64_DARWIN-NEXT: str x9, [x0] + // AARCH64_DARWIN-NEXT: mov x0, x8 + // AARCH64_DARWIN-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // AARCH64_LINUX-CHECK: read_ptr = read_i64 + // AARCH64_BE-CHECK: read_ptr = read_i64 + // ARM64EC_MSVC: "#read_ptr" = "#read_i64" + // AARCH64_DARWIN-CHECK: _read_ptr = _read_i64 + va_arg(ap) +} diff --git a/tests/assembly-llvm/c-variadic/avr.rs b/tests/assembly-llvm/c-variadic/avr.rs new file mode 100644 index 0000000000000..afc00436a3e2c --- /dev/null +++ b/tests/assembly-llvm/c-variadic/avr.rs @@ -0,0 +1,218 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: AVR +//@ [AVR] compile-flags: --target=avr-none -Ctarget-cpu=atmega328p +//@ [AVR] needs-llvm-components: avr +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i16 {} +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +unsafe impl VaArgSafe for f32 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f32(ap: &mut VaList<'_>) -> f32 { + // CHECK-LABEL: read_f32 + // + // AVR: movw r30, r24 + // AVR-NEXT: ld r24, Z + // AVR-NEXT: ldd r25, Z+1 + // AVR-NEXT: movw r26, r24 + // AVR-NEXT: adiw r26, 2 + // AVR-NEXT: std Z+1, r27 + // AVR-NEXT: st Z, r26 + // AVR-NEXT: movw r20, r30 + // AVR-NEXT: movw r18, r24 + // AVR-NEXT: movw r30, r18 + // AVR-NEXT: ld r22, Z + // AVR-NEXT: ldd r23, Z+1 + // AVR-NEXT: adiw r24, 4 + // AVR-NEXT: movw r30, r20 + // AVR-NEXT: std Z+1, r25 + // AVR-NEXT: st Z, r24 + // AVR-NEXT: movw r30, r18 + // AVR-NEXT: ldd r24, Z+2 + // AVR-NEXT: ldd r25, Z+3 + // AVR-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // CHECK-LABEL: read_f64 + // + // AVR: push r14 + // AVR-NEXT: push r15 + // AVR-NEXT: push r16 + // AVR-NEXT: push r17 + // AVR-NEXT: movw r30, r24 + // AVR-NEXT: ld r24, Z + // AVR-NEXT: ldd r25, Z+1 + // AVR-NEXT: movw r26, r24 + // AVR-NEXT: adiw r26, 2 + // AVR-NEXT: std Z+1, r27 + // AVR-NEXT: st Z, r26 + // AVR-NEXT: movw r14, r30 + // AVR-NEXT: movw r16, r24 + // AVR-NEXT: movw r30, r16 + // AVR-NEXT: ld r18, Z + // AVR-NEXT: ldd r19, Z+1 + // AVR-NEXT: movw r26, r24 + // AVR-NEXT: adiw r26, 4 + // AVR-NEXT: movw r30, r14 + // AVR-NEXT: std Z+1, r27 + // AVR-NEXT: st Z, r26 + // AVR-NEXT: movw r30, r16 + // AVR-NEXT: ldd r20, Z+2 + // AVR-NEXT: ldd r21, Z+3 + // AVR-NEXT: movw r26, r24 + // AVR-NEXT: adiw r26, 6 + // AVR-NEXT: movw r30, r14 + // AVR-NEXT: std Z+1, r27 + // AVR-NEXT: st Z, r26 + // AVR-NEXT: movw r30, r16 + // AVR-NEXT: ldd r22, Z+4 + // AVR-NEXT: ldd r23, Z+5 + // AVR-NEXT: adiw r24, 8 + // AVR-NEXT: movw r30, r14 + // AVR-NEXT: std Z+1, r25 + // AVR-NEXT: st Z, r24 + // AVR-NEXT: movw r30, r16 + // AVR-NEXT: ldd r24, Z+6 + // AVR-NEXT: ldd r25, Z+7 + // AVR-NEXT: pop r17 + // AVR-NEXT: pop r16 + // AVR-NEXT: pop r15 + // AVR-NEXT: pop r14 + // AVR-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i16(ap: &mut VaList<'_>) -> i16 { + // CHECK-LABEL: read_i16 + // + // AVR: movw r30, r24 + // AVR-NEXT: ld r24, Z + // AVR-NEXT: ldd r25, Z+1 + // AVR-NEXT: movw r26, r24 + // AVR-NEXT: adiw r26, 2 + // AVR-NEXT: std Z+1, r27 + // AVR-NEXT: st Z, r26 + // AVR-NEXT: movw r30, r24 + // AVR-NEXT: ld r24, Z + // AVR-NEXT: ldd r25, Z+1 + // AVR-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // CHECK-LABEL: read_i32 + // + // AVR: movw r30, r24 + // AVR-NEXT: ld r24, Z + // AVR-NEXT: ldd r25, Z+1 + // AVR-NEXT: movw r26, r24 + // AVR-NEXT: adiw r26, 2 + // AVR-NEXT: std Z+1, r27 + // AVR-NEXT: st Z, r26 + // AVR-NEXT: movw r20, r30 + // AVR-NEXT: movw r18, r24 + // AVR-NEXT: movw r30, r18 + // AVR-NEXT: ld r22, Z + // AVR-NEXT: ldd r23, Z+1 + // AVR-NEXT: adiw r24, 4 + // AVR-NEXT: movw r30, r20 + // AVR-NEXT: std Z+1, r25 + // AVR-NEXT: st Z, r24 + // AVR-NEXT: movw r30, r18 + // AVR-NEXT: ldd r24, Z+2 + // AVR-NEXT: ldd r25, Z+3 + // AVR-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // CHECK-LABEL: read_i64 + // + // AVR: push r14 + // AVR-NEXT: push r15 + // AVR-NEXT: push r16 + // AVR-NEXT: push r17 + // AVR-NEXT: movw r30, r24 + // AVR-NEXT: ld r24, Z + // AVR-NEXT: ldd r25, Z+1 + // AVR-NEXT: movw r26, r24 + // AVR-NEXT: adiw r26, 2 + // AVR-NEXT: std Z+1, r27 + // AVR-NEXT: st Z, r26 + // AVR-NEXT: movw r14, r30 + // AVR-NEXT: movw r16, r24 + // AVR-NEXT: movw r30, r16 + // AVR-NEXT: ld r18, Z + // AVR-NEXT: ldd r19, Z+1 + // AVR-NEXT: movw r26, r24 + // AVR-NEXT: adiw r26, 4 + // AVR-NEXT: movw r30, r14 + // AVR-NEXT: std Z+1, r27 + // AVR-NEXT: st Z, r26 + // AVR-NEXT: movw r30, r16 + // AVR-NEXT: ldd r20, Z+2 + // AVR-NEXT: ldd r21, Z+3 + // AVR-NEXT: movw r26, r24 + // AVR-NEXT: adiw r26, 6 + // AVR-NEXT: movw r30, r14 + // AVR-NEXT: std Z+1, r27 + // AVR-NEXT: st Z, r26 + // AVR-NEXT: movw r30, r16 + // AVR-NEXT: ldd r22, Z+4 + // AVR-NEXT: ldd r23, Z+5 + // AVR-NEXT: adiw r24, 8 + // AVR-NEXT: movw r30, r14 + // AVR-NEXT: std Z+1, r25 + // AVR-NEXT: st Z, r24 + // AVR-NEXT: movw r30, r16 + // AVR-NEXT: ldd r24, Z+6 + // AVR-NEXT: ldd r25, Z+7 + // AVR-NEXT: pop r17 + // AVR-NEXT: pop r16 + // AVR-NEXT: pop r15 + // AVR-NEXT: pop r14 + // AVR-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // AVR: read_ptr = pm(read_i16) + va_arg(ap) +} diff --git a/tests/assembly-llvm/c-variadic/gpu.rs b/tests/assembly-llvm/c-variadic/gpu.rs new file mode 100644 index 0000000000000..02798354bbf3d --- /dev/null +++ b/tests/assembly-llvm/c-variadic/gpu.rs @@ -0,0 +1,178 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: AMDGPU NVPTX +//@ [AMDGPU] compile-flags: --crate-type=rlib --target=amdgcn-amd-amdhsa -Ctarget-cpu=gfx900 +//@ [AMDGPU] needs-llvm-components: amdgpu +//@ [NVPTX] compile-flags: --crate-type=rlib --target=nvptx64-nvidia-cuda +//@ [NVPTX] needs-llvm-components: nvptx +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +unsafe impl VaArgSafe for i128 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // CHECK-LABEL: read_f64 + // + // AMDGPU: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dwordx2 v[4:5], v[0:1] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dwordx2 v[2:3], v[4:5] + // AMDGPU-NEXT: v_add_co_u32_e32 v4, vcc, 8, v4 + // AMDGPU-NEXT: v_addc_co_u32_e32 v5, vcc, 0, v5, vcc + // AMDGPU-NEXT: flat_store_dwordx2 v[0:1], v[4:5] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: v_mov_b32_e32 v0, v2 + // AMDGPU-NEXT: v_mov_b32_e32 v1, v3 + // AMDGPU-NEXT: s_setpc_b64 s[30:31] + // + // NVPTX: ld.param.b64 %rd1, [read_f64_param_0]; + // NVPTX-NEXT: ld.b64 %rd2, [%rd1]; + // NVPTX-NEXT: add.s64 %rd3, %rd2, 7; + // NVPTX-NEXT: and.b64 %rd4, %rd3, -8; + // NVPTX-NEXT: add.s64 %rd5, %rd4, 8; + // NVPTX-NEXT: st.b64 [%rd1], %rd5; + // NVPTX-NEXT: ld.b64 %rd6, [%rd4]; + // NVPTX-NEXT: st.param.b64 [func_retval0], %rd6; + // NVPTX-NEXT: ret; + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // CHECK-LABEL: read_i32 + // + // AMDGPU: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dwordx2 v[3:4], v[0:1] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dword v2, v[3:4] + // AMDGPU-NEXT: v_add_co_u32_e32 v3, vcc, 4, v3 + // AMDGPU-NEXT: v_addc_co_u32_e32 v4, vcc, 0, v4, vcc + // AMDGPU-NEXT: flat_store_dwordx2 v[0:1], v[3:4] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: v_mov_b32_e32 v0, v2 + // AMDGPU-NEXT: s_setpc_b64 s[30:31] + // + // NVPTX: ld.param.b64 %rd1, [read_i32_param_0]; + // NVPTX-NEXT: ld.b64 %rd2, [%rd1]; + // NVPTX-NEXT: add.s64 %rd3, %rd2, 3; + // NVPTX-NEXT: and.b64 %rd4, %rd3, -4; + // NVPTX-NEXT: add.s64 %rd5, %rd4, 4; + // NVPTX-NEXT: st.b64 [%rd1], %rd5; + // NVPTX-NEXT: ld.b32 %r1, [%rd4]; + // NVPTX-NEXT: st.param.b32 [func_retval0], %r1; + // NVPTX-NEXT: ret; + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // CHECK-LABEL: read_i64 + // + // AMDGPU: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dwordx2 v[4:5], v[0:1] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dwordx2 v[2:3], v[4:5] + // AMDGPU-NEXT: v_add_co_u32_e32 v4, vcc, 8, v4 + // AMDGPU-NEXT: v_addc_co_u32_e32 v5, vcc, 0, v5, vcc + // AMDGPU-NEXT: flat_store_dwordx2 v[0:1], v[4:5] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: v_mov_b32_e32 v0, v2 + // AMDGPU-NEXT: v_mov_b32_e32 v1, v3 + // AMDGPU-NEXT: s_setpc_b64 s[30:31] + // + // NVPTX: ld.param.b64 %rd1, [read_i64_param_0]; + // NVPTX-NEXT: ld.b64 %rd2, [%rd1]; + // NVPTX-NEXT: add.s64 %rd3, %rd2, 7; + // NVPTX-NEXT: and.b64 %rd4, %rd3, -8; + // NVPTX-NEXT: add.s64 %rd5, %rd4, 8; + // NVPTX-NEXT: st.b64 [%rd1], %rd5; + // NVPTX-NEXT: ld.b64 %rd6, [%rd4]; + // NVPTX-NEXT: st.param.b64 [func_retval0], %rd6; + // NVPTX-NEXT: ret; + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { + // CHECK-LABEL: read_i128 + // + // AMDGPU: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: v_mov_b32_e32 v5, v1 + // AMDGPU-NEXT: v_mov_b32_e32 v4, v0 + // AMDGPU-NEXT: flat_load_dwordx2 v[6:7], v[4:5] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dwordx4 v[0:3], v[6:7] + // AMDGPU-NEXT: v_add_co_u32_e32 v6, vcc, 16, v6 + // AMDGPU-NEXT: v_addc_co_u32_e32 v7, vcc, 0, v7, vcc + // AMDGPU-NEXT: flat_store_dwordx2 v[4:5], v[6:7] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: s_setpc_b64 s[30:31] + // + // NVPTX: ld.param.b64 %rd1, [read_i128_param_0]; + // NVPTX-NEXT: ld.b64 %rd2, [%rd1]; + // NVPTX-NEXT: add.s64 %rd3, %rd2, 15; + // NVPTX-NEXT: and.b64 %rd4, %rd3, -16; + // NVPTX-NEXT: add.s64 %rd5, %rd4, 16; + // NVPTX-NEXT: st.b64 [%rd1], %rd5; + // NVPTX-NEXT: ld.v2.b64 {%rd6, %rd7}, [%rd4]; + // NVPTX-NEXT: st.param.v2.b64 [func_retval0], {%rd6, %rd7}; + // NVPTX-NEXT: ret; + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // CHECK-LABEL: read_ptr + // + // AMDGPU: s_waitcnt vmcnt(0) expcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dwordx2 v[4:5], v[0:1] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: flat_load_dwordx2 v[2:3], v[4:5] + // AMDGPU-NEXT: v_add_co_u32_e32 v4, vcc, 8, v4 + // AMDGPU-NEXT: v_addc_co_u32_e32 v5, vcc, 0, v5, vcc + // AMDGPU-NEXT: flat_store_dwordx2 v[0:1], v[4:5] + // AMDGPU-NEXT: s_waitcnt vmcnt(0) lgkmcnt(0) + // AMDGPU-NEXT: v_mov_b32_e32 v0, v2 + // AMDGPU-NEXT: v_mov_b32_e32 v1, v3 + // AMDGPU-NEXT: s_setpc_b64 s[30:31] + // + // NVPTX: ld.param.b64 %rd1, [read_ptr_param_0]; + // NVPTX-NEXT: ld.b64 %rd2, [%rd1]; + // NVPTX-NEXT: add.s64 %rd3, %rd2, 7; + // NVPTX-NEXT: and.b64 %rd4, %rd3, -8; + // NVPTX-NEXT: add.s64 %rd5, %rd4, 8; + // NVPTX-NEXT: st.b64 [%rd1], %rd5; + // NVPTX-NEXT: ld.b64 %rd6, [%rd4]; + // NVPTX-NEXT: st.param.b64 [func_retval0], %rd6; + // NVPTX-NEXT: ret; + va_arg(ap) +} diff --git a/tests/assembly-llvm/c-variadic/powerpc.rs b/tests/assembly-llvm/c-variadic/powerpc.rs new file mode 100644 index 0000000000000..47d35e7579737 --- /dev/null +++ b/tests/assembly-llvm/c-variadic/powerpc.rs @@ -0,0 +1,198 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: POWERPC POWERPC64 POWERPC64LE AIX +//@ [POWERPC] compile-flags: -Copt-level=3 --target powerpc-unknown-linux-gnu +//@ [POWERPC] needs-llvm-components: powerpc +//@ [POWERPC64] compile-flags: -Copt-level=3 --target powerpc64-unknown-linux-gnu +//@ [POWERPC64] needs-llvm-components: powerpc +//@ [POWERPC64LE] compile-flags: -Copt-level=3 --target powerpc64le-unknown-linux-gnu +//@ [POWERPC64LE] needs-llvm-components: powerpc +//@ [AIX] compile-flags: -Copt-level=3 --target powerpc64-ibm-aix +//@ [AIX] needs-llvm-components: powerpc +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +// Check that the assembly that rustc generates matches what clang emits. + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +#[cfg(target_pointer_width = "64")] +unsafe impl VaArgSafe for i128 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // CHECK-LABEL: read_f64 + // + // POWERPC: lbz 5, 1(3) + // POWERPC-NEXT: cmplwi 5, 7 + // POWERPC-NEXT: bgt 0, .LBB0_2 + // POWERPC-NEXT: lwz 4, 8(3) + // POWERPC-NEXT: rlwinm 6, 5, 3, 24, 28 + // POWERPC-NEXT: addi 5, 5, 1 + // POWERPC-NEXT: add 4, 4, 6 + // POWERPC-NEXT: addi 4, 4, 32 + // POWERPC-NEXT: lfd 1, 0(4) + // POWERPC-NEXT: stb 5, 1(3) + // POWERPC-NEXT: blr + // + // POWERPC64: ld 4, 0(3) + // POWERPC64-NEXT: lfd 1, 0(4) + // POWERPC64-NEXT: addi 4, 4, 8 + // POWERPC64-NEXT: std 4, 0(3) + // POWERPC64-NEXT: blr + // + // POWERPC64LE: ld 4, 0(3) + // POWERPC64LE-NEXT: lfd 1, 0(4) + // POWERPC64LE-NEXT: addi 5, 4, 8 + // POWERPC64LE-NEXT: std 5, 0(3) + // POWERPC64LE-NEXT: blr + // + // AIX: ld 4, 0(3) + // AIX-NEXT: lfd 1, 0(4) + // AIX-NEXT: addi 5, 4, 8 + // AIX-NEXT: std 5, 0(3) + // AIX-NEXT: blr + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // CHECK-LABEL: read_i32 + // + // POWERPC: lbz 5, 0(3) + // POWERPC-NEXT: mr 4, 3 + // POWERPC-NEXT: cmplwi 5, 7 + // POWERPC-NEXT: bgt 0, .LBB1_2 + // POWERPC-NEXT: lwz 3, 8(4) + // POWERPC-NEXT: rlwinm 6, 5, 2, 24, 29 + // POWERPC-NEXT: addi 5, 5, 1 + // POWERPC-NEXT: add 3, 3, 6 + // POWERPC-NEXT: lwz 3, 0(3) + // POWERPC-NEXT: stb 5, 0(4) + // POWERPC-NEXT: blr + // + // POWERPC64: ld 5, 0(3) + // POWERPC64-NEXT: mr 4, 3 + // POWERPC64-NEXT: lwa 3, 4(5) + // POWERPC64-NEXT: addi 5, 5, 8 + // POWERPC64-NEXT: std 5, 0(4) + // POWERPC64-NEXT: blr + // + // POWERPC64LE: ld 4, 0(3) + // POWERPC64LE-NEXT: addi 5, 4, 8 + // POWERPC64LE-NEXT: std 5, 0(3) + // POWERPC64LE-NEXT: lwa 3, 0(4) + // POWERPC64LE-NEXT: blr + // + // AIX: ld 4, 0(3) + // AIX-NEXT: addi 5, 4, 8 + // AIX-NEXT: std 5, 0(3) + // AIX-NEXT: lwa 3, 4(4) + // AIX-NEXT: blr + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // CHECK-LABEL: read_i64 + // + // POWERPC: mr 5, 3 + // POWERPC-NEXT: lbz 3, 0(3) + // POWERPC-NEXT: addi 3, 3, 1 + // POWERPC-NEXT: clrlwi 4, 3, 24 + // POWERPC-NEXT: cmplwi 4, 7 + // POWERPC-NEXT: bgt 0, .LBB2_2 + // POWERPC-NEXT: lwz 4, 8(5) + // POWERPC-NEXT: rlwinm 6, 3, 0, 29, 30 + // POWERPC-NEXT: rlwinm 3, 3, 2, 27, 28 + // POWERPC-NEXT: addi 6, 6, 2 + // POWERPC-NEXT: add 4, 4, 3 + // POWERPC-NEXT: lwz 3, 0(4) + // POWERPC-NEXT: lwz 4, 4(4) + // POWERPC-NEXT: stb 6, 0(5) + // POWERPC-NEXT: blr + // + // POWERPC64: ld 5, 0(3) + // POWERPC64-NEXT: mr 4, 3 + // POWERPC64-NEXT: ld 3, 0(5) + // POWERPC64-NEXT: addi 5, 5, 8 + // POWERPC64-NEXT: std 5, 0(4) + // POWERPC64-NEXT: blr + // + // POWERPC64LE: ld 4, 0(3) + // POWERPC64LE-NEXT: addi 5, 4, 8 + // POWERPC64LE-NEXT: std 5, 0(3) + // POWERPC64LE-NEXT: ld 3, 0(4) + // POWERPC64LE-NEXT: blr + // + // AIX: ld 4, 0(3) + // AIX-NEXT: addi 5, 4, 8 + // AIX-NEXT: std 5, 0(3) + // AIX-NEXT: ld 3, 0(4) + // AIX-NEXT: blr + va_arg(ap) +} + +#[unsafe(no_mangle)] +#[cfg(target_pointer_width = "64")] +unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { + // POWERPC64-LABEL: read_i128 + // POWERPC64: ld 6, 0(3) + // POWERPC64-NEXT: mr 5, 3 + // POWERPC64-NEXT: ld 3, 0(6) + // POWERPC64-NEXT: ld 4, 8(6) + // POWERPC64-NEXT: addi 6, 6, 16 + // POWERPC64-NEXT: std 6, 0(5) + // POWERPC64-NEXT: blr + // + // POWERPC64LE-LABEL: read_i128 + // POWERPC64LE: ld 4, 0(3) + // POWERPC64LE-NEXT: addi 5, 4, 16 + // POWERPC64LE-NEXT: std 5, 0(3) + // POWERPC64LE-NEXT: ld 3, 0(4) + // POWERPC64LE-NEXT: ld 4, 8(4) + // POWERPC64LE-NEXT: blr + // + // AIX-LABEL: read_i128 + // AIX: ld 4, 0(3) + // AIX-NEXT: addi 5, 4, 16 + // AIX-NEXT: std 5, 0(3) + // AIX-NEXT: ld 3, 0(4) + // AIX-NEXT: ld 4, 8(4) + // AIX-NEXT: blr + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // POWERPC: read_ptr = read_i32 + // POWERPC64: read_ptr = read_i64 + // POWERPC64LE: read_ptr = read_i64 + va_arg(ap) +} diff --git a/tests/assembly-llvm/c-variadic/riscv.rs b/tests/assembly-llvm/c-variadic/riscv.rs new file mode 100644 index 0000000000000..893d2a65eb37d --- /dev/null +++ b/tests/assembly-llvm/c-variadic/riscv.rs @@ -0,0 +1,127 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: RISCV32 RISCV64 +//@ [RISCV32] compile-flags: -Copt-level=3 --target riscv32gc-unknown-linux-gnu +//@ [RISCV32] needs-llvm-components: riscv +//@ [RISCV64] compile-flags: -Copt-level=3 --target riscv64gc-unknown-linux-gnu +//@ [RISCV64] needs-llvm-components: riscv +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +#[cfg(target_pointer_width = "64")] +unsafe impl VaArgSafe for i128 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // CHECK-LABEL: read_f64 + // + // RISCV32: lw a1, 0(a0) + // RISCV32-NEXT: addi a1, a1, 7 + // RISCV32-NEXT: andi a1, a1, -8 + // RISCV32-NEXT: fld fa0, 0(a1) + // RISCV32-NEXT: addi a1, a1, 8 + // RISCV32-NEXT: sw a1, 0(a0) + // RISCV32-NEXT: ret + // + // RISCV64: ld a1, 0(a0) + // RISCV64-NEXT: fld fa0, 0(a1) + // RISCV64-NEXT: addi a1, a1, 8 + // RISCV64-NEXT: sd a1, 0(a0) + // RISCV64-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // CHECK-LABEL: read_i32 + // + // RISCV32: lw a2, 0(a0) + // RISCV32-NEXT: lw a1, 0(a2) + // RISCV32-NEXT: addi a2, a2, 4 + // RISCV32-NEXT: sw a2, 0(a0) + // RISCV32-NEXT: mv a0, a1 + // RISCV32-NEXT: ret + // + // RISCV64: ld a2, 0(a0) + // RISCV64-NEXT: lw a1, 0(a2) + // RISCV64-NEXT: addi a2, a2, 8 + // RISCV64-NEXT: sd a2, 0(a0) + // RISCV64-NEXT: mv a0, a1 + // RISCV64-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // CHECK-LABEL: read_i64 + // + // RISCV32: lw a1, 0(a0) + // RISCV32-NEXT: addi a1, a1, 7 + // RISCV32-NEXT: andi a3, a1, -8 + // RISCV32-NEXT: lw a2, 0(a3) + // RISCV32-NEXT: lw a1, 4(a3) + // RISCV32-NEXT: addi a3, a3, 8 + // RISCV32-NEXT: sw a3, 0(a0) + // RISCV32-NEXT: mv a0, a2 + // RISCV32-NEXT: ret + // + // RISCV64: ld a2, 0(a0) + // RISCV64-NEXT: ld a1, 0(a2) + // RISCV64-NEXT: addi a2, a2, 8 + // RISCV64-NEXT: sd a2, 0(a0) + // RISCV64-NEXT: mv a0, a1 + // RISCV64-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +#[cfg(target_pointer_width = "64")] +unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { + // RISCV64-LABEL: read_i128 + // + // RISCV64: ld a1, 0(a0) + // RISCV64-NEXT: addi a1, a1, 15 + // RISCV64-NEXT: andi a3, a1, -16 + // RISCV64-NEXT: ld a2, 0(a3) + // RISCV64-NEXT: ld a1, 8(a3) + // RISCV64-NEXT: addi a3, a3, 16 + // RISCV64-NEXT: sd a3, 0(a0) + // RISCV64-NEXT: mv a0, a2 + // RISCV64-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // RISCV32: read_ptr = read_i32 + // RISCV64: read_ptr = read_i64 + va_arg(ap) +} diff --git a/tests/assembly-llvm/c-variadic/s390x.rs b/tests/assembly-llvm/c-variadic/s390x.rs new file mode 100644 index 0000000000000..fef239273e2c5 --- /dev/null +++ b/tests/assembly-llvm/c-variadic/s390x.rs @@ -0,0 +1,133 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ compile-flags: -Copt-level=3 --target s390x-unknown-linux-gnu +//@ needs-llvm-components: systemz +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +// Check that the assembly that rustc generates matches what clang emits. + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +unsafe impl VaArgSafe for i128 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // CHECK-LABEL: read_f64: + // CHECK: lg %r3, 8(%r2) + // CHECK-NEXT: clgijh %r3, 3, .LBB0_2 + // CHECK-NEXT: lg %r1, 24(%r2) + // CHECK-NEXT: sllg %r4, %r3, 3 + // CHECK-NEXT: la %r1, 128(%r4,%r1) + // CHECK-NEXT: la %r0, 1(%r3) + // CHECK-NEXT: stg %r0, 8(%r2) + // CHECK-NEXT: ld %f0, 0(%r1) + // CHECK-NEXT: br %r14 + // CHECK-NEXT: .LBB0_2: + // CHECK-NEXT: lg %r1, 16(%r2) + // CHECK-NEXT: la %r0, 8(%r1) + // CHECK-NEXT: stg %r0, 16(%r2) + // CHECK-NEXT: ld %f0, 0(%r1) + // CHECK-NEXT: br %r14 + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // CHECK-LABEL: read_i32: + // CHECK: lg %r3, 0(%r2) + // CHECK-NEXT: clgijh %r3, 4, .LBB1_2 + // CHECK-NEXT: lg %r1, 24(%r2) + // CHECK-NEXT: sllg %r4, %r3, 3 + // CHECK-NEXT: la %r1, 20(%r4,%r1) + // CHECK-NEXT: la %r0, 1(%r3) + // CHECK-NEXT: stg %r0, 0(%r2) + // CHECK-NEXT: lgf %r2, 0(%r1) + // CHECK-NEXT: br %r14 + // CHECK-NEXT: .LBB1_2: + // CHECK-NEXT: lg %r3, 16(%r2) + // CHECK-NEXT: la %r1, 4(%r3) + // CHECK-NEXT: la %r0, 8(%r3) + // CHECK-NEXT: stg %r0, 16(%r2) + // CHECK-NEXT: lgf %r2, 0(%r1) + // CHECK-NEXT: br %r14 + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // CHECK-LABEL: read_i64: + // CHECK: lg %r3, 0(%r2) + // CHECK-NEXT: clgijh %r3, 4, .LBB2_2 + // CHECK-NEXT: lg %r1, 24(%r2) + // CHECK-NEXT: sllg %r4, %r3, 3 + // CHECK-NEXT: la %r1, 16(%r4,%r1) + // CHECK-NEXT: la %r0, 1(%r3) + // CHECK-NEXT: stg %r0, 0(%r2) + // CHECK-NEXT: lg %r2, 0(%r1) + // CHECK-NEXT: br %r14 + // CHECK-NEXT: .LBB2_2: + // CHECK-NEXT: lg %r1, 16(%r2) + // CHECK-NEXT: la %r0, 8(%r1) + // CHECK-NEXT: stg %r0, 16(%r2) + // CHECK-NEXT: lg %r2, 0(%r1) + // CHECK-NEXT: br %r14 + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { + // CHECK-LABEL: read_i128: + // CHECK: lg %r4, 0(%r3) + // CHECK-NEXT: clgijh %r4, 4, .LBB3_2 + // CHECK-NEXT: lg %r1, 24(%r3) + // CHECK-NEXT: sllg %r5, %r4, 3 + // CHECK-NEXT: la %r1, 16(%r5,%r1) + // CHECK-NEXT: la %r0, 1(%r4) + // CHECK-NEXT: stg %r0, 0(%r3) + // CHECK-NEXT: lg %r1, 0(%r1) + // CHECK-NEXT: mvc 8(8,%r2), 8(%r1) + // CHECK-NEXT: mvc 0(8,%r2), 0(%r1) + // CHECK-NEXT: br %r14 + // CHECK-NEXT: .LBB3_2: + // CHECK-NEXT: lg %r1, 16(%r3) + // CHECK-NEXT: la %r0, 8(%r1) + // CHECK-NEXT: stg %r0, 16(%r3) + // CHECK-NEXT: lg %r1, 0(%r1) + // CHECK-NEXT: mvc 8(8,%r2), 8(%r1) + // CHECK-NEXT: mvc 0(8,%r2), 0(%r1) + // CHECK-NEXT: br %r14 + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // CHECK: read_ptr = read_i64 + va_arg(ap) +} diff --git a/tests/assembly-llvm/c-variadic/sparc.rs b/tests/assembly-llvm/c-variadic/sparc.rs index 59f039e7df28b..9caab3dba7faf 100644 --- a/tests/assembly-llvm/c-variadic/sparc.rs +++ b/tests/assembly-llvm/c-variadic/sparc.rs @@ -20,6 +20,8 @@ pub unsafe trait VaArgSafe {} unsafe impl VaArgSafe for i32 {} unsafe impl VaArgSafe for i64 {} +#[cfg(target_pointer_width = "64")] +unsafe impl VaArgSafe for i128 {} unsafe impl VaArgSafe for f64 {} unsafe impl VaArgSafe for *const T {} @@ -104,6 +106,22 @@ unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { va_arg(ap) } +#[unsafe(no_mangle)] +#[cfg(target_pointer_width = "64")] +unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { + // SPARC64-LABEL: read_i128 + // SPARC64: ldx [%o0], %o1 + // SPARC64-NEXT: add %o1, 15, %o1 + // SPARC64-NEXT: and %o1, -16, %o1 + // SPARC64-NEXT: add %o1, 16, %o2 + // SPARC64-NEXT: stx %o2, [%o0] + // SPARC64-NEXT: ldx [%o1], %o0 + // SPARC64-NEXT: or %o1, 8, %o1 + // SPARC64-NEXT: retl + // SPARC64-NEXT: ldx [%o1], %o1 + va_arg(ap) +} + #[unsafe(no_mangle)] unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { // SPARC: read_ptr = read_i32 diff --git a/tests/assembly-llvm/c-variadic/wasm.rs b/tests/assembly-llvm/c-variadic/wasm.rs new file mode 100644 index 0000000000000..8d05cdb1923b4 --- /dev/null +++ b/tests/assembly-llvm/c-variadic/wasm.rs @@ -0,0 +1,224 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: WASM32 WASM64 +//@ [WASM32] compile-flags: -Copt-level=3 -Zmerge-functions=disabled --target wasm32-unknown-unknown +//@ [WASM32] needs-llvm-components: webassembly +//@ [WASM64] compile-flags: -Copt-level=3 -Zmerge-functions=disabled --target wasm64-unknown-unknown +//@ [WASM64] needs-llvm-components: webassembly +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +// Check that the assembly that rustc generates matches what clang emits. + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +unsafe impl VaArgSafe for i128 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // WASM32-LABEL: read_f64: + // WASM32: local.get 0 + // WASM32-NEXT: local.get 0 + // WASM32-NEXT: i32.load 0 + // WASM32-NEXT: i32.const 7 + // WASM32-NEXT: i32.add + // WASM32-NEXT: i32.const -8 + // WASM32-NEXT: i32.and + // WASM32-NEXT: local.tee 1 + // WASM32-NEXT: i32.const 8 + // WASM32-NEXT: i32.add + // WASM32-NEXT: i32.store 0 + // WASM32-NEXT: local.get 1 + // WASM32-NEXT: f64.load 0 + // WASM32-NEXT: end_function + // + // WASM64-LABEL: read_f64: + // WASM64: local.get 0 + // WASM64-NEXT: local.get 0 + // WASM64-NEXT: i64.load 0 + // WASM64-NEXT: i64.const 7 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.const -8 + // WASM64-NEXT: i64.and + // WASM64-NEXT: local.tee 1 + // WASM64-NEXT: i64.const 8 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.store 0 + // WASM64-NEXT: local.get 1 + // WASM64-NEXT: f64.load 0 + // WASM64-NEXT: end_function + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // WASM32-LABEL: read_i32: + // WASM32: local.get 0 + // WASM32-NEXT: local.get 0 + // WASM32-NEXT: i32.load 0 + // WASM32-NEXT: local.tee 1 + // WASM32-NEXT: i32.const 4 + // WASM32-NEXT: i32.add + // WASM32-NEXT: i32.store 0 + // WASM32-NEXT: local.get 1 + // WASM32-NEXT: i32.load 0 + // WASM32-NEXT: end_function + // + // WASM64-LABEL: read_i32: + // WASM64: local.get 0 + // WASM64-NEXT: local.get 0 + // WASM64-NEXT: i64.load 0 + // WASM64-NEXT: local.tee 1 + // WASM64-NEXT: i64.const 4 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.store 0 + // WASM64-NEXT: local.get 1 + // WASM64-NEXT: i32.load 0 + // WASM64-NEXT: end_function + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // WASM32-LABEL: read_ptr: + // WASM32: local.get 0 + // WASM32-NEXT: local.get 0 + // WASM32-NEXT: i32.load 0 + // WASM32-NEXT: local.tee 1 + // WASM32-NEXT: i32.const 4 + // WASM32-NEXT: i32.add + // WASM32-NEXT: i32.store 0 + // WASM32-NEXT: local.get 1 + // WASM32-NEXT: i32.load 0 + // WASM32-NEXT: end_function + // + // WASM64-LABEL: read_ptr: + // WASM64: local.get 0 + // WASM64-NEXT: local.get 0 + // WASM64-NEXT: i64.load 0 + // WASM64-NEXT: i64.const 7 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.const -8 + // WASM64-NEXT: i64.and + // WASM64-NEXT: local.tee 1 + // WASM64-NEXT: i64.const 8 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.store 0 + // WASM64-NEXT: local.get 1 + // WASM64-NEXT: i64.load 0 + // WASM64-NEXT: end_function + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // WASM32-LABEL: read_i64: + // WASM32: local.get 0 + // WASM32-NEXT: local.get 0 + // WASM32-NEXT: i32.load 0 + // WASM32-NEXT: i32.const 7 + // WASM32-NEXT: i32.add + // WASM32-NEXT: i32.const -8 + // WASM32-NEXT: i32.and + // WASM32-NEXT: local.tee 1 + // WASM32-NEXT: i32.const 8 + // WASM32-NEXT: i32.add + // WASM32-NEXT: i32.store 0 + // WASM32-NEXT: local.get 1 + // WASM32-NEXT: i64.load 0 + // WASM32-NEXT: end_function + // + // WASM64-LABEL: read_i64: + // WASM64: local.get 0 + // WASM64-NEXT: local.get 0 + // WASM64-NEXT: i64.load 0 + // WASM64-NEXT: i64.const 7 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.const -8 + // WASM64-NEXT: i64.and + // WASM64-NEXT: local.tee 1 + // WASM64-NEXT: i64.const 8 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.store 0 + // WASM64-NEXT: local.get 1 + // WASM64-NEXT: i64.load 0 + // WASM64-NEXT: end_function + va_arg(ap) +} + +// Clang and Rustc use a different ABI for i128 on wasm32, and LLVM optimizes differently if we use +// a mutable reference instead of just a pointer. With this setup we match the equivalent Clang +// input exactly. +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i128(out: *mut i128, ap: *mut VaList<'_>) { + // WASM32-LABEL: read_i128: + // WASM32: local.get 1 + // WASM32-NEXT: local.get 1 + // WASM32-NEXT: i32.load 0 + // WASM32-NEXT: i32.const 15 + // WASM32-NEXT: i32.add + // WASM32-NEXT: i32.const -16 + // WASM32-NEXT: i32.and + // WASM32-NEXT: local.tee 2 + // WASM32-NEXT: i32.const 16 + // WASM32-NEXT: i32.add + // WASM32-NEXT: i32.store 0 + // WASM32-NEXT: local.get 0 + // WASM32-NEXT: local.get 2 + // WASM32-NEXT: i64.load 0 + // WASM32-NEXT: i64.store 0 + // WASM32-NEXT: local.get 0 + // WASM32-NEXT: local.get 2 + // WASM32-NEXT: i64.load 8 + // WASM32-NEXT: i64.store 8 + // WASM32-NEXT: end_function + // + // WASM64-LABEL: read_i128: + // WASM64: local.get 1 + // WASM64-NEXT: local.get 1 + // WASM64-NEXT: i64.load 0 + // WASM64-NEXT: i64.const 15 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.const -16 + // WASM64-NEXT: i64.and + // WASM64-NEXT: local.tee 2 + // WASM64-NEXT: i64.const 16 + // WASM64-NEXT: i64.add + // WASM64-NEXT: i64.store 0 + // WASM64-NEXT: local.get 0 + // WASM64-NEXT: local.get 2 + // WASM64-NEXT: i64.load 0 + // WASM64-NEXT: i64.store 0 + // WASM64-NEXT: local.get 0 + // WASM64-NEXT: local.get 2 + // WASM64-NEXT: i64.load 8 + // WASM64-NEXT: i64.store 8 + // WASM64-NEXT: end_function + *out = va_arg(mem::transmute(ap)); +} diff --git a/tests/assembly-llvm/c-variadic/x86-linux.rs b/tests/assembly-llvm/c-variadic/x86-linux.rs new file mode 100644 index 0000000000000..8cc4731346b17 --- /dev/null +++ b/tests/assembly-llvm/c-variadic/x86-linux.rs @@ -0,0 +1,237 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: X86_64 X86_64_GNUX32 I686 +//@ [X86_64] compile-flags: -Copt-level=3 -Cllvm-args=-x86-asm-syntax=intel +//@ [X86_64] compile-flags: --target x86_64-unknown-linux-gnu +//@ [X86_64] needs-llvm-components: x86 +//@ [X86_64_GNUX32] compile-flags: -Copt-level=3 -Cllvm-args=-x86-asm-syntax=intel +//@ [X86_64_GNUX32] compile-flags: --target x86_64-unknown-linux-gnux32 +//@ [X86_64_GNUX32] needs-llvm-components: x86 +//@ [I686] compile-flags: -Copt-level=3 -Cllvm-args=-x86-asm-syntax=intel +//@ [I686] compile-flags: --target i686-unknown-linux-gnu +//@ [I686] needs-llvm-components: x86 +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +// Check that the assembly that rustc generates matches what clang emits. + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +#[cfg(target_pointer_width = "64")] +unsafe impl VaArgSafe for i128 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // CHECK-LABEL: read_f64 + + // X86_64: mov ecx, dword ptr [rdi + 4] + // X86_64-NEXT: cmp rcx, 160 + // X86_64-NEXT: ja .LBB0_2 + // X86_64-NEXT: mov rax, rcx + // X86_64-NEXT: add rax, qword ptr [rdi + 16] + // X86_64-NEXT: add ecx, 16 + // X86_64-NEXT: mov dword ptr [rdi + 4], ecx + // X86_64-NEXT: movsd xmm0, qword ptr [rax] + // X86_64-NEXT: ret + // X86_64-NEXT: .LBB0_2: + // X86_64-NEXT: mov rax, qword ptr [rdi + 8] + // X86_64-NEXT: lea rcx, [rax + 8] + // X86_64-NEXT: mov qword ptr [rdi + 8], rcx + // X86_64-NEXT: movsd xmm0, qword ptr [rax] + // X86_64-NEXT: ret + + // X86_64-NEXT_GNUX32: mov ecx, dword ptr [edi + 4] + // X86_64-NEXT_GNUX32-NEXT: cmp ecx, 160 + // X86_64-NEXT_GNUX32-NEXT: ja .LBB0_2 + // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [edi + 12] + // X86_64-NEXT_GNUX32-NEXT: add eax, ecx + // X86_64-NEXT_GNUX32-NEXT: add ecx, 16 + // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi + 4], ecx + // X86_64-NEXT_GNUX32-NEXT: movsd xmm0, qword ptr [eax] + // X86_64-NEXT_GNUX32-NEXT: ret + // X86_64-NEXT_GNUX32-NEXT: .LBB0_2: + // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [edi + 8] + // X86_64-NEXT_GNUX32-NEXT: lea ecx, [rax + 8] + // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi + 8], ecx + // X86_64-NEXT_GNUX32-NEXT: movsd xmm0, qword ptr [eax] + // X86_64-NEXT_GNUX32-NEXT: ret + + // I686: mov eax, dword ptr [esp + 4] + // I686-NEXT: mov ecx, dword ptr [eax] + // I686-NEXT: lea edx, [ecx + 8] + // I686-NEXT: mov dword ptr [eax], edx + // I686-NEXT: fld qword ptr [ecx] + // I686-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // CHECK-LABEL: read_i32 + // + // X86_64: mov ecx, dword ptr [rdi] + // X86_64-NEXT: cmp rcx, 40 + // X86_64-NEXT: ja .LBB1_2 + // X86_64-NEXT: mov rax, rcx + // X86_64-NEXT: add rax, qword ptr [rdi + 16] + // X86_64-NEXT: add ecx, 8 + // X86_64-NEXT: mov dword ptr [rdi], ecx + // X86_64-NEXT: mov eax, dword ptr [rax] + // X86_64-NEXT: ret + // X86_64-NEXT: .LBB1_2: + // X86_64-NEXT: mov rax, qword ptr [rdi + 8] + // X86_64-NEXT: lea rcx, [rax + 8] + // X86_64-NEXT: mov qword ptr [rdi + 8], rcx + // X86_64-NEXT: mov eax, dword ptr [rax] + // X86_64-NEXT: ret + + // X86_64-NEXT_GNUX32: mov ecx, dword ptr [edi] + // X86_64-NEXT_GNUX32-NEXT: cmp ecx, 40 + // X86_64-NEXT_GNUX32-NEXT: ja .LBB1_2 + // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [edi + 12] + // X86_64-NEXT_GNUX32-NEXT: add eax, ecx + // X86_64-NEXT_GNUX32-NEXT: add ecx, 8 + // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi], ecx + // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [eax] + // X86_64-NEXT_GNUX32-NEXT: ret + // X86_64-NEXT_GNUX32-NEXT: .LBB1_2: + // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [edi + 8] + // X86_64-NEXT_GNUX32-NEXT: lea ecx, [rax + 8] + // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi + 8], ecx + // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [eax] + // X86_64-NEXT_GNUX32-NEXT: ret + + // I686: mov eax, dword ptr [esp + 4] + // I686-NEXT: mov ecx, dword ptr [eax] + // I686-NEXT: lea edx, [ecx + 4] + // I686-NEXT: mov dword ptr [eax], edx + // I686-NEXT: mov eax, dword ptr [ecx] + // I686-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // CHECK-LABEL: read_i64 + + // X86_64: mov ecx, dword ptr [rdi] + // X86_64-NEXT: cmp rcx, 40 + // X86_64-NEXT: ja .LBB2_2 + // X86_64-NEXT: mov rax, rcx + // X86_64-NEXT: add rax, qword ptr [rdi + 16] + // X86_64-NEXT: add ecx, 8 + // X86_64-NEXT: mov dword ptr [rdi], ecx + // X86_64-NEXT: mov rax, qword ptr [rax] + // X86_64-NEXT: ret + // X86_64-NEXT: .LBB2_2: + // X86_64-NEXT: mov rax, qword ptr [rdi + 8] + // X86_64-NEXT: lea rcx, [rax + 8] + // X86_64-NEXT: mov qword ptr [rdi + 8], rcx + // X86_64-NEXT: mov rax, qword ptr [rax] + // X86_64-NEXT: ret + + // X86_64-NEXT_GNUX32: mov ecx, dword ptr [edi] + // X86_64-NEXT_GNUX32-NEXT: cmp ecx, 40 + // X86_64-NEXT_GNUX32-NEXT: ja .LBB2_2 + // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [edi + 12] + // X86_64-NEXT_GNUX32-NEXT: add eax, ecx + // X86_64-NEXT_GNUX32-NEXT: add ecx, 8 + // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi], ecx + // X86_64-NEXT_GNUX32-NEXT: mov rax, qword ptr [eax] + // X86_64-NEXT_GNUX32-NEXT: ret + // X86_64-NEXT_GNUX32-NEXT: .LBB2_2: + // X86_64-NEXT_GNUX32-NEXT: mov eax, dword ptr [edi + 8] + // X86_64-NEXT_GNUX32-NEXT: lea ecx, [rax + 8] + // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi + 8], ecx + // X86_64-NEXT_GNUX32-NEXT: mov rax, qword ptr [eax] + // X86_64-NEXT_GNUX32-NEXT: ret + + // I686: mov eax, dword ptr [esp + 4] + // I686-NEXT: mov ecx, dword ptr [eax] + // I686-NEXT: lea edx, [ecx + 8] + // I686-NEXT: mov dword ptr [eax], edx + // I686-NEXT: mov eax, dword ptr [ecx] + // I686-NEXT: mov edx, dword ptr [ecx + 4] + // I686-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +#[cfg(target_pointer_width = "64")] +unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { + // X86_64-LABEL: read_i128 + // + // X86_64: mov ecx, dword ptr [rdi] + // X86_64-NEXT: cmp rcx, 32 + // X86_64-NEXT: ja .LBB3_2 + // X86_64-NEXT: mov rdx, qword ptr [rdi + 16] + // X86_64-NEXT: mov rax, qword ptr [rdx + rcx] + // X86_64-NEXT: mov rdx, qword ptr [rdx + rcx + 8] + // X86_64-NEXT: add ecx, 16 + // X86_64-NEXT: mov dword ptr [rdi], ecx + // X86_64-NEXT: ret + // X86_64-NEXT: .LBB3_2: + // X86_64-NEXT: mov rcx, qword ptr [rdi + 8] + // X86_64-NEXT: add rcx, 15 + // X86_64-NEXT: and rcx, -16 + // X86_64-NEXT: lea rax, [rcx + 16] + // X86_64-NEXT: mov qword ptr [rdi + 8], rax + // X86_64-NEXT: mov rax, qword ptr [rcx] + // X86_64-NEXT: mov rdx, qword ptr [rcx + 8] + // X86_64-NEXT: ret + + // X86_64-NEXT_GNUX32: mov ecx, dword ptr [edi] + // X86_64-NEXT_GNUX32-NEXT: cmp ecx, 32 + // X86_64-NEXT_GNUX32-NEXT: ja .LBB3_2 + // X86_64-NEXT_GNUX32-NEXT: mov edx, dword ptr [edi + 12] + // X86_64-NEXT_GNUX32-NEXT: mov rax, qword ptr [edx + ecx] + // X86_64-NEXT_GNUX32-NEXT: mov rdx, qword ptr [edx + ecx + 8] + // X86_64-NEXT_GNUX32-NEXT: add ecx, 16 + // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi], ecx + // X86_64-NEXT_GNUX32-NEXT: ret + // X86_64-NEXT_GNUX32-NEXT: .LBB3_2: + // X86_64-NEXT_GNUX32-NEXT: mov ecx, dword ptr [edi + 8] + // X86_64-NEXT_GNUX32-NEXT: add ecx, 15 + // X86_64-NEXT_GNUX32-NEXT: and ecx, -16 + // X86_64-NEXT_GNUX32-NEXT: lea eax, [rcx + 16] + // X86_64-NEXT_GNUX32-NEXT: mov dword ptr [edi + 8], eax + // X86_64-NEXT_GNUX32-NEXT: mov rax, qword ptr [ecx] + // X86_64-NEXT_GNUX32-NEXT: mov rdx, qword ptr [ecx + 8] + // X86_64-NEXT_GNUX32-NEXT: ret + + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // X86_64: read_ptr = read_i64 + // X86_64_GNUX32: read_ptr = read_i32 + // I686: read_ptr = read_i32 + va_arg(ap) +} diff --git a/tests/assembly-llvm/c-variadic/x86_64-windows.rs b/tests/assembly-llvm/c-variadic/x86_64-windows.rs new file mode 100644 index 0000000000000..3ddf262776838 --- /dev/null +++ b/tests/assembly-llvm/c-variadic/x86_64-windows.rs @@ -0,0 +1,94 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: WINDOWS_GNU WINDOWS_MSVC +//@ [WINDOWS_GNU] compile-flags: -Copt-level=3 -Cllvm-args=-x86-asm-syntax=intel +//@ [WINDOWS_GNU] compile-flags: --target x86_64-pc-windows-gnu +//@ [WINDOWS_GNU] needs-llvm-components: x86 +//@ [WINDOWS_MSVC] compile-flags: -Copt-level=3 -Cllvm-args=-x86-asm-syntax=intel +//@ [WINDOWS_MSVC] compile-flags: --target x86_64-pc-windows-msvc +//@ [WINDOWS_MSVC] needs-llvm-components: x86 +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs)] +#![no_core] +#![crate_type = "lib"] + +// Check that the assembly that rustc generates matches what clang emits. + +extern crate minicore; +use minicore::*; + +#[lang = "va_arg_safe"] +pub unsafe trait VaArgSafe {} + +unsafe impl VaArgSafe for i32 {} +unsafe impl VaArgSafe for i64 {} +unsafe impl VaArgSafe for i128 {} +unsafe impl VaArgSafe for f64 {} +unsafe impl VaArgSafe for *const T {} + +#[repr(transparent)] +struct VaListInner { + ptr: *const c_void, +} + +#[repr(transparent)] +#[lang = "va_list"] +pub struct VaList<'a> { + inner: VaListInner, + _marker: PhantomData<&'a mut ()>, +} + +#[rustc_intrinsic] +#[rustc_nounwind] +pub const unsafe fn va_arg(ap: &mut VaList<'_>) -> T; + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_f64(ap: &mut VaList<'_>) -> f64 { + // CHECK-LABEL: read_f64: + // CHECK: mov rax, qword ptr [rcx] + // CHECK-NEXT: lea rdx, [rax + 8] + // CHECK-NEXT: mov qword ptr [rcx], rdx + // CHECK-NEXT: movsd xmm0, qword ptr [rax] + // CHECK-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // CHECK-LABEL: read_i32: + // CHECK: mov rax, qword ptr [rcx] + // CHECK-NEXT: lea rdx, [rax + 8] + // CHECK-NEXT: mov qword ptr [rcx], rdx + // CHECK-NEXT: mov eax, dword ptr [rax] + // CHECK-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // CHECK-LABEL: read_i64: + // CHECK: mov rax, qword ptr [rcx] + // CHECK-NEXT: lea rdx, [rax + 8] + // CHECK-NEXT: mov qword ptr [rcx], rdx + // CHECK-NEXT: mov rax, qword ptr [rax] + // CHECK-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i128(ap: &mut VaList<'_>) -> i128 { + // CHECK-LABEL: read_i128: + // CHECK: mov rax, qword ptr [rcx] + // CHECK-NEXT: lea rdx, [rax + 8] + // CHECK-NEXT: mov qword ptr [rcx], rdx + // CHECK-NEXT: mov rax, qword ptr [rax] + // CHECK-NEXT: movups xmm0, xmmword ptr [rax] + // CHECK-NEXT: ret + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // CHECK: read_ptr = read_i64 + va_arg(ap) +} diff --git a/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs b/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs index f3d82474e94aa..9fc7523ecc7f3 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs +++ b/tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs @@ -1,5 +1,5 @@ #![crate_type = "staticlib"] -#![feature(c_variadic)] +#![feature(c_variadic, c_variadic_int128)] use core::ffi::{CStr, VaList, c_char, c_double, c_int, c_long, c_longlong}; @@ -57,6 +57,49 @@ pub unsafe extern "C" fn check_list_copy_0(mut ap: VaList) -> usize { if compare_c_str(ap.next_arg::<*const c_char>(), c"Correct") { 0 } else { 0xff } } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn check_list_i128(mut ap: VaList) -> usize { + cfg_select! { + any( + target_arch = "aarch64", + target_arch = "amdgpu", + target_arch = "arm64ec", + target_arch = "bpf", + target_arch = "loongarch64", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "nvptx64", + target_arch = "powerpc64", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "sparc64", + target_arch = "wasm32", + target_arch = "wasm64", + target_arch = "x86_64", + ) => { + #[cfg(not(any( + target_arch = "wasm32", + target_abi = "x32", + target_pointer_width = "64", + )))] + compile_error!("unexpected target architecture for 128-bit c-variadic"); + + continue_if!(ap.next_arg::() == -42); + // use a 32-bit value here to test the alignment logic. + continue_if!(ap.next_arg::() == 0xAAAA_AAAAu32.cast_signed()); + continue_if!(ap.next_arg::() == u128::MAX); + + return 0; + } + _ => { + // This function was called a platform where rustc does not implement + // VaArgSafe for i128 but clang does define __int128. Rustc should add + // the implementation if this comes up. + 0xFF + } + } +} + #[unsafe(no_mangle)] pub unsafe extern "C" fn check_varargs_0(_: c_int, mut ap: ...) -> usize { continue_if!(ap.next_arg::() == 42); diff --git a/tests/run-make/c-link-to-rust-va-list-fn/test.c b/tests/run-make/c-link-to-rust-va-list-fn/test.c index b368302326c71..c7510a29445a5 100644 --- a/tests/run-make/c-link-to-rust-va-list-fn/test.c +++ b/tests/run-make/c-link-to-rust-va-list-fn/test.c @@ -8,6 +8,7 @@ extern size_t check_list_0(va_list ap); extern size_t check_list_1(va_list ap); extern size_t check_list_2(va_list ap); extern size_t check_list_copy_0(va_list ap); +extern size_t check_list_i128(va_list ap); extern size_t check_varargs_0(int fixed, ...); extern size_t check_varargs_1(int fixed, ...); extern size_t check_varargs_2(int fixed, ...); @@ -38,6 +39,10 @@ int main(int argc, char* argv[]) { assert(test_rust(check_list_copy_0, 6.28, 16, 'A', "Skip Me!", "Correct") == 0); +#if defined(__SIZEOF_INT128__) + assert(test_rust(check_list_i128, (__int128)-42, 0xAAAAAAAA, (unsigned __int128)-1) == 0); +#endif + assert(check_varargs_0(0, 42, "Hello, World!") == 0); assert(check_varargs_1(0, 3.14, 12l, 'A', 0x1LL) == 0); diff --git a/tests/ui/c-variadic/roundtrip.rs b/tests/ui/c-variadic/roundtrip.rs index 22a545fec5a88..ab6d61a5b8250 100644 --- a/tests/ui/c-variadic/roundtrip.rs +++ b/tests/ui/c-variadic/roundtrip.rs @@ -1,6 +1,13 @@ //@ run-pass //@ ignore-backends: gcc -#![feature(c_variadic, const_c_variadic, const_destruct, const_raw_ptr_comparison)] +#![feature( + c_variadic, + const_c_variadic, + c_variadic_int128, + const_destruct, + const_raw_ptr_comparison +)] +#![allow(unused_features)] // c_variadic_int128 is only used on 64-bit targets. use std::ffi::*; @@ -12,7 +19,8 @@ use std::ffi::*; const unsafe extern "C" fn variadic(mut ap: ...) -> (T, T) { let x = ap.next_arg::(); // Intersperse a small type to test alignment logic. A `u32` (i.e. `c_uint`) is the smallest - // type that implements `VaArgSafe`: smaller types would automatically be promoted. + // type that implements `VaArgSafe` (except on some 16-bit targets): smaller types would + // automatically be promoted. assert!(ap.next_arg::() == 0xAAAA_AAAA); let y = ap.next_arg::(); @@ -74,5 +82,38 @@ fn main() { static mut B: u32 = 2u32; roundtrip_ptr!(*const u32, &raw const A, &raw const B); roundtrip_ptr!(*mut u32, &raw mut A, &raw mut B); + + // The 128-bit integers only implement VaArgSafe on some targets, a subset of those that + // define `__int128`. We test some of those targets here. + cfg_select! { + any( + target_arch = "aarch64", + target_arch = "amdgpu", + target_arch = "arm64ec", + target_arch = "bpf", + target_arch = "loongarch64", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "nvptx64", + target_arch = "powerpc64", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "sparc64", + target_arch = "wasm32", + target_arch = "wasm64", + target_arch = "x86_64", + ) => { + #[cfg(not(any( + target_arch = "wasm32", + target_abi = "x32", + target_pointer_width = "64", + )))] + compile_error!("unexpected target architecture for 128-bit c-variadic"); + + roundtrip!(i128, -1, -2); + roundtrip!(u128, 1, 2); + } + _ => {} + } } }