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?
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
.