[llvm-dev] [cfe-dev] Intrinsic llvm::isnan

Serge Pavlov via llvm-dev llvm-dev at lists.llvm.org
Thu Sep 2 23:01:40 PDT 2021


On Thu, Sep 2, 2021 at 3:03 AM Sanjay Patel <spatel at rotateright.com> wrote:

> On Wed, Sep 1, 2021 at 1:26 PM Serge Pavlov <sepavloff at gmail.com> wrote:
>
>>
>> 1. We want isnan() for strict-FP support. The arguments are similar to
>>> why we added "fneg" as a real instruction.
>>> 2. We also need a bunch of other FP classify functions for strict-FP
>>> support to properly deal with SNAN: isinf, isfinite, isnormal, issubnormal,
>>> iszero, fpclassify.
>>
>>
>> Correct. It looks like we have consensus on this topic and agree that
>> `isnan` as well as other classification intrinsics are necessary. If it is
>> so then we discuss only behavior of the intrinsic in fast-math mode.
>>
>
> This is ignoring the fact that we need more general functionality for
> strict-FP. Do we want N different intrinsics to accomplish that? Or is one
> intrinsic that takes parameters and/or returns some enum value a better
> choice? Aren't we going to need that more general function for 'fpclassify'
> anyway?
>

I think separate intrinsics for each function is a better solution. It
allows effective implementation and expresses semantics more clearly.
Universal functions like `fpclassify` or hypothetical
`is_of_fp_class(FP_SUBNORMAL)` involve constants which are target-dependent
and require more effort in codegen to produce better code.


>
>> 5. The change to the regular FP env would make clang/LLVM behave
>> differently than GCC as noted here:
>> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84949 . That bug report has
>> been cited as motivation, but IIUC, the last comment says everything is
>> behaving as expected and the report should be closed pending a C/C++
>> language change.
>>
>> I know nothing about such a pending C/C++ language change. Could you
>> please share the references?
>>
>
> I don't either. I'm just reading the comment there:
> "Thus, it seems the status quo is working as intended. It's just that
> we're missing a standard interface to ask for behavior conformance. Any
> progress on this issue must go via WG21."
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84949#c8
>

AFAIK there is no such pending proposal neither in WG21 nor in WG14.


>
>
>> An argument was made that because the original patch has been in main for
>>> a few weeks, and there are no known bug reports, that it is fine as-is. I
>>> disagree with that: it's not easy to recognize the potential harm (likely
>>> small perf regression), not many users live/test continuously against trunk
>>> for FP fast-math perf, and it's not easy to file bugs against LLVM.
>>
>>
>> Note that leaving `isnan` in the code compiled with `fast-math` is at
>> most a missed optimization. It **does not** break semantics. Hardly it
>> brings noticeable performance regression. It could if the code had many
>> calls to 'isnan', but it is unlikely for real code that is compiled with
>> `fast-math`. Quite the contrary, there are users who have to use integer
>> arithmetics to check for NaN values, because they come from outside
>> (memory, other modules), where `fast-math` is not used. Users expect that
>> `isnan` behaves according to C standard but it does not due to this
>> optimization.
>>
>
> I haven't seen any evidence that users are expecting the new behavior, and
> I don't know how the C standard applies to code that explicitly exempts
> itself from that standard by using -ffast-math.
>

-fast-math is just an optimization option, it is a hint to the compiler,
which can generate faster code in this case. It should not deny the C
standard.

As for user discontent about such optimization, actually there are bug
reports and discussions in forums. For example, GCC bug:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=50724. In the first posts
there the submitter described their concern about using isnan
with fast-math, which is typical for such case. In
https://stackoverflow.com/questions/22931147/stdisinf-does-not-work-with-ffast-math-how-to-check-for-infinity
there is a recomendation to turn off temporarily fast-math when doing such
check. `isnan` has become useless with fast-math.

Library developers also alleviate the problem. According to C standard,
`isnan` is a macro and can be expanded in different ways depending on
configuration options. An external function that implement `isnan` is
compiled obviously without fast-math and allows users to work around this
compiler behavior.


>
> In any case, the patch that introduced the isnan intrinsic changed default
> FP behavior/perf without the appropriate changes to the LLVM LangRef or
> clang manual to even let users know that things changed. We could also
> argue that we're worse off in the current state because isnan changed, but
> isinf did not. At least before, the behavior was consistent?
>

That patch didn't change anything that  LLVM LangRef or clang
manual states. fast-math is an optimization hint, a correct program that
satisfies requirements for -ffast-math must behave identically when
compiled with or without this option. In the worst case leaving the
function call in code may be considered as missed optimization but not
changed functionality. There was nothing that should be mentioned in the
documentation. Of course, remarks in documentation may be useful.

There is no consistency in using `isnan`. If this function is taken from
libc implementation, it makes a real check. If compiler builtin is used, it
disappears. Leaving `isnan` in the code would restore the consistency.


>
>>
>> Thanks,
>> --Serge
>>
>>
>> On Wed, Sep 1, 2021 at 10:07 PM Sanjay Patel <spatel at rotateright.com>
>> wrote:
>>
>>> I'll take a shot at summarizing where we are. Correct as needed:
>>> 1. We want isnan() for strict-FP support. The arguments are similar to
>>> why we added "fneg" as a real instruction.
>>> 2. We also need a bunch of other FP classify functions for strict-FP
>>> support to properly deal with SNAN: isinf, isfinite, isnormal, issubnormal,
>>> iszero, fpclassify.
>>> 3. There's a 2nd motivation to use at least some of these functions in
>>> the regular LLVM FP environment with fast-math. For isnan(), this boils
>>> down to is "fcmp ord nnan" always true, poison, or unknown? So the root
>>> cause might really be that we shouldn't have fast-math-flags on fcmp (see
>>> https://bugs.llvm.org/show_bug.cgi?id=38086 and related bugs).
>>> 4. The change to the regular FP env requires adding/changing
>>> documentation to the LLVM LangRef and clang (James posted a draft in a
>>> reply on 8/24).
>>> 5. The change to the regular FP env would make clang/LLVM behave
>>> differently than GCC as noted here:
>>> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84949 . That bug report
>>> has been cited as motivation, but IIUC, the last comment says everything is
>>> behaving as expected and the report should be closed pending a C/C++
>>> language change.
>>>
>>> Several people have suggested reverting the original patch, so we can
>>> address this both at larger scale (so we have a clear plan for the other
>>> functions that are needed) and with smaller steps (so we don't break
>>> things).
>>>
>>> An argument was made that because the original patch has been in main
>>> for a few weeks, and there are no known bug reports, that it is fine as-is.
>>> I disagree with that: it's not easy to recognize the potential harm (likely
>>> small perf regression), not many users live/test continuously against trunk
>>> for FP fast-math perf, and it's not easy to file bugs against LLVM.
>>>
>>>
>>>
>>> On Wed, Aug 25, 2021 at 7:13 AM Serge Pavlov via llvm-dev <
>>> llvm-dev at lists.llvm.org> wrote:
>>>
>>>> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84949 was mentioned as
>>>>>> motivational, but i don't see any resolution there,
>>>>>> it's not even in "confirmed" state.
>>>>>
>>>>>
>>>>> I agree, this is not at all clear evidence as to GCC's position on the
>>>>> matter.
>>>>>
>>>>
>>>> Sure, but that demonstrates that they are inclined to such
>>>> interpretation.
>>>>
>>>> GCC mail list has relevant discussion on this topic:
>>>> https://gcc.gnu.org/pipermail/gcc-patches/2020-April/544622.html. They
>>>> tried to make the documentation about -ffinite-math-only clearer. The
>>>> discussion was based on a view that if -ffinite-math-only was specified, a
>>>> floating point value cannot be NaN or Inf. During the discussion an
>>>> interesting observation was made:
>>>>
>>>> … double and double under -ffinite-math-only are different types. Any
>>>>> function call from
>>>>> one world to the other is dangerous. Inline functions translated in
>>>>> different
>>>>> TUs compiled with different math flags violate the ODR.
>>>>
>>>>
>>>> If different parts of a program are compiled with and without
>>>> -ffinite-math-only doubles of different flavors can intermix. In the code
>>>> compiled with -ffast-math a user cannot check assumption about the value by
>>>> calling "assert(!isnan(x));" because `isnan` is replaced with expected
>>>> value due to optimizations. The only usable solution in this case could be
>>>> UBSAN, which is a much heavier solution.
>>>>
>>>> Two different flavors of double require different mangling. Template
>>>> specializations also must be different, in particular, specializations of
>>>> `std::numeric_limits<T>` must be different for these two double types, the
>>>> methods `has_infinity` and `has_quite_NaN` must return different values.
>>>>
>>>> They agree that it is profitable to treat NaNs and Infs separately. In
>>>> this case there would be 4 different double types. It is not clear what to
>>>> do with constexpr expressions, should the compiler treat using NaN/Inf as
>>>> undefined behavior even if the ultimate result is finite?
>>>>
>>>> Participants agree that such optimizations are not good:
>>>>
>>>>  … the example of simplifying x * 0 to 0 is about preserving NaNs
>>>>> during expression simplification when the FPU would.  I think these
>>>>> kind of optimizations are what originally was intended to be allowed
>>>>> with -ffinite-math-only - that we started to simplify isnan(x) to false
>>>>> was extending the scope and that change wasn't uncontested since
>>>>> it makes -ffinite-math-only less useful to some people.
>>>>
>>>>
>>>> Eventually they came to conclusion:
>>>>
>>>> … if we want a version of
>>>>> -ffinite-math-only that's well-behaved in language terms (including
>>>>> in terms of the macros that are defined and in the values of
>>>>> numeric_limits), perhaps this should be an official (optional) C/C++
>>>>> extension that defines what the rules are.
>>>>
>>>>
>>>> Thanks,
>>>> --Serge
>>>>
>>>>
>>>> On Wed, Aug 25, 2021 at 4:25 AM James Y Knight <jyknight at google.com>
>>>> wrote:
>>>>
>>>>> On Tue, Aug 24, 2021 at 1:53 PM Roman Lebedev via cfe-dev <
>>>>> cfe-dev at lists.llvm.org> wrote:
>>>>>
>>>>>> Regardless of everything, i would like to see a patch that restores
>>>>>> the -ffast-math handling, and *then* the RFC on what the is-nan check
>>>>>> should do when -ffast-math is present.
>>>>>> It is more than possible that the RFC will succeed,
>>>>>> but i don't think a change like that should happen the way it did.
>>>>>
>>>>>
>>>>> I find the rationale to be convincing, as to the need for a change.
>>>>> But, the scope of the proposal is too narrow. We cannot discuss fast-math
>>>>> semantics changes *only* for "isnan", it needs to be in the context
>>>>> of the desired behavior for all operations -- the RFC should cover the
>>>>> entire set of changes we want to eventually make, even if isnan is the only
>>>>> thing implemented so far. Discussing this greater scope could result in a
>>>>> different desired implementation, rather than simply adding "llvm.isnan"
>>>>> intrinsic.
>>>>>
>>>>> Yet, even with that expanded scope, the two halves of the proposal are
>>>>> still going to be closely linked, so I suspect it still makes sense to
>>>>> discuss both the strict-fp and fast-math changes in a single RFC.
>>>>>
>>>>> Anyhow, for the fast-math section, I believe the proposed semantics
>>>>> 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.
>>>>>
>>>>> 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.
>>>>>
>>>>>
>>>>>> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84949 was mentioned as
>>>>>> motivational, but i don't see any resolution there,
>>>>>> it's not even in "confirmed" state.
>>>>>
>>>>>
>>>>> I agree, this is not at all clear evidence as to GCC's position on the
>>>>> matter.
>>>>>
>>>> _______________________________________________
>>>> LLVM Developers mailing list
>>>> llvm-dev at lists.llvm.org
>>>> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
>>>>
>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20210903/82bc6a53/attachment.html>


More information about the llvm-dev mailing list