Hi all,

One of the purposes of `llvm::isnan`

was to help preserve the check made by `isnan`

if fast-math mode is

specified (https://reviews.llvm.org/D104854). I’d like to describe reason for that and propose to use the behavior

implemented in that patch.

The option `-ffast-math`

is often used when performance is important, as it allows a compiler to generate faster code.This option itself is a collection of different optimization techniques, each having its own option. For this topic only the

option `-ffinite-math-only`

is of interest. With it the compiler treats floating point numbers as mathematical real numbers,

so transformations like `0 * x -> 0`

become valid.

In clang documentation (https://clang.llvm.org/docs/UsersManual.html#cmdoption-ffast-math) this option is described as:

“Allow floating-point optimizations that assume arguments and results are not NaNs or ±Inf.”

GCC documentation (https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html) is a bit more concrete:

“Allow optimizations for floating-point arithmetic that assume that arguments and results are not NaNs or ±Infs.”

**What is the issue?**

C standard defines a macro `isnan`

, which can be mapped to an intrinsic function provided by the compiler. For both

clang and gcc it is `__builtin_isnan`

. How should this function behave if `-ffinite-math-only`

is specified? Should it make a

real check or the compiler can assume that it always returns false?

GCC optimizes out `isnan`

. It follows from the viewpoint that (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=50724#c1):

“With -ffinite-math-only you are telling that there are no NaNs and thus GCC optimizes isnan (x) to 0.”

Such treatment of `-ffinite-math-only`

has sufficient drawbacks. In particular it makes it impossible to check validity of

data: a user cannot write

assert(!isnan(x));

because the compiler replaces the actual function call with its expected value. There are many complaints in GCC bug

tracker (for instance https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84949 or https://gcc.gnu.org/bugzilla/show_bug.cgi?id=50724)

as well as in forums (https://stackoverflow.com/questions/47703436/isnan-does-not-work-correctly-with-ofast-flags or

https://stackoverflow.com/questions/22931147/stdisinf-does-not-work-with-ffast-math-how-to-check-for-infinity). Proposed

solutions are using integer operations to make the check, to turn off `-ffinite-math-only`

in some parts of the code or to

ensure that libc function is called. It clearly demonstrates that `isnan`

in this case is useless, but users need its functionality

and do not have a proper tool to make required checks. The similar direction was criticized in llvm as well (https://reviews.llvm.org/D18513#387418).

**Why imposing restrictions on floating types is bad?**

If `-ffinite-math-only`

modifies properties of `double`

type, several issues arise, for instance:

- What should return
`std::numeric_limits<double>::has_quiet_NaN()`

? - What body should have this function if it is used in a program where some functions are compiled with
`fast-math`

and some without? - Should inlining of a function compiled with
`fast-math`

to a function compiled without it be prohibited in inliner? - Should
`std::isnan(std::numeric_limits<float>::quiet_NaN())`

be true?

If the type `double`

cannot have NaN value, it means that `double`

and `double`

under `-ffinite-math-only`

are different types

(https://gcc.gnu.org/pipermail/gcc-patches/2020-April/544641.html). Such a way can solve these problems but it is so expensive

that hardly it has a chance to be realized.

**The solution**

Instead of modifying properties of floating point types, the effect of `-ffinite-math-only`

can be expressed as a restriction on

operation usage. Actually clang and gcc documentation already follows this way. Fast-math flags in llvm IR also are attributes

of instructions. The only question is whether `isnan`

and similar functions are floating-point arithmetic.

From a practical viewpoint, treating non-computational functions as arithmetic does not add any advantage. If a code extensively

uses `isnan`

(so could profit by their removal), it is likely it is not suitable for -ffinite-math-only. This interpretation however creates

the problems described above. So it is profitable to consider `isnan`

and similar functions as non-arithmetical.

**Why is it safe to leave isnan?**

The probable concern of this solution is deviation from gcc behavior. There are several reasons why this is not an issue.

- -ffinite-math-only is an optimization option. A correct program compiled with -ffinite-math-only and without it should behave

identically, if conditions for using -ffinite-math-only are fulfilled. So making the check cannot break functionality. -
`isnan`

is implemented by libc, which can map it to a compiler builtin or use its own implementation, depending on

configuration options.`isnan`

implemented in libc obviously always does the real check. - ICC and MSVC preserve
`isnan`

in fast-math mode.

The proposal is to not consider `isnan`

and other such functions as arithmetic operations and do not optimize them out

just because -ffinite-math-only is specified. Of course, there are cases when `isnan`

may be optimized out, for instance,

`isnan(a + b)`

may be optimized if -ffinite-math-only is in effect due to the assumption (result of arithmetic operation is not NaN).

What are your opinions?