Skip to content

Add nan2008 target feature for MIPS#159526

Open
fo40225 wants to merge 1 commit into
rust-lang:mainfrom
fo40225:fix_mips_nan2008
Open

Add nan2008 target feature for MIPS#159526
fo40225 wants to merge 1 commit into
rust-lang:mainfrom
fo40225:fix_mips_nan2008

Conversation

@fo40225

@fo40225 fo40225 commented Jul 18, 2026

Copy link
Copy Markdown

Adds nan2008 to the allowed target features for MIPS, gated behind theexisting unstable mips_target_feature gate (tracking issue #150253), matching LLVM's nan2008 subtarget feature (IEEE 754-2008 NaN encoding).

Previously, -Ctarget-feature=+nan2008 produced warning: unknown feature specified for '-Ctarget-feature': 'nan2008' and the feature was silently ignored. Since the prebuilt std for MIPS targets is compiled with legacy NaN encoding, linking against libraries built with GCC's -mnan=2008 failed with ld: failed to merge target specific data (mixing -mnan=legacy and -mnan=2008 objects).

With this change, users can enable the feature (together with -Zbuild-std to rebuild std with matching NaN encoding) to link such libraries.

The feature name matches LLVM's directly, so no to_llvm_features mapping is needed. The tests/ui/check-cfg/target_feature.rs expected output was re-blessed.

Fixes #98999

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jul 18, 2026
@rustbot

rustbot commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the pull request, and welcome! The Rust Project is excited to review your changes, and you should hear from @nnethercote (or someone else) some time within the next two weeks.

Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (S-waiting-on-review and S-waiting-on-author) stays updated, invoking these commands when appropriate:

  • @rustbot author: the review is finished, PR author should check the comments and take action accordingly
  • @rustbot review: the author is ready for a review, this PR will be queued again in the reviewer's queue
Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler
  • compiler expanded to 74 candidates
  • Random selection from 16 candidates

@rustbot

This comment has been minimized.

Allow enabling the LLVM `nan2008` subtarget feature (IEEE 754-2008 NaN
encoding) via `-Ctarget-feature` and `#[target_feature]`, gated behind
the existing unstable `mips_target_feature` feature gate. This makes it
possible to link against MIPS libraries built with GCC's `-mnan=2008`.
@fo40225
fo40225 force-pushed the fix_mips_nan2008 branch from 0edd123 to 180dde2 Compare July 18, 2026 15:33
@folkertdev

Copy link
Copy Markdown
Contributor

Here is some further context from Clang

https://github.com/llvm/llvm-project/blob/acd3fcfdd6d065f914eadbe2036e703c056b4ccc/clang/lib/AST/ByteCode/InterpBuiltin.cpp#L465-L485

Prior to IEEE 754-2008, architectures were allowed to choose whether
the first bit of their significand was set for qNaN or sNaN. MIPS chose
a different encoding to what became a standard in 2008, and for pre-
2008 revisions, MIPS interpreted sNaN-2008 as qNan and qNaN-2008 as
sNaN. This is now known as "legacy NaN" encoding.

So, more IEEE-compliant sounds good? But also this sounds like a target modifier maybe?

cc @RalfJung

@Gelbpunkt

Gelbpunkt commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Indeed, I believe this should be a target modifier. It is ABI breaking.


Some background:

NaNs generated by old (read: most) MIPS processors have the signaling/quiet bit set to zero if the NaN is quiet and non-zero if the NaN is signaling. The is contrary to most other architectures, which set the signaling/quiet bit to non-zero if the NaN is quiet, and to zero if the NaN is signaling. In IEEE754-2008, the behavior of x86 and most other architectures was made the recommended (not required!) encoding of the signaling/quiet state.

MIPS64r3 added optional support for IEE754-2008 NaN signaling/quiet state encoding. It was made required in MIPS64r5. See https://web.archive.org/web/20170928005646/http://cdn2.imgtec.com/documentation/MD00083-2B-MIPS64INT-AFP-05.04.pdf, page 79 and page 84.

Of the two relevant families of MIPS64 processors I'm aware (Cavium Octeon and Loongson's Godson), the later revisions of Godson implement this, the Octeon III is MIPS64r2 and does not.

If the 2008 encoding is used, EF_MIPS_NAN2008 is set in the ELF header. The Linux kernel supports emulating either encoding in software via the ieee754=emulated cmdline parameter.

I'm not aware of many distros that actively support MIPS64, but for reference, Debian's MIPS64 targeted MIPS64r2 for Octeon III and Godson and did use the legacy encoding. I believe AOSC which only targets Godson uses 2008 encoding.

So yeah, it would be good to have a target modifier for this, but a target feature would allow mixing code with different NaN signaling/quiet encodings, which I don't think should be allowed.

@xry111

xry111 commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

I'm not aware of many distros that actively support MIPS64, but for reference, Debian's MIPS64 targeted MIPS64r2 for Octeon III and Godson and did use the legacy encoding. I believe AOSC which only targets Godson uses 2008 encoding.

AOSC uses the legacy encoding with ieee754=relaxed so some math operations actually behave erratically on Loongson 3A4000 (which uses 2008 encoding). As AOSC developers, I and @chenx97 have customly built systems with 2008 encoding on 3A4000 running but there's no consensus to switch the encoding for AOSC (AFAIK some users are running AOSC on Loongson 3A3000 which still uses legacy encoding).

So yeah, it would be good to have a target modifier for this, but a target feature would allow mixing code with different NaN signaling/quiet encodings, which I don't think should be allowed.

The linker (at least BFD, unsure about LLD) will error out if the input object files use different NaN encodings.

@Gelbpunkt

Copy link
Copy Markdown
Contributor

The linker (at least BFD, unsure about LLD) will error out if the input object files use different NaN encodings.

Yes, but Rust lets you toggle target features for individual functions, which would observe different NaN behavior. I couldn't come up with a very good example since the standard library actually explicitly doesn't make a lot of guarantees about signaling/quiet NaNs, so here's a bad attempt:

fn compiled_with_default_nans() {
    let some_number = some_function_compiled_with_2008_nans();
    // No nan2008, this is no longer a quiet NaN from the
    // perspective of this code
}

#[target_feature(enable = "nan2008")]
fn some_function_compiled_with_2008_nans() -> f64 {
    // As per the docs, this is *guaranteed* to be a quiet NaN
    // on conformant platforms (which we are in this function)
    std::hint::black_box(f64::NAN)
}

@Gelbpunkt

Gelbpunkt commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Also, it would be good to have support for nan2008 in is_mips64_target_feature_detected!, but I'm not sure how that plays with target modifiers since we wouldn't want that to be substituted at compile time ever.

@RalfJung

RalfJung commented Jul 18, 2026

Copy link
Copy Markdown
Member

we wouldn't want that to be substituted at compile time ever

Why not? The assumption for target features is that if they are enabled at compile time, then it is UB to run the program in an environment where the feature is not available, and therefore the macro is substituted with true at compile time. Only when a feature is disabled at compile time does the macro do anything.

I have no idea what nan2008 target feature detection should even do? I assume it's UB to run a program compiled with 754-2008 NaNs on a CPU that doesn't have them.


Okay, so... so far all target modifiers are hard-coded rustc flags (-Zflag). We probably don't want a new -Z flag for this one though, we'll have to figure out a more scalable way to expose LLVM target features that "don't compose" and that we hence want to treat as target modifiers.

We have an accepted MCP for a general "LLVM target features" target modifier: rust-lang/compiler-team#994. If that was implemented, -Zllvm-target-feature=nan2008 would be one way to enable this feature. We'd consider this -Z flag to be "unsafe" though since it directly exposes all LLVM target features and some of them apparently can wrack havoc even if the entire binary uses them consistently. These don't have cfg(...) support nor runtime detection support.

If we want anything beyond that, a target modifier equivalent of "Rust target features" with sanity checks and cfg support and all that, that's a new compiler feature and a new language feature, so that'll need some exploration of the design space and discussion with relevant teams before we'd want to land anything.

So I'm afraid this is going to be neither fast nor trivial. Adding this flag in compiler/rustc_target/src/target_features.rs breaks fundamental invariants and promises of the Rust compiler; that file is for well-behaved compositional target features which this one is not. If you want to help push this forward, I think it'd be good to start by implementing rust-lang/compiler-team#994. That won't give us cfg support but it'll be a good baseline for a better-integrated feature.

@Gelbpunkt

Gelbpunkt commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

we wouldn't want that to be substituted at compile time ever

Why not? The assumption for target features is that if they are enabled at compile time, then it is UB to run the program in an environment where the feature is not available, and therefore the macro is substituted with true at compile time. Only when a feature is disabled at compile time does the macro do anything.

I have no idea what nan2008 target feature detection should even do? I assume it's UB to run a program compiled with 754-2008 NaNs on a CPU that doesn't have them.

So the issue here is that from a distribution perspective, it is desirable to support as many CPUs as possible. To achieve this, what is done these days is that everything is compiled under the assumption of legacy NaNs and to avoid a performance hit, ieee754=relaxed is set on the kernel cmdline so the kernel ignores the ELF header flag and runs the legacy NaN binaries on the FPU without emulation, no matter whether the current FPU is using legacy or IEEE754-2008 NaNs.

Most code will work just fine with this. Code that relies on specific quiet/signalling encoding does not. So suppose I would like to write a function that always returns a quiet NaN: The program is always compiled for legacy NaNs, even though at runtime it might run on a FPU which uses IEEE754-2008 NaN encoding.

It would be no issue if it were possible to detect at runtime which encoding the FPU is using and then conditionally change the bit around as needed.

// Note: I have no idea what f64::NAN behaves like on legacy NAN encoding on the moment, this
// is under the assumption that the same bits are returned no matter the platform

let quiet_nan = if is_mips64_target_feature_detected!("nan2008") {
    // This is only a quiet NaN on processors that use IEEE754-2008 encoding
    f64::NAN
} else {
    // When we detect legacy encoding at runtime, we can clear the signaling/quiet bit
    f64::from_bits(f64::NAN.to_bits() & !(1 << 51))
}

I'd love for this not to be necessary, but not having any kind of runtime detection at all means programs will misbehave in practice with ieee754=relaxed with no way for them to behave properly no matter what they're actually running on.

@folkertdev

Copy link
Copy Markdown
Contributor

Does that runtime detection have to be built into the language though? is_mips64_feature_detected is just a macro, you can do manually what it can do.

@Gelbpunkt

Copy link
Copy Markdown
Contributor

Why wouldn't it be? I think this is a very desirable thing to check for and the macro already exists and provides all the nice extras like caching

@RalfJung

RalfJung commented Jul 18, 2026

Copy link
Copy Markdown
Member

The program is always compiled for legacy NaNs, even though at runtime it might run on a FPU which uses IEEE754-2008 NaN encoding.

That sounds like plain UB to me, similar to how it's UB to run a program in a non-default rounding mode.

So far the way you explained it I can't see any kind of coherent semantic that we could give to this, so I don't see a good way to provide any kind of Rust-level support. We definitely should not pretend that there's reasonable behavior here (e.g. by making normal-looking APIs work) when actually it's an ill-behaved mess.

But one point that's not quite clear to me -- nan2008 as described here seems like an LLVM/linker-level flag But you seem to say there's also a CPU-level flag that can be queried like normal target features? But unlike normal target features it doesn't say whether a certain instruction is available, it indicates a piece of actual mutable state reflecting the current NaN mode? (In particular, for normal target features, false is always a safe answer. Anything where that is not true is not a normal target feature and should not be treated as such.) That is extremely cursed and not at all how normal target features behave and I don't think we should try to shoehorn it into the same interface. We'd just get into trouble down the road when the fact that this is not actually a normal target feature leaks in various ways.

@RalfJung

RalfJung commented Jul 18, 2026

Copy link
Copy Markdown
Member

FWIW, from an LLVM perspective, we always assume that NaNs use the 754-2008 encoding. So technically speaking it is UB to ever run a program in legacy mode. I don't know if that can be used to cause actual unsoundness but I would assume so. It is therefore kind of pointless to try and do anything systematic for legacy mode, and certainly we shouldn't add any library API surface for this as that could be misinterpreted as things actually being well-behaved.

What does the LLVM nan2008 target feature actually do? It doesn't affect the emitted instructions, right? Does it just set a bit in the object file which asks the loader to please configure the CPU into nan2008 mode before starting the program? What happens when such a program is loaded on a CPU that does not support nan2008 mode?
If it's really just such a bit then we should very clearly distinguish between that bit in the object file, and the CPU-level "does this CPU support nan2008 mode" bit, as those are two very different bits. Above it seems like both have been conflated when is_mips64_target_feature_detected was brought up, and that is quite confusing. Or maybe I misunderstood what is happening.

@Gelbpunkt

Gelbpunkt commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

But you seem to say there's also a CPU-level flag that can be queried like normal target features? But unlike normal target features it doesn't say whether a certain instruction is available, it indicates a piece of actual mutable state reflecting the current NaN mode?

Yes. There is the Floating Point Implementation Register (FIR), in which bit 23 Has2008 indicates whether at least one of Abs2008 and NAN2008 is set. It's read-only.

If Has2008 is 1, one can check the Floating Point Control and Status Register (FCSR) bit 18 (NAN2008). Value 1 indicates that IEEE754-2008 NaN encoding is used by the hardware, 0 means legacy NaN encoding is used.

@RalfJung

Copy link
Copy Markdown
Member

Thanks. And what about this part?

What does the LLVM nan2008 target feature actually do? It doesn't affect the emitted instructions, right? Does it just set a bit in the object file which asks the loader to please configure the CPU into nan2008 mode before starting the program? What happens when such a program is loaded on a CPU that does not support nan2008 mode?

@workingjubilee

Copy link
Copy Markdown
Member

I am indifferent to whether we make this a target feature or target modifier. I can see the arguments for either. But if we make it detectable, then it should in fact be valid to substitute it at compile time if we have been guaranteed it statically, just like it has been for other features.

I do understand why the beach-house residents of this particular lake of fire like their homes simultaneously smoky yet well-lit. I just don't think we need to send them remittances of more fuel by altering our semantics of how target features work.

For instance, we would have to also make cfg(target_feature) not work for this.

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

Labels

S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

mips -nan2008 feature miss

8 participants