[llvm-dev] [cfe-dev] Should isnan be optimized out in fast-math mode?

Cranmer, Joshua via llvm-dev llvm-dev at lists.llvm.org
Thu Sep 9 13:38:33 PDT 2021


(Speaking only for myself here, and mostly as someone who doesn’t typically write floating-point-heavy code).

The root issue we have here is that, as with many compiler extensions, fast-math flags ends up creating a vaguely-defined variant of the C specification governed by the “obvious” semantics, and is the case with “obvious” semantics, there are several different “obvious” results.

Given the standard C taste for undefined behavior, it would seem to me that the most natural definition of -ffinite-math-only would be to say that any operation that produces NaN or infinity results is undefined behavior, or produces a poison value using LLVM’s somewhat tighter definition here [1]. This notably doesn’t give a clear answer on what to do with floating-point operations that don’t produce floating-point results (e.g., casts, comparison operators), and the volume of discussions on this point is I think indicative that there are multiple reasonable options here. Personally, I find the extension of the UB to cases that consume but do not produce floating-point values to be the most natural option.

It’s also the case that many users don’t like undefined behavior as a concept, in large part because it can be very difficult to work around in a few cases where it is desired to explicitly override the undefined behavior. For some of the more basic integer UB, clang already provides builtin overflow checking macros to handle the I-want-to-check-if-it-overflowed-without-UB case, for example. And if fast math flags are to create UB, then similar functionality to override the floating-point UB ought to be provided. Already, C provides a mechanism to twiddle floating-point behavior on a per-scope basis (e.g., #pragma STDC FENV_ACCESS, CX_LIMITED_RANGE, FP_CONTRACT). LLVM already supports these flags on a per-instruction basis, so it really shouldn’t be very difficult to have Clang support pragmas to twiddle fast-math flags like the existing C pragmas. And in this model, the -ffast-math and related flags are doing nothing more than setting the default values of these pragmas.

In that vein, I can imagine a user writing a program that would look something like this:

int some_hard_math_kernel(float *inputs, float *outputs, int N) {
  {
    #pragma clang fast_math off
    for (int i = 0; i < N; i++) {
       if (isinf(inputs[i]) || isnan(inputs[i]))
         return ILLEGAL_ARGUMENT;
    }
  }
  #pragma clang fast_math on
  // Do fancy math here…
  // and if we see isnan(x) here, even if it’s in a library routine [compiled with -ffast-math],
  // or maybe implied by some operation the compiler understands [say, complex multiplication]
  // it is optimized to false.
  return SUCCESS;
}

I can clearly see use cases where the programmer might wish to have the optimizer eliminate any isnan calls that are generated when -ffast-math is used, but like other UB, I think it is extremely beneficial to provide some way to explicitly opt-out of UB on a case-by-case basis.

I would even go so far as to suggest that maybe the C standards committee should discuss how to handle at least the nsz/nnan/ninf parts of fast-math flags, given that very similar concepts seem to exist in all of the major C/C++ compilers.

[1] I fully expect any user who is knowledgeable about poison in LLVM—which admittedly is a fairly expert user—would expect poison to kick in most of the time C or C++ provides for undefined behavior, and potentially to rely on that expectation.

From: llvm-dev <llvm-dev-bounces at lists.llvm.org> On Behalf Of Mehdi AMINI via llvm-dev
Sent: Thursday, September 9, 2021 14:29
To: Serge Pavlov <sepavloff at gmail.com>
Cc: llvm-dev at lists.llvm.org; cfe-dev at lists.llvm.org
Subject: Re: [llvm-dev] [cfe-dev] Should isnan be optimized out in fast-math mode?



On Thu, Sep 9, 2021 at 10:34 AM Serge Pavlov via cfe-dev <cfe-dev at lists.llvm.org<mailto:cfe-dev at lists.llvm.org>> wrote:
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).

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.

--
Mehdi



Thanks,
--Serge


On Fri, Sep 10, 2021 at 12:09 AM Chris Tetreault <ctetreau at quicinc.com<mailto:ctetreau at quicinc.com>> wrote:
If the issue is that users want their asserts to fire, then they should be encouraged to only enable fast math in release builds.

From: llvm-dev <llvm-dev-bounces at lists.llvm.org<mailto:llvm-dev-bounces at lists.llvm.org>> On Behalf Of Serge Pavlov via llvm-dev
Sent: Thursday, September 9, 2021 9:53 AM
To: Krzysztof Parzyszek <kparzysz at quicinc.com<mailto:kparzysz at quicinc.com>>
Cc: LLVM Developers <llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>>; cfe-dev at lists.llvm.org<mailto:cfe-dev at lists.llvm.org>
Subject: Re: [llvm-dev] [cfe-dev] Should isnan be optimized out in fast-math mode?


WARNING: This email originated from outside of Qualcomm. Please be wary of any links or attachments, and do not enable macros.
On Thu, Sep 9, 2021 at 11:29 PM Krzysztof Parzyszek <kparzysz at quicinc.com<mailto:kparzysz at quicinc.com>> wrote:
This goes back to what these options actually imply.  The interpretation that I favor is “this code will never see a NaN”, or “the program can assume that no floating point expression will evaluate to a NaN”.  The benefit of that is that it’s intuitively clear.  In that case “isnan(x)” is false, because x cannot be a NaN.  There is no distinction between “isnan(x+x)” and “isnan(x)”.  If the user wants to preserve “isnan(x)”, they can apply some pragma (which clang may actually have already).

It is apparent simplicity. As the discussion in gcc mail list demonstrated (https://gcc.gnu.org/pipermail/gcc-patches/2020-April/544641.html) this is actually an impromissing way. From a practical viewpoint it is also a bad solution as users cannot even check the assertions.


To be honest, I’m not sure that I understand your argument.  Are you saying that under your interpretation we could optimize “isnan(x+x) -> false”, but not “isnan(x) -> false”?

Argument of `isnan(x+x)` is a result of arithmetic operation. According to the meaning of -ffinite-math-only it cannot produce NaN. So this call can be optimized out. In the general case `isnan(x)` value may be, say, loaded from memory. Load is not an arithmetic operation, so nothing prevents from loading NaN. Optimizing the call out is dangerous in this case.



--
Krzysztof Parzyszek  kparzysz at quicinc.com<mailto:kparzysz at quicinc.com>   AI tools development

From: Serge Pavlov <sepavloff at gmail.com<mailto:sepavloff at gmail.com>>
Sent: Thursday, September 9, 2021 11:10 AM
To: Krzysztof Parzyszek <kparzysz at quicinc.com<mailto:kparzysz at quicinc.com>>
Cc: Chris Lattner <clattner at nondot.org<mailto:clattner at nondot.org>>; James Y Knight <jyknight at google.com<mailto:jyknight at google.com>>; LLVM Developers <llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>>; cfe-dev at lists.llvm.org<mailto:cfe-dev at lists.llvm.org>
Subject: Re: [cfe-dev] [llvm-dev] Should isnan be optimized out in fast-math mode?


WARNING: This email originated from outside of Qualcomm. Please be wary of any links or attachments, and do not enable macros.
On Thu, Sep 9, 2021 at 8:30 PM Krzysztof Parzyszek via cfe-dev <cfe-dev at lists.llvm.org<mailto:cfe-dev at lists.llvm.org>> wrote:
If we say that the fast-math flags are “enabling optimizations that the presence of nans otherwise prohibits”, then there is no reason for clang to keep calls to “isnan” around, or to keep checks like “fpclassify(x) == it’s_a_nan” unfolded.  These are exactly the types of optimizations that the presence of NaNs would prohibit.

Transformation 'x * 0 -> 0' is an optimization allowed in the absence of nans as arguments, because it produces a program that behaves identically under the given restrictions. Replacement of `isnan(x + x)` is also an optimization under the same restrictions. Replacement of `isnan(x)` in general case is not, because we cannot assume that x cannot be a NaN.


I understand the need for having some NaN-handling preserved in an otherwise finite-math code.  We already have fast-math-related attributes attached to each function in the LLVM IR, so we could introduce a source-level attribute for enabling/disabling these flags per function.

GCC allows using `#pragma GCC optimize ("finite-math-only")` or `#pragma GCC optimize ("no-finite-math-only")` to enable/disable optimization per function basis. Clang could support this pragmf or maybe `#pragma clang fp` can be extended to support similar functionality.



--
Krzysztof Parzyszek  kparzysz at quicinc.com<mailto:kparzysz at quicinc.com>   AI tools development

From: cfe-dev <cfe-dev-bounces at lists.llvm.org<mailto:cfe-dev-bounces at lists.llvm.org>> On Behalf Of Chris Lattner via cfe-dev
Sent: Wednesday, September 8, 2021 5:51 PM
To: James Y Knight <jyknight at google.com<mailto:jyknight at google.com>>
Cc: LLVM Developers <llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>>; Clang Dev <cfe-dev at lists.llvm.org<mailto:cfe-dev at lists.llvm.org>>
Subject: Re: [cfe-dev] [llvm-dev] Should isnan be optimized out in fast-math mode?


WARNING: This email originated from outside of Qualcomm. Please be wary of any links or attachments, and do not enable macros.
On Sep 8, 2021, at 3:27 PM, James Y Knight via llvm-dev <llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>> wrote:

I expressed my strong support for this on the previous thread, but I'll just repost the most important piece...

I believe the proposed semantics from the Clang level ought to be:
  The -ffinite-math-only and -fno-signed-zeros options do not impact the ability to accurately load, store, copy, or pass or return such values from general function calls. They also do not impact any of the "non-computational" and "quiet-computational" IEEE-754 operations, which includes classification functions (fpclassify, signbit, isinf/isnan/etc), sign-modification (copysign, fabs, and negation `-(x)`), as well as the totalorder and totalordermag functions. Those correctly handle NaN, Inf, and signed zeros even when the flags are in effect. These flags do affect the behavior of other expressions and math standard-library calls, as well as comparison operations.

FWIW, I completely agree - these flags are about enabling optimizations that the presence of nans otherwise prohibits.  We shouldn’t take a literal interpretation of an old GCC manual, as that would not be useful.

If we converge on this definition, I think it should be documented.  This is a source of confusion that comes up periodically.

-Chris



I would not expect this to have an actual negative impact on the performance benefit of those flags, since the optimization benefits mainly arise from comparisons and the general computation instructions which are unchanged.

In further support of this position, I note that the previous thread uncovered at least one vendor -- Apple (https://opensource.apple.com/source/Libm/Libm-2026/Source/Intel/math.h.auto.html) -- going out of their way to cause isnan and friends to function properly with -ffast-math enabled.



On Wed, Sep 8, 2021 at 1:02 PM Serge Pavlov via cfe-dev <cfe-dev at lists.llvm.org<mailto:cfe-dev at lists.llvm.org>> wrote:
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.

1. -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.
2. `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.
3. 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?
Thanks,
--Serge
_______________________________________________
cfe-dev mailing list
cfe-dev at lists.llvm.org<mailto:cfe-dev at lists.llvm.org>
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
_______________________________________________
LLVM Developers mailing list
llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev

_______________________________________________
cfe-dev mailing list
cfe-dev at lists.llvm.org<mailto:cfe-dev at lists.llvm.org>
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
_______________________________________________
cfe-dev mailing list
cfe-dev at lists.llvm.org<mailto:cfe-dev at lists.llvm.org>
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20210909/cb1db0e2/attachment.html>


More information about the llvm-dev mailing list