Skip to content

transpile: Implement __builtin_nans* and honor NaN payloads - #2049

Open
shashikanth-a wants to merge 2 commits into
immunant:masterfrom
shashikanth-a:transpile-builtin-nans
Open

shashikanth-a wants to merge 2 commits into
immunant:masterfrom
shashikanth-a:transpile-builtin-nans

Conversation

@shashikanth-a

Copy link
Copy Markdown

Fixes #1800.

__builtin_nans{,f,l} were unimplemented, __builtin_nan* ignored their payload argument, and the __float128 variants (__builtin_{nan,nans,inf,huge_val}f128) were unimplemented too.

All NaN builtins now translate to the bits clang's constant folding produces:

  • The payload is parsed like LLVM's StringRef::getAsInteger with an auto-detected radix ("", decimal, leading-0 octal, 0x, 0b, 0o), and the NaN is built like APFloat::makeNaN. A signaling NaN clears the quiet bit, and sets the next bit down if the payload is then empty.
  • Where clang doesn't fold (a non-literal or malformed payload), it emits a libm call. The quiet variants become the default NaN with a warning, keeping the argument's side effects. libm has no nans, so an unfoldable signaling NaN is an error.
  • Edition 2024 emits f64::from_bits(..). Older editions emit transmute, since from_bits isn't const on their pinned toolchain (1.70) and these can appear in static initializers. The default quiet NaN still becomes f32::NAN/f64::NAN.
  • long double and __float128 use transmute::<u128, f128::f128>, as the f128 crate has no public bit-pattern constructor.

One behaviour change: __builtin_nanl("") used to be f128::f128::NAN, whose payload (all ones) differs from clang's. It now gets clang's bits.

The second commit adds the __float128 builtins separately, in case you'd rather review or take it on its own.

Testing

  • New snapshot tests nan.c and f128_builtins.c (translated for x86_64-unknown-linux-gnu), plus unit tests for payload parsing and NaN bits.
  • Checked against clang outside the test suite: for 25 payload strings with each of nan, nans, nanf and nansf, I compiled and ran both the C and the translated Rust (editions 2021 and 2024) and compared the bits. The f128 variants were compared against clang's x86_64 constants, including payloads wider than 128 bits.
  • Ran the f128 output on x86_64 Linux against the real f128 crate, and compared it with GCC.

Limitations

  • long double is translated as binary128, so on x86 the bits differ from x87's 80-bit format. This is the existing mapping.
  • A payload only known at runtime gets the default NaN.
  • _Float16 isn't supported by the transpiler at all, so __builtin_nanf16 isn't handled here.

`__builtin_nans`, `__builtin_nansf` and `__builtin_nansl` were
unimplemented, and `__builtin_nan*` ignored their payload argument.

Translate both to the bits clang's constant folding produces: the
payload string is parsed like LLVM's `StringRef::getAsInteger` with an
auto-detected radix, and the NaN is built like `APFloat::makeNaN`.
`long double` uses binary128, matching its translation to `f128::f128`.

Where clang doesn't fold (a non-literal or malformed payload), it emits
a libm call instead. For the quiet variants, emit the default NaN with a
warning, keeping the argument's side effects. libm has no `nans`, so an
unfoldable signaling NaN is an error.

Edition 2024 uses `f32::from_bits`/`f64::from_bits`. Older editions use
`transmute`, as `from_bits` isn't `const` on their pinned toolchain and
these can appear in `static` initializers.

Fixes immunant#1800.
`__builtin_nanf128`, `__builtin_nansf128`, `__builtin_inff128` and
`__builtin_huge_valf128` were unimplemented. `__float128` is translated
to `f128::f128` like `long double`, so handle them like the `l` variants.

/// The IEEE format of the result of a `__builtin_nan*` builtin.
#[derive(Copy, Clone)]
enum NanFloat {

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.

This seems superfluous to the FloatingKind enum?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

C2Rust fails to translate __builtin_nans with “Unimplemented builtin __builtin_nans”

2 participants