[cfe-dev] incorrect floating point accrued exception flags with -O

Stephen Canon via cfe-dev cfe-dev at lists.llvm.org
Mon Apr 17 06:08:41 PDT 2017


Hi Michael —

You’re dancing around a real issue in clang (and most other compilers), but it’s camouflaged by a few issues in your code. I’ll address those first:

1. If you want to read or set the floating-point environment, your code must contain:

	#pragma STDC FENV_ACCESS ON

If you do not have this pragma, all bets are off. The compiler is free to re-arrange your calls to fe* functions, treat the floating-point environment as constant, or eliminate them all together. See §7.6.1 of the C standard for more details, in particular, the following sentence:

> If part of a program tests floating-point status flags, sets floating-point control modes, or runs under non-default mode settings, but was translated with the state for the FENV_ACCESS pragma ‘‘off’’, the behavior is undefined.


If you add this pragma to your code example, you’ll get a helpful warning from clang that FENV_ACCESS is not [yet] supported.
 
2. Also in §7.6, you will note the following sentence (third bullet in paragraph 3):

> a function call is assumed to have the potential for raising floating-point exceptions, unless its documentation promises otherwise.

In particular, your code calls `printf` between `feclearexcept` and `fetestexcept`. To the best of my recollection, `printf` is not documented as not modifying the floating-point environment, so once you call it, all bets are off w.r.t. the floating-point state, even if you set FENV_ACCESS ON.

OK, now the real issue in clang: it doesn’t [yet] support FENV_ACCESS. Neither does GCC. There’s been some motion recently toward adding support for FENV_ACCESS, but it’s a largish project, and it hasn’t happened yet. Both compilers, when optimization is enabled, simply replace your call to fcvt(1.1) with 1 (because they don’t support FENV_ACCESS). GCC happens to “work” in your second example because it inlines `fcvt` into `test_fcvt`, but doesn’t inline `test_fcvt` into `main`, clang inlines both, does constant propagation, and no flags are raised.

godbolt.org is a good resource to see what’s going on here, though it won’t tell you *why*:
https://godbolt.org/g/Zb8Eoc

Best,
– Steve

> On Apr 15, 2017, at 5:51 PM, Michael Clark via cfe-dev <cfe-dev at lists.llvm.org> wrote:
> 
> Hi,
> 
> First, apologies if this is not the right place to post.
> 
> I am seeing unexpected values in the floating point accrued exception flags with clang generated programs. My original issue is seeing FE_INEXACT after an exact float to unsigned int conversion within a ternary expression. This issue does not occur with gcc. In trying to isolate the problem I wrote a simple test program, which results in completely opposite behaviour. FE_INEXACT is not getting set for an inexact conversion when optimisation is enabled. 
> 
> Given I’m not yet seeing predictable results for accrued exception flags, I gave up trying to reproduce my original issue (FE_INEXACT for exact conversion) until I am certain which floating point optimisations are being enabled, and under what conditions floating point accrued exceptions are optimised away, otherwise I can’t be sure to isolate my first problem.
> 
> I have two versions of a simple test program below, one which even returns incorrect results in gcc. The tests below run on Linux using Debian vendor build of clang 3.8.1 and on macos with the Xcode 8.3.1 vendor build of clang. I don’t have -fast-math enabled so I would expect standards compliant behaviour. I would like to know what optimisations are preventing floating point accrued exceptions from being set and how to disable these optimisation so that I am get deterministic results, then I can try to reproduce my first issue in isolation.
> 
> - fcvt1.c triggers the same issue with gcc (FE_INEXACT not set for inexact conversion)
> - fcvt2.c triggers the issue only with clang (FE_INEXACT not set for inexact conversion)
> - no reproducer yet… (FE_INEXACT set after exact conversion)
> 
> Happy Holidays,
> 
> Michael.
> 
> $ gcc --version
> gcc (Debian 6.3.0-6) 6.3.0 20170205
> Copyright (C) 2016 Free Software Foundation, Inc.
> This is free software; see the source for copying conditions.  There is NO
> warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
> 
> $ gcc -O0 -lm fcvt1.c 
> $ ./a.out 
> 1 exact
> 1 inexact
> $ gcc -O3 -lm fcvt1.c 
> $ ./a.out 
> 1 exact
> 1 exact
> $ gcc -O0 -lm fcvt2.c 
> $ ./a.out 
> 1 exact
> 1 inexact
> $ gcc -O3 -lm fcvt2.c 
> $ ./a.out 
> 1 exact
> 1 inexact
> 
> $ clang --version
> clang version 3.8.1-16 (tags/RELEASE_381/final)
> Target: x86_64-pc-linux-gnu
> Thread model: posix
> InstalledDir: /usr/bin
> 
> $ clang -O0 -lm fcvt1.c 
> $ ./a.out 
> 1 exact
> 1 inexact
> $ clang -O3 -lm fcvt1.c 
> $ ./a.out 
> 1 exact
> 1 exact
> $ clang -O0 -lm fcvt2.c 
> $ ./a.out 
> 1 exact
> 1 inexact
> $ clang -O3 -lm fcvt2.c 
> $ ./a.out 
> 1 exact
> 1 exact
> 
> $ clang --version
> Apple LLVM version 8.1.0 (clang-802.0.41)
> Target: x86_64-apple-darwin16.5.0
> Thread model: posix
> InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
> 
> $ cc -O0 fcvt1.c 
> $ ./a.out 
> 1 exact
> 1 inexact
> $ cc -O3 fcvt1.c 
> $ ./a.out 
> 1 exact
> 1 exact
> $ cc -O0 fcvt2.c 
> $ ./a.out 
> 1 exact
> 1 inexact
> $ cc -O3 fcvt2.c 
> $ ./a.out 
> 1 exact
> 1 exact
> 
> 
> $ cat fcvt1.c 
> #include <stdio.h>
> #include <fenv.h>
> 
> unsigned fcvt(float a)
> {
>        return (unsigned)a;
> }
> 
> int main()
> {
>        fesetround(FE_TONEAREST);
> 
>        feclearexcept(FE_ALL_EXCEPT);
>        printf("%d ", fcvt(1.0f));
>        printf("%s\n", fetestexcept(FE_INEXACT) ? "inexact" : "exact");
> 
>        feclearexcept(FE_ALL_EXCEPT);
>        printf("%d ", fcvt(1.1f));
>        printf("%s\n", fetestexcept(FE_INEXACT) ? "inexact" : "exact");
> }
> 
> 
> $ cat fcvt2.c
> #include <stdio.h>
> #include <fenv.h>
> 
> unsigned fcvt(float a)
> {
>        return (unsigned)a;
> }
> 
> void test_fcvt(float a)
> {
>        feclearexcept(FE_ALL_EXCEPT);
>        printf("%d ", fcvt(a));
>        printf("%s\n", fetestexcept(FE_INEXACT) ? "inexact" : "exact");
> }
> 
> int main()
> {
>        fesetround(FE_TONEAREST);
> 
>        test_fcvt(1.0f);
>        test_fcvt(1.1f);
> }
> 
> _______________________________________________
> cfe-dev mailing list
> cfe-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev




More information about the cfe-dev mailing list