<div dir="ltr">I agree that the root cause is no formal definition for -ffast-math and friends. AFAIK, nobody has nailed that down:<br>1. gcc: "This mode enables optimizations that allow arbitrary reassociations and transformations with no accuracy guarantees" [1]<br>2. icc: "fast[=1|2] Enables more aggressive optimizations on floating-point data" [2]<br>3. xlc: "some IEEE floating-point rules are violated in ways that can improve performance but might affect program correctness" [3]<br><div><br></div><div>All of these are effectively: "Use at your own risk. If you don't like the result, then turn down the optimizations."<br></div><div><br></div><div>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.<br></div><div><br></div><div><br></div>[1] <a href="https://gcc.gnu.org/wiki/FloatingPointMath" target="_blank">https://gcc.gnu.org/wiki/FloatingPointMath</a><br>[2] <a href="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" target="_blank">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</a><br>[3] <a href="https://www.ibm.com/support/knowledgecenter/en/SSGH3R_13.1.3/com.ibm.compilers.aix.doc/proguide.pdf" target="_blank">https://www.ibm.com/support/knowledgecenter/en/SSGH3R_13.1.3/com.ibm.compilers.aix.doc/proguide.pdf</a><div><br></div><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, Sep 24, 2020 at 11:57 PM Eli Friedman <<a href="mailto:efriedma@quicinc.com" target="_blank">efriedma@quicinc.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">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.<br>
<br>
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.<br>
<br>
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.<br>
<br>
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.<br>
<br>
-Eli<br>
<br>
-----Original Message-----<br>
From: llvm-dev <<a href="mailto:llvm-dev-bounces@lists.llvm.org" target="_blank">llvm-dev-bounces@lists.llvm.org</a>> On Behalf Of Kaylor, Andrew via llvm-dev<br>
Sent: Thursday, September 24, 2020 4:20 PM<br>
To: <a href="mailto:llvm-dev@lists.llvm.org" target="_blank">llvm-dev@lists.llvm.org</a>; Friedman, Eli <<a href="mailto:efriedma@codeaurora.org" target="_blank">efriedma@codeaurora.org</a>>; <a href="mailto:hfinkel@anl.gov" target="_blank">hfinkel@anl.gov</a>; Sanjay Patel <<a href="mailto:spatel@rotateright.com" target="_blank">spatel@rotateright.com</a>>; <a href="mailto:scanon@apple.com" target="_blank">scanon@apple.com</a>; <a href="mailto:dag@hpe.com" target="_blank">dag@hpe.com</a>; Arsenault, Matthew <<a href="mailto:Matthew.Arsenault@amd.com" target="_blank">Matthew.Arsenault@amd.com</a>>; Cameron McInally <<a href="mailto:cameron.mcinally@nyu.edu" target="_blank">cameron.mcinally@nyu.edu</a>>; Kevin Neal <<a href="mailto:Kevin.Neal@sas.com" target="_blank">Kevin.Neal@sas.com</a>>; Ulrich Weigand <<a href="mailto:Ulrich.Weigand@de.ibm.com" target="_blank">Ulrich.Weigand@de.ibm.com</a>><br>
Subject: [EXT] [llvm-dev] nnan, ninf, and poison<br>
<br>
Hi everyone,<br>
<br>
I'd like to bring up a decision that was made a couple of years ago for reconsideration. In this review (<a href="https://reviews.llvm.org/D47963" rel="noreferrer" target="_blank">https://reviews.llvm.org/D47963</a>), 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.<br>
<br>
Here's a reduced version of my test case:<br>
<br>
define double @f(double %x) {<br>
entry:<br>
  %t1 = fadd fast double %x, 0xFFE5555555555555<br>
  %t2 = fadd fast double %x, 0xFFE5555555555555<br>
  %result = fadd fast double %t1, %t2<br>
  ret double %result<br>
}<br>
<br>
To my surprise, InstCombine turns that into this:<br>
<br>
define double @f(double %x) {<br>
  ret double undef<br>
}<br>
<br>
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:<br>
<br>
<br>
  %t1 = fadd fast double %x, 0xFFE5555555555555 ; t1 + -1.1984620899082105e+308<br>
  %t2 = fadd fast double %x, 0xFFE5555555555555 ; t2 + -1.1984620899082105e+308<br>
  %resuit = fadd fast double %t1, %t2 ; result = t1 + t2<br>
-><br>
  %t1 = fadd fast double %x, 0xFFE5555555555555 ; t1 = x + -1.1984620899082105e+308<br>
  %result = fadd fast double %t1, %t1 ; result = t1 + t1<br>
-><br>
  %t1 = fadd fast double %x, 0xFFE5555555555555 ; t1 = x + -1.1984620899082105e+308<br>
  %result = fmul fast double %t1, 2.000000e+00 ; result = t1 * 2.0<br>
-><br>
  %t1 = fmul fast double %x, 2.000000e+00 ; t1 = x * 2.0<br>
  %result = fadd fast double %t1, 0xFFF0000000000000 ; result = x + (-inf)<br>
-><br>
  undef ; Poison!<br>
<br>
<br>
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).<br>
<br>
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.<br>
<br>
I would prefer the definition that Eli originally proposed in D47963:<br>
<br>
``ninf``<br>
   No Infs - Allow optimizations to assume the arguments and result are not<br>
   +/-Inf. Such optimizations are required to retain defined behavior over<br>
   +/-Inf, but the value of the result is unspecified. The unspecified result<br>
   may not be the same each time the expression is evaluated.<br>
<br>
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.<br>
<br>
Thoughts? Opinions?<br>
<br>
Thanks,<br>
Andy<br>
_______________________________________________<br>
LLVM Developers mailing list<br>
<a href="mailto:llvm-dev@lists.llvm.org" target="_blank">llvm-dev@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev</a><br>
</blockquote></div></div>