Let me describe a real life example.
There is a realtime program that processes float values from a huge array. Calculations do not produce NaNs and do not expect them. Using -ffinite-math-only substantially speeds up the program, so it is highly desirable to use it. The problem is that the array contains NaNs, they mark elements that should not be processed.
An obvious solution is to check an element for NaN, and if it is not, process it. Now there is no clean way to do so. Only workarounds, like using integer arithmetics. The function ‘isnan’ became useless. And there are many cases when users complain of this optimization.
I personally would separate the “pre-processing” of the input in a compilation unit that isn’t compiled with -ffinite-math-only and isolate the perf-critical routines to be compiled with this flag if needed (I’d also like a sanitizer to have a build mode that validate that no NaNs are ever seen in this routines).
It could be a workaround. GCC supports ‘#pragma GCC optimize’, which could be used to turn on and off -ffinite-math-only. In clang this pragma does not work, so only separate translation units with subsequent linking, which is not possible in some cases, like in ML kernels.
In general, Krzysztof’s reasoning in this thread makes sense to me, in particular in terms of being consistent with how we treat isnan(x) vs isnan(x+0) for example.
The key point here is what guarantees the user provides to the compiler when they specify -ffinite-math-only. If “NaN never cannot be seen” then indeed, isnan may be optimized out. If “NaNs do not occur in arithmetic operations”, then ‘isnan’ must be kept unless we know for sure that its argument cannot be a NaN. The choice should be based on practical needs IMHO. The second approach is more flexible and enables more use cases.