cgcccompiler-flagsfast-math

Which specific optimization flag causes libm functions to be treated as pure?


I see that passing -ffast-math causes cmath functions like log, cos etc to be treated as pure functions as the compiler is able to perform loop-invariant code motion. As a simple example.

double log_demo(double x, int n) {
   double rv = 0.0l;
   for (int i = 0; i < n; i++) {
       // log only needs to be called once
       // but the compiler does not see that without specific flags
       rv += log(x);
   }
   return rv;
}

Without -ffast-math log(x) is computed in the loop, and is lifted out of the loop otherwise. See this on compiler explorer here -- the branch target .L3 is the loop branch target, you can see call log in the output of gcc without -ffast-math inside the loop body, and only addsd in the other one.

Now, I do not want to enable all the unsafe optimizations and violate the IEEE 754 specs. How can I achieve this without using -ffast-math itself?


Solution

  • The flag that you need is -fno-math-errno.

    -ffast-math is a collection of flags and not just one flag. Some of those flags break conformance to IEEE 754. From man gcc

    -ffast-math

    Sets the options -fno-math-errno, -funsafe-math-optimizations, -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans, -fcx-limited-range and -fexcess-precision=fast.

    So, as it turns out the math functions in libm are not pure because they may set errno. So, the flag that you need to make them pure is -fno-math-errno. It seems that doing so makes the compiler treat them like pure functions, and the compiler can now perform the usual optimizations like loop-invariant code motion. See this in compiler explorer.

    Of course, a consequence is that some error condition related information might be lost. But, the errors in the math functions should mostly be due to domain errors resulting in signalling nans and infs. You can find errno related information on man 3 log.