diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index c4ff1eee56750..fe1475621ac9d 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -347,8 +347,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { // 64-bit floats are always OK. } Primitive::Float(Float::F128) => { - // FIXME(f128) figure out whether we should support this. - bug!("the va_arg intrinsic does not support `f128`") + // Supported on some targets, especially where long double is IEEE f128. } } diff --git a/library/core/src/ffi/va_list.rs b/library/core/src/ffi/va_list.rs index 63942003c568b..5b71a73a5e1aa 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -415,6 +415,50 @@ cfg_select! { #[stable(feature = "c_variadic", since = "1.99.0")] unsafe impl VaArgSafe for f64 {} +// Implement `VaArgSafe` for 128-bit integers on targets where either: +// +// - clang provides `__float128` +// - `long double` is IEEE f128 on the platform. +// +// When updating this cfg, also update the tests to match. Currently this condition +// is duplicated in: +// +// - tests/ui/c-variadic/roundtrip.rs +// - tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs +cfg_select! { + any( + all(target_arch = "x86_64", not(target_vendor = "apple"), not(target_env = "msvc")), + // Clang 23 hits https://github.com/llvm/llvm-project/issues/217747. + all(target_arch = "x86", not(target_vendor = "apple"), not(target_env = "msvc")), + // PowerPC requires VSX - only little endian has it enabled by default. + all(target_arch = "powerpc64", target_endian = "little"), + all( + not(windows), + not(target_vendor = "apple"), + any( + target_arch = "aarch64", + target_arch = "loongarch32", + target_arch = "loongarch64", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "riscv32", + target_arch = "riscv64", + target_arch = "s390x", + // Clang 23 on sparc hits https://github.com/llvm/llvm-project/pull/214981. + target_arch = "sparc", + target_arch = "sparc64", + target_arch = "wasm32", + target_arch = "wasm64", + ), + ), + ) => { + #[unstable_feature_bound(f128)] + #[unstable(feature = "f128", issue = "116909")] + unsafe impl VaArgSafe for f128 {} + } + _ => { /* unsupported */ } +} + #[stable(feature = "c_variadic", since = "1.99.0")] unsafe impl VaArgSafe for *mut T {} #[stable(feature = "c_variadic", since = "1.99.0")] 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 37110e75f4779..e19ebe7bb395a 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_int128, c_variadic_experimental_arch)] +#![feature(c_variadic_int128, c_variadic_experimental_arch, f128)] use core::ffi::{CStr, VaList, c_char, c_double, c_int, c_long, c_longlong}; @@ -100,6 +100,47 @@ pub unsafe extern "C" fn check_list_i128(mut ap: VaList) -> usize { } } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn check_list_f128(mut ap: VaList) -> usize { + cfg_select! { + any( + all(target_arch = "x86_64", not(target_vendor = "apple"), not(target_env = "msvc")), + all(target_arch = "x86", not(target_vendor = "apple"), not(target_env = "msvc")), + all(target_arch = "powerpc64", target_endian = "little"), + all( + not(windows), + not(target_vendor = "apple"), + any( + target_arch = "aarch64", + target_arch = "loongarch32", + target_arch = "loongarch64", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "sparc", + target_arch = "sparc64", + target_arch = "wasm32", + target_arch = "wasm64", + ), + ), + ) => { + continue_if!(ap.next_arg::() == -42.0); + // 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::() == f128::NEG_INFINITY); + + return 0; + } + _ => { + // This function was called a platform where rustc does not implement + // VaArgSafe for f128 but clang does define _Float128. 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 c7510a29445a5..f378181cdde79 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 @@ -3,12 +3,14 @@ #include #include #include +#include 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_list_f128(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, ...); @@ -43,6 +45,22 @@ int main(int argc, char* argv[]) { assert(test_rust(check_list_i128, (__int128)-42, 0xAAAAAAAA, (unsigned __int128)-1) == 0); #endif + // Run the f128 test when __float128/_Float128 is defined or long double is IEEE f128. + // Use #define instead of typedef so that `#ifdef` can detect it. +#if defined(__LDBL_MANT_DIG__) && __LDBL_MANT_DIG__ == 113 +#define f128 long double +#elif defined(__SIZEOF_FLOAT128__) +#ifdef __clang__ +#define f128 __float128 +#else +#define f128 _Float128 +#endif +#endif + +#ifdef f128 + assert(test_rust(check_list_f128, (f128)-42.0, 0xAAAAAAAA, (f128)-INFINITY) == 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 59ab4099f7620..31b827eee1953 100644 --- a/tests/ui/c-variadic/roundtrip.rs +++ b/tests/ui/c-variadic/roundtrip.rs @@ -1,6 +1,6 @@ //@ run-pass //@ ignore-backends: gcc -#![feature(const_c_variadic, c_variadic_int128, const_destruct, const_raw_ptr_comparison)] +#![feature(const_c_variadic, c_variadic_int128, const_destruct, const_raw_ptr_comparison, f128)] #![allow(unused_features)] // c_variadic_int128 is only used on 64-bit targets. use std::ffi::*; @@ -107,7 +107,38 @@ fn main() { roundtrip!(i128, -1, -2); roundtrip!(u128, 1, 2); } - _ => {} + _ => { /* unsupported */ } + } + + cfg_select! { + any( + all( + any(target_arch = "x86_64", target_arch = "x86"), + not(target_vendor = "apple"), + not(target_env = "msvc") + ), + all(target_arch = "powerpc64", target_endian = "little"), + all( + not(windows), + not(target_vendor = "apple"), + any( + target_arch = "aarch64", + target_arch = "loongarch32", + target_arch = "loongarch64", + target_arch = "mips64", + target_arch = "mips64r6", + target_arch = "riscv64", + target_arch = "s390x", + target_arch = "sparc", + target_arch = "sparc64", + target_arch = "wasm32", + target_arch = "wasm64", + ), + ), + ) => { + roundtrip!(f128, -1.0, f128::MAX); + } + _ => { /* unsupported */ } } } }