Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions compiler/rustc_codegen_llvm/src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}
}

Expand Down
49 changes: 49 additions & 0 deletions library/core/src/ffi/va_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Comment thread
folkertdev marked this conversation as resolved.
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")),
Comment thread
tgross35 marked this conversation as resolved.
// 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 {}
Comment thread
folkertdev marked this conversation as resolved.
}
_ => { /* unsupported */ }
}
Comment thread
tgross35 marked this conversation as resolved.

#[unstable(feature = "c_variadic_va_arg_safe", issue = "162911", implied_by = "c_variadic")]
unsafe impl<T> VaArgSafe for *mut T {}
#[unstable(feature = "c_variadic_va_arg_safe", issue = "162911", implied_by = "c_variadic")]
Expand Down
55 changes: 54 additions & 1 deletion tests/run-make/c-link-to-rust-va-list-fn/checkrust.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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;
Comment on lines +126 to +127

@folkertdev folkertdev Sep 17, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed on powerpc64 where (at least my) GCC does have f128 support but our baseline does not.

View changes since the review


#[unsafe(no_mangle)]
pub unsafe extern "C" fn check_list_f128(mut ap: VaList) -> usize {
continue_if!(ap.next_arg::<f128>() == -42.0);
// use a 32-bit value here to test the alignment logic.
continue_if!(ap.next_arg::<c_int>() == 0xAAAA_AAAAu32.cast_signed());
continue_if!(ap.next_arg::<f128>() == 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::<c_int>() == 42);
Expand Down
39 changes: 39 additions & 0 deletions tests/run-make/c-link-to-rust-va-list-fn/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

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, ...);
Expand All @@ -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;
Expand All @@ -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

@tgross35 tgross35 Sep 17, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to load a literal for MAX since INFINITY only exercises the top 16 bits. E.g.:

    union cvt128 {
        struct {
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
            uint64_t lo, hi;
#else
            uint64_t hi, lo;
#endif
        } i;
        _Float128 f;
    };
    union cvt128 f128_max;
    f128_max.i.hi = 0x7ffeffffffffffff;
    f128_max.i.lo = 0xffffffffffffffff;
    assert(test_rust(check_list_f128, (f128)-42.0, 0xAAAAAAAA, f128_max.f) == 0);

Kind of annoying that llvm/llvm-project#97335 is still open and we can't just use __FLT128_MAX__.

View changes since the review


assert(check_varargs_0(0, 42, "Hello, World!") == 0);

assert(check_varargs_1(0, 3.14, 12l, 'A', 0x1LL) == 0);
Expand Down
36 changes: 34 additions & 2 deletions tests/ui/c-variadic/roundtrip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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 */ }
}
}
}
Loading