diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 3cd4e490fd5b..cfdd47b3a8c8 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -88,11 +88,30 @@ enum SlotSize { Bytes1 = 1, } +/// Whether to respect a value alignment that is higher than the slot alignment. +/// +/// When `No` the argument is in the next slot, when `Yes` there will be empty slots +/// until a slot's starting address has the required alignment. enum AllowHigherAlign { No, Yes, } +/// Determines where in the slot the value is located. Only takes effect on big-endian targets. +/// +/// with 8-byte slots, a 32-bit integer is either stored right-adjusted: +/// +/// ```text +/// [0x0, 0x0, 0x0, 0x0, 0xaa, 0xaa, 0xaa, 0xaa] +/// ``` +/// +/// or left-adjusted: +/// +/// ```text +/// [0xaa, 0xaa, 0xaa, 0xaa, 0x0, 0x0, 0x0, 0x0] +/// ``` +/// +/// Most big-endian targets store values as right-adjusted. enum ForceRightAdjust { No, Yes, @@ -1169,7 +1188,8 @@ pub(super) fn emit_va_arg<'ll, 'tcx>( if target_ty_size > 2 * 8 { PassMode::Indirect } else { PassMode::Direct }, SlotSize::Bytes8, AllowHigherAlign::Yes, - ForceRightAdjust::No, + // sparc64 is a big-endian target and stores variable arguments right-adjusted. + ForceRightAdjust::Yes, ), Arch::Mips | Arch::Mips32r6 | Arch::Mips64 | Arch::Mips64r6 => emit_ptr_va_arg( bx, diff --git a/tests/assembly-llvm/c-variadic-arm.rs b/tests/assembly-llvm/c-variadic/arm.rs similarity index 73% rename from tests/assembly-llvm/c-variadic-arm.rs rename to tests/assembly-llvm/c-variadic/arm.rs index 1cfc115badee..682e9736958b 100644 --- a/tests/assembly-llvm/c-variadic-arm.rs +++ b/tests/assembly-llvm/c-variadic/arm.rs @@ -7,7 +7,9 @@ #![crate_type = "lib"] #![feature(c_variadic)] -// Check that the assembly that rustc generates matches what clang emits. +// Check that the assembly that rustc generates matches what clang emits. This example in particular +// is related to https://github.com/rust-lang/rust/pull/144549 and shows the effect of us correctly +// emitting annotations that start and end the lifetime of the va_list. #[unsafe(no_mangle)] unsafe extern "C" fn variadic(a: f64, mut args: ...) -> f64 { diff --git a/tests/assembly-llvm/c-variadic-mips.rs b/tests/assembly-llvm/c-variadic/mips.rs similarity index 100% rename from tests/assembly-llvm/c-variadic-mips.rs rename to tests/assembly-llvm/c-variadic/mips.rs diff --git a/tests/assembly-llvm/c-variadic/sparc.rs b/tests/assembly-llvm/c-variadic/sparc.rs new file mode 100644 index 000000000000..59f039e7df28 --- /dev/null +++ b/tests/assembly-llvm/c-variadic/sparc.rs @@ -0,0 +1,112 @@ +//@ add-minicore +//@ assembly-output: emit-asm +// +//@ revisions: SPARC SPARC64 +//@ [SPARC] compile-flags: -Copt-level=3 --target sparc-unknown-linux-gnu +//@ [SPARC] needs-llvm-components: sparc +//@ [SPARC64] compile-flags: -Copt-level=3 --target sparc64-unknown-linux-gnu +//@ [SPARC64] needs-llvm-components: sparc +#![feature(c_variadic, no_core, lang_items, intrinsics, rustc_attrs, asm_experimental_arch)] +#![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 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 + // + // SPARC: ld [%o0], %o1 + // SPARC-NEXT: add %o1, 8, %o2 + // SPARC-NEXT: st %o2, [%o0] + // SPARC-NEXT: ld [%o1+4], %o0 + // SPARC-NEXT: add %sp, 96, %o2 + // SPARC-NEXT: or %o2, 4, %o2 + // SPARC-NEXT: st %o0, [%o2] + // SPARC-NEXT: ld [%o1], %o0 + // SPARC-NEXT: st %o0, [%sp+96] + // SPARC-NEXT: ldd [%sp+96], %f0 + // SPARC-NEXT: retl + // SPARC-NEXT: add %sp, 104, %sp + // + // SPARC64: ldx [%o0], %o1 + // SPARC64-NEXT: add %o1, 8, %o2 + // SPARC64-NEXT: stx %o2, [%o0] + // SPARC64-NEXT: retl + // SPARC64-NEXT: ldd [%o1], %f0 + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i32(ap: &mut VaList<'_>) -> i32 { + // CHECK-LABEL: read_i32 + // + // SPARC: ld [%o0], %o1 + // SPARC-NEXT: add %o1, 4, %o2 + // SPARC-NEXT: st %o2, [%o0] + // SPARC-NEXT: retl + // SPARC-NEXT: ld [%o1], %o0 + // + // SPARC64: ldx [%o0], %o1 + // SPARC64-NEXT: add %o1, 8, %o2 + // SPARC64-NEXT: stx %o2, [%o0] + // SPARC64-NEXT: retl + // SPARC64-NEXT: ldsw [%o1+4], %o0 + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_i64(ap: &mut VaList<'_>) -> i64 { + // CHECK-LABEL: read_i64 + // + // SPARC: ld [%o0], %o1 + // SPARC-NEXT: add %o1, 4, %o2 + // SPARC-NEXT: st %o2, [%o0] + // SPARC-NEXT: ld [%o1], %o2 + // SPARC-NEXT: add %o1, 8, %o3 + // SPARC-NEXT: st %o3, [%o0] + // SPARC-NEXT: ld [%o1+4], %o1 + // SPARC-NEXT: retl + // SPARC-NEXT: mov %o2, %o0 + // + // SPARC64: ldx [%o0], %o1 + // SPARC64-NEXT: add %o1, 8, %o2 + // SPARC64-NEXT: stx %o2, [%o0] + // SPARC64-NEXT: retl + // SPARC64-NEXT: ldx [%o1], %o0 + va_arg(ap) +} + +#[unsafe(no_mangle)] +unsafe extern "C" fn read_ptr(ap: &mut VaList<'_>) -> *const u8 { + // SPARC: read_ptr = read_i32 + // SPARC64: read_ptr = read_i64 + va_arg(ap) +}