From 3d281d48815e6213ad9911e97d5d8d59d2348b1a Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Thu, 3 Sep 2026 21:25:04 +0200 Subject: [PATCH] implement `VaArgSafe` for `f128` --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 3 +- library/core/src/ffi/va_list.rs | 49 +++++++++++++++++ .../c-link-to-rust-va-list-fn/checkrust.rs | 55 ++++++++++++++++++- .../run-make/c-link-to-rust-va-list-fn/test.c | 39 +++++++++++++ tests/ui/c-variadic/roundtrip.rs | 36 +++++++++++- 5 files changed, 177 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index db896fa9c0f2b..c71e83d8f99bd 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 21f8e264db953..b308cb7f85df2 100644 --- a/library/core/src/ffi/va_list.rs +++ b/library/core/src/ffi/va_list.rs @@ -416,6 +416,55 @@ cfg_select! { #[unstable(feature = "c_variadic_va_arg_safe", issue = "162911", implied_by = "c_variadic")] unsafe impl VaArgSafe for f64 {} +// Implement `VaArgSafe` for f128 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 +// +// # Known incompatibilities +// +// Testing versus clang exposed bugs in clang. GCC has no known incompatibilities. +// +// - Clang <= 23 on sparc, see https://github.com/llvm/llvm-project/pull/214981. +// - Clang <= 23 on x86, see https://github.com/llvm/llvm-project/issues/217747. +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")), + // PowerPC requires VSX (only little endian has it enabled by default). + all(target_arch = "powerpc64", target_feature = "vsx"), + 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", + 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 */ } +} + #[unstable(feature = "c_variadic_va_arg_safe", issue = "162911", implied_by = "c_variadic")] unsafe impl VaArgSafe for *mut T {} #[unstable(feature = "c_variadic_va_arg_safe", issue = "162911", implied_by = "c_variadic")] 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..3f9698afe2d6c 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,59 @@ pub unsafe extern "C" fn check_list_i128(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_feature = "vsx"), + 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", + ), + ), + ) => { + #[unsafe(no_mangle)] + pub static RUST_HAS_F128: c_int = 1; + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn check_list_f128(mut ap: VaList) -> usize { + 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::MAX); + + return 0; + } + } + _ => { + #[unsafe(no_mangle)] + pub static RUST_HAS_F128: c_int = 0; + + #[unsafe(no_mangle)] + pub unsafe extern "C" fn check_list_f128(_: VaList) -> usize { + // This function was called a platform where rustc does not implement + // VaArgSafe for f128 but clang does define _Float128. + // + // This occurs on powerpc64 where f128 support depends on a target feature. + // + // Otherwise, 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..14c507cd6cccd 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, ...); @@ -21,6 +23,9 @@ extern size_t run_test_va_list_by_value(); extern size_t run_test_va_list_by_pointer(); extern size_t run_test_va_list_by_pointer_pointer(); +// Was the rust side compiled with f128 support? +extern const int RUST_HAS_F128; + int test_rust(size_t (*fn)(va_list), ...) { size_t ret = 0; va_list ap; @@ -40,9 +45,43 @@ 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 + // 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 + // construct f128::MAX. + union cvt128 { + struct { +#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + uint64_t hi, lo; +#else + uint64_t lo, hi; +#endif + } i; + f128 f; + }; + union cvt128 f128_max; + f128_max.i.hi = 0x7ffeffffffffffff; + f128_max.i.lo = 0xffffffffffffffff; + + if (RUST_HAS_F128) { + assert(test_rust(check_list_f128, (f128)-42.0, 0xAAAAAAAA, f128_max.f) == 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 9fa55c36e2441..1fb96ab7cfad7 100644 --- a/tests/ui/c-variadic/roundtrip.rs +++ b/tests/ui/c-variadic/roundtrip.rs @@ -5,7 +5,8 @@ c_variadic_va_arg_safe, c_variadic_int128, const_destruct, - const_raw_ptr_comparison + const_raw_ptr_comparison, + f128 )] #![allow(unused_features)] // c_variadic_int128 is only used on 64-bit targets. @@ -113,7 +114,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_feature = "vsx"), + 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 */ } } } }