[754] Fold FP "Op SNaN" to QNaN?

I’m looking at lib/Analysis/InstructionSimplify.cpp where the function propagateNaN() has a comment asking if it should quiet a signaling NaN.

If I understand the IEEE 754-2019 standard correctly: an SNaN shall be converted to a QNaN whenever an “operation” is done. The standard doesn’t say, or I couldn’t find it, exactly when that operation must be done. Which implies that the floating-point operation could be done by the compiler. In which case folding an instruction that has an SNaN operand should result in a QNaN.

Is my reading of the standard correct? And should we make this change to InstructionSimplify.cpp:propagateNaN() and perhaps more generally in LLVM?

I’m looking at lib/Analysis/InstructionSimplify.cpp where the function propagateNaN() has a comment asking if it should quiet a signaling NaN.

If I understand the IEEE 754-2019 standard correctly: an SNaN shall be converted to a QNaN whenever an “operation” is done. The standard doesn’t say, or I couldn’t find it, exactly when that operation must be done. Which implies that the floating-point operation could be done by the compiler. In which case folding an instruction that has an SNaN operand should result in a QNaN.

That should work just fine, but only when you know that FP exceptions flags are ignored, since the signalling NaN causes the operation to generate the Invalid exception, and a quiet NaN doesn’t generally cause exceptions.

Jacob Lifshay

Yes, true.

This came up in the context of adding support for the constrained FP intrinsics. These intrinsics all include a metadata argument for specifying how important FP traps are. If traps are “strict” then we can’t fold away any traps. The default FP environment would use “ignore” where there are no traps and folding is fine. I’m working on the “maytrap” case where we can eliminate traps but cannot introduce any new ones. More on constrained intrinsics: https://llvm.org/docs/LangRef.html#constrained-floating-point-intrinsics

To be clear, are you saying that my interpretation of 754 is correct?

754 doesn’t have anything to say about this, because “maytrap” does not bind 754’s semantics. However, the documentation for maytrap is pretty clear about this: "For example, exceptions may be potentially hidden by constant folding.” I.e. it’s fine for constant propagation to convert sNaN to qNaN in this mode.

In general, the viewpoint of 754 is that sNaN should never be lost without signaling (hence trapping at runtime if traps are unmasked). So this transformation would not be licensed under strict.

– Steve

Yes, true.

This came up in the context of adding support for the constrained FP intrinsics. These intrinsics all include a metadata argument for specifying how important FP traps are. If traps are “strict” then we can’t fold away any traps. The default FP environment would use “ignore” where there are no traps and folding is fine. I’m working on the “maytrap” case where we can eliminate traps but cannot introduce any new ones. More on constrained intrinsics: https://llvm.org/docs/LangRef.html#constrained-floating-point-intrinsics

Afaict converting SNaN inputs to QNaN should be fine for maytrap, excluding, of course, things like fneg and copysign that just manipulate bits. If LLVM wants to guarantee correct NaN bits in the output, be aware that some platforms have special cases for SNaN → QNaN: RISC-V always produces the canonical NaN, MIPS pre-2008-mode has the quiet bit inverted from the usual, so a SNaN mantissa with a 1 MSB and the rest zeros needs to adjust the mantissa to avoid generating an infinity instead of a QNaN.

To be clear, are you saying that my interpretation of 754 is correct?

Yes, afaict. I’d suggest waiting for someone else to also check this since we may be missing some special case…

Jacob