1. RISC-V spec said NaN propagation should generate canonical NaN
Except when otherwise stated, if the result of a floating-point operation is NaN, it is the canonical NaN. The canonical NaN has a positive sign and all significand bits clear except the MSB, a.k.a. the quiet bit. For single-precision floating-point, this corresponds to the pattern 0x7fc00000.
2. IEEE 754 2018 standard recommends that operations on NaNs could propagate the input's NaN, but it's not required.
3. LLVM NaN propagation implementation follows IEEE rule which does not match RISC-V spec.
https://github.com/llvm/llvm-project/blob/main/llvm/lib/Analysis/InstructionSimplify.cpp#L4966-L4968
The simplest test is adding two constant floating values and one is NaN, and the result of -O0 and -O3 are different due to the NaN's fraction bit.
So software could use C++ isnan() to check NaN before comparing, it's why we could not use memcmp to compare two floating values, not only accuracy issue, but also non-portable.
BTW, I don't like spec giving a recommended rule...