I agree that the root cause is no formal definition for -ffast-math and
friends. AFAIK, nobody has nailed that down:
1. gcc: "This mode enables optimizations that allow arbitrary
reassociations and transformations with no accuracy guarantees" [1]
2. icc: "fast[=1|2] Enables more aggressive optimizations on floating-point
data" [2]
3. xlc: "some IEEE floating-point rules are violated in ways that can
improve performance but might affect program correctness" [3]
All of these are effectively: "Use at your own risk. If you don't like
the
result, then turn down the optimizations."
As suggested, we can hack the offending transforms to avoid producing
inf/nan at compile-time, but we can't prevent that at run-time. We already
have similar hacks for denorm constants.
[1] https://gcc.gnu.org/wiki/FloatingPointMath
[2]
https://software.intel.com/content/www/us/en/develop/documentation/cpp-compiler-developer-guide-and-reference/top/compiler-reference/compiler-options/compiler-option-details/floating-point-options/fp-model-fp.html#fp-model-fp
[3]
https://www.ibm.com/support/knowledgecenter/en/SSGH3R_13.1.3/com.ibm.compilers.aix.doc/proguide.pdf
On Thu, Sep 24, 2020 at 11:57 PM Eli Friedman <efriedma at quicinc.com>
wrote:
> Fundamentally, I think the problem is the way that "reassoc" is
defined.
> Or rather, the fact that it doesn't really have a definition that can
be
> formalized. We just vaguely promise to produce assembly where the
> computation roughly resembles the mathematical formula written in the
> source code. So really, we don't have any ground to judge whether a
given
> transform is legal or not; it's just based on the gut judgement of a
few
> developers and whatever bugs people file. I wish we had some better way to
> deal with this, but I don't know of one.
>
> The advantage of the sort of definition discussed in D47963 is that it
> would restrict the damage caused by ninf/nnan transforms: it could eat
> floating-point computations, but not integer computations that depend on
> them, like float=>string conversions.
>
> Now that we have freeze instructions, we could use them instead: the
> definition of freeze is basically the same. Not sure if that's better,
> though; the extra instructions involved would add some overhead, and be a
> bit more complicated to reason about.
>
> Either way, that doesn't help if the goal is to ensure floating-point
> computations don't "evaporate", i.e. still exist in the IR.
If that's your
> goal, producing "freeze double undef" isn't really any better
than
> producing "double undef". Probably you could accomplish this by
hacking
> various transforms to refuse to produce inf/nan literals, so reassoc
> transforms can't make a whole function collapse due to ninf/nnan. Of
> course, that doesn't say anything about the value produced at runtime,
so
> I'm not sure how helpful this is.
>
> -Eli
>
> -----Original Message-----
> From: llvm-dev <llvm-dev-bounces at lists.llvm.org> On Behalf Of
Kaylor,
> Andrew via llvm-dev
> Sent: Thursday, September 24, 2020 4:20 PM
> To: llvm-dev at lists.llvm.org; Friedman, Eli <efriedma at
codeaurora.org>;
> hfinkel at anl.gov; Sanjay Patel <spatel at rotateright.com>; scanon
at apple.com;
> dag at hpe.com; Arsenault, Matthew <Matthew.Arsenault at amd.com>;
Cameron
> McInally <cameron.mcinally at nyu.edu>; Kevin Neal <Kevin.Neal at
sas.com>;
> Ulrich Weigand <Ulrich.Weigand at de.ibm.com>
> Subject: [EXT] [llvm-dev] nnan, ninf, and poison
>
> Hi everyone,
>
> I'd like to bring up a decision that was made a couple of years ago for
> reconsideration. In this review (https://reviews.llvm.org/D47963), it was
> decided that if an instruction with the nnan or ninf flag set has an
> argument or a result that is a NaN or +/-inf, respectively, it produces a
> poison value. I came across something today that convinced me that this was
> the wrong decision.
>
> Here's a reduced version of my test case:
>
> define double @f(double %x) {
> entry:
> %t1 = fadd fast double %x, 0xFFE5555555555555
> %t2 = fadd fast double %x, 0xFFE5555555555555
> %result = fadd fast double %t1, %t2
> ret double %result
> }
>
> To my surprise, InstCombine turns that into this:
>
> define double @f(double %x) {
> ret double undef
> }
>
> What happened? InstCombine made a series of optimizations that are each
> fully allowed by the fast math flag and ended up with an intermediate value
> that was -inf, and because I used a setting which promised my program would
> have no infinite inputs or outputs, InstCombine declared the result of the
> entire computation to be poison. Here is the series of unfortunate events
> that led to this outcome:
>
>
> %t1 = fadd fast double %x, 0xFFE5555555555555 ; t1 +
> -1.1984620899082105e+308
> %t2 = fadd fast double %x, 0xFFE5555555555555 ; t2 +
> -1.1984620899082105e+308
> %resuit = fadd fast double %t1, %t2 ; result = t1 + t2
> ->
> %t1 = fadd fast double %x, 0xFFE5555555555555 ; t1 = x +
> -1.1984620899082105e+308
> %result = fadd fast double %t1, %t1 ; result = t1 + t1
> ->
> %t1 = fadd fast double %x, 0xFFE5555555555555 ; t1 = x +
> -1.1984620899082105e+308
> %result = fmul fast double %t1, 2.000000e+00 ; result = t1 * 2.0
> ->
> %t1 = fmul fast double %x, 2.000000e+00 ; t1 = x * 2.0
> %result = fadd fast double %t1, 0xFFF0000000000000 ; result = x + (-inf)
> ->
> undef ; Poison!
>
>
> I fully understand that by using fast-math flags I am giving the compiler
> permission to make optimizations that can change the numeric results and
> that in the worst case, particularly in the presence of extreme values,
> this can result in complete loss of accuracy. However, I do not think this
> should result in the complete elimination of my computation (which would
> make it very hard to debug).
>
> The thing that particularly bothers me about this example is that from a
> user's perspective I would understand the ninf flag to mean that my
data
> doesn't have any infinite values and my algorithm will not produce
them. I
> wouldn't expect to be responsible for infinite values that are the
result
> of things that the compiler did. I expect this option to allow
> transformations like 'x * 0 -> 0'. I don't expect it to
allow '2 * (x +
> VERY_LARGE_FINITE_VALUE) -> poison'. The clang documentation says
> -ffinite-math-only will "Allow floating-point optimizations that
assume
> arguments and results are not NaNs or +-Inf." It seems like a
significant
> leap from this to saying that NaN and Inf values may be treated as having
> introduced undefined behavior.
>
> I would prefer the definition that Eli originally proposed in D47963:
>
> ``ninf``
> No Infs - Allow optimizations to assume the arguments and result are not
> +/-Inf. Such optimizations are required to retain defined behavior over
> +/-Inf, but the value of the result is unspecified. The unspecified
> result
> may not be the same each time the expression is evaluated.
>
> Somewhat tangentially, it seems like we should avoid transformations that
> introduce non-finite values. Craig Topper sent me a patch which does this
> (at least in InstCombine) when I talked to him about this issue.
>
> Thoughts? Opinions?
>
> Thanks,
> Andy
> _______________________________________________
> 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/20200925/c9a980c7/attachment.html>