If a program is built with software floating-point emulation, is it affected by hardware floating-point environment?

Hello everyone!

Some FPUs support changeable settings and status flags that affect (and are affected) by floating-point operations, known as floating-point environment. This includes rounding mode and floating-point exceptions, as well as "denormals-are-zero" and other settings supported by some hardware. Currently, behavior of Rust program running in non-default floating-point environment (such as non-default rounding mode, enabled "denormals-are-zero" mode or unmasking any floating-point exceptions) is undefined, and floating-point exception status flags may have meaningless value. I wonder, what about Rust programs compiled in software floating-point emulation mode (soft-float)? Can its behavior become undefined solely because of unexpected floating-point environment, and can it affect floating-point exceptions in an unexpected way (that may be important if, for example, hardware floating-point computations are done with inline assembly)?

I would assume that (for those platforms where soft-float compilation even exists), the equivalent properties should be true as well: If you were to somehow manage to modify any global state of the soft-float implementation being linked (if it even has any state; I don’t actually know) then that would be similarly UB unless the modifications were contained to within a single inline-assembly block. If you merely modify the FPU status (assuming the chip has an FPU, but you compiled your code in such a way that it doesn’t use the FPU but soft-float instead) then that should probably be harmless (but also arguably a weird setup).

That being said, I don’t actually know how realistic any access to the soft-float implementation even is in the first place, nor whether or not it features global state that could be messed with. Perhaps you know that better than me? Or maybe you meant something entirely different with your question? Also, for looking at concrete examples maybe, do you have some concrete hardware/architecture in mind?

Currently, running Rust code with non-default floating-point rounding mode results in undefined behavior, and floating-point exception status flags may have meaningless value (a floating-point operation may fail without raising a floating-point exception if it's reordered, optimized out or evaluated at compile-time and replaced with a constant, and a floating-point exception may be raised without any observable reason because a compiler may reorder a failing operation and floating-point exception checking operation). I wonder, does the same apply to Rust programs built in soft-float mode?

Let me re-phrase my question for more relevant information: what exactly do you mean by “running Rust code with non-default floating-point rounding mode” for a program that is “built in soft-float mode”?

Feel free to choose a specific architecture with a soft-float target-triple for the “built in soft-float mode”, and explain specifically how you would go about setting a “non-default floating-point rounding mode” in that specific context.

For example, if it uses inline assembly to interact with FPU.

I think the point is that an architecture with a soft-float implementation likely doesn’t have a physical FPU (or at least the compiler doesn’t use it), and I’d assume that the “logical FPU” software (if you think of it that way) presumably doesn’t have settings that can be changed by assembly.

As far as I understand, a Rust program can be built in "soft-float" mode even on an architecture that has a hardware FPU (for example, x86-64). If it is indeed true, then some parts of a program on such architecture can interact with the FPU using inline assembly. I wonder, can such interaction cause undefined behavior in Rust program? As far as I understand, "soft-float" program should not attempt to interact with FPU outside the inline assembly sections, therefore (the Rust part of) it shouldn't affect or be affected by floating-point environment, but I wonder if it is indeed true.

There’s a deprecated (and in the process of being fully removed) flag that never really worked in any more general manner for codegen -C soft-float.

The way to actually choose soft-float is to compile for one of the targets (see this chapter) which has a …hf and non-…hf variant, e.g. armv7-unknown-linux-gnueabi vs armv7-unknown-linux-gnueabihf, where the “hf” stands for “hard float” and the other one thus uses soft float :slight_smile:

Thank you, now I understand. Does that mean that for x86-64, "soft-float" is not currently supported?

Yeah, I believe so; as far as I understand, x86-64 does always come with hardware support for floating-point operations.

From x86-64 - Wikipedia:

Floating-point arithmetic is supported through mandatory SSE2 instructions in 64-bit mode.

Sure, I only wonder how floating-point environment expectations interact with software floating-point emulation. As far as I understand, the reason why floating-point environment is not supported is that it causes floating-point operations to be impure functions by causing them to have a "hidden input" (rounding mode) and "hidden output" (floating-point exceptions), which would inhibit optimizations and complicate the floating-point math even further. As far as I understand, currently the only supported way to operate in non-default floating-point environment is by using inline assembly.

Of course you can always do “manual” usage of software-based floating-point operations by using some crate/library that implements floating-point operations in software.

Also… looking at https://stackoverflow.com/questions/3321468/whats-the-difference-between-hard-and-soft-floating-point-numbers I must concur I don’t know where exactly in the picture of all possible approaches to floating-point these soft-float and -…hf target triples actually operate. If you’re running code compiled for soft-float targets, I’m not sure if the “soft” implementation cannot - if hardware support is detected at run-time - fall back to become a wrapper around the actual hardware operations.
(In which case my claim that

If you merely modify the FPU status (assuming the chip has an FPU, but you compiled your code in such a way that it doesn’t use the FPU but soft-float instead) then that should probably be harmless

might actually be wrong!) I don’t know if LLVM inserts the floating-point support for you or it’s something provided by an OS – maybe the answer even depends on whether or not you do compile for “bare metal” or do use an OS.

+soft-float is also a target feature, and the (tier 2) x86_64-unknown-none target apparently uses that.

That said, there seem to be a bunch of issues related to soft-float. I don't know the answer to the OP's question, but it seems like a not-terribly-robust area at the moment (as an outside observer).