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
44 changes: 44 additions & 0 deletions library/core/src/ffi/va_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(

@beetrees beetrees Sep 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Probably worth adding a comment here telling future developers to keep this cfg in sync with the cfgs in the tests.

View changes since the review

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")),

@folkertdev folkertdev Aug 21, 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.

clang 23 runs into an alignment bug llvm/llvm-project#217747, but GCC can handle this.

View changes since the review

// 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 {}
Comment thread
folkertdev marked this conversation as resolved.
}
_ => { /* unsupported */ }
}
Comment on lines +428 to +460

@tgross35 tgross35 Aug 25, 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.

This is pretty sparse on background information and I'm not positive what I should be checking against, could you add a comment similar to what __int128 has?

View changes since the review

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 one is definitely less principled. Looking into it, __float128 is not a good heuristic based on https://gcc.gnu.org/onlinedocs/gcc-14.3.0/gcc.pdf:

__float128 is available on i386, x86 64, IA-64, LoongArch and hppa HP-UX, as well
as on PowerPC GNU/Linux targets that enable the vector scalar (VSX) instruction set.

Something like defined(__LDBL_MANT_DIG__) && __LDBL_MANT_DIG__ == 113 is also imperfect.

What I'm going for is "anywhere c_longdouble is IEEE f128, and whatever else works".

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.

I added some comments to the va_list.rs definition.


#[stable(feature = "c_variadic", since = "1.99.0")]
unsafe impl<T> VaArgSafe for *mut T {}
#[stable(feature = "c_variadic", since = "1.99.0")]
Expand Down
43 changes: 42 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,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",
Comment thread
folkertdev marked this conversation as resolved.
target_arch = "wasm32",
target_arch = "wasm64",
),
),
) => {
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::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::<c_int>() == 42);
Expand Down
18 changes: 18 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 Down Expand Up @@ -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);
Expand Down
35 changes: 33 additions & 2 deletions tests/ui/c-variadic/roundtrip.rs
Original file line number Diff line number Diff line change
@@ -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::*;
Expand Down Expand Up @@ -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 */ }
}
}
}
Loading