Properly shrink pow() to powf() as a binary function and, when no other simplification applies, do not discard it.
Details
Diff Detail
- Repository
- rL LLVM
Event Timeline
llvm/test/Transforms/InstCombine/double-float-shrink-1.ll | ||
---|---|---|
363 ↗ | (On Diff #158403) | It looks like optimizeBinaryDoubleFP doesn't do all the same checks optimizeUnaryDoubleFP does? optimizeUnaryDoubleFP intentionally chooses not to optimize operations where the result isn't truncated because the extra bits might actually be useful. |
llvm/test/Transforms/InstCombine/double-float-shrink-1.ll | ||
---|---|---|
363 ↗ | (On Diff #158403) | Arguably, this should not shrink. |
llvm/test/Transforms/InstCombine/double-float-shrink-1.ll | ||
---|---|---|
363 ↗ | (On Diff #158403) | Besides here, optimizeBinaryDoubleFP() is also used for f{min,max}() and copysign(). Neither of the latter functions need the extra bits in the result. |
llvm/test/Transforms/InstCombine/double-float-shrink-1.ll | ||
---|---|---|
363 ↗ | (On Diff #158403) | An argument in favor of shrinking: it may reduce the number of instructions as shown in this test. |
llvm/test/Transforms/InstCombine/double-float-shrink-1.ll | ||
---|---|---|
363 ↗ | (On Diff #158403) | And the series in powf() is probably shorter and faster than in pow(). |
I think we should commit this based on fixing the miscompile alone; we can debate the other diff in a follow-on patch if needed.
But let's see if @efriedma agrees.
The miscompile was never noticed because the shrinking was always discarded. It seems sensible to me that both are fixed in one swoop.
llvm/test/Transforms/InstCombine/double-float-shrink-1.ll | ||
---|---|---|
363 ↗ | (On Diff #158403) | On the other hand, this shrink is only performed when -enable-double-float-shrink is specified or with FMF, which implies the attribute afn. |
I'd like to get this right... both the CheckRetType check, and the "infinite loop" check from optimizeUnaryDoubleFP are probably relevant. Can we share code between the two functions?
Maybe the CheckRetType check isn't critical if we're only doing this transform by default when afn is enabled, but it's still losing a lot of bits, particularly if the input is supposed to be an exact number. pow(2.f, 0.5f) is a lot different from (double)powf(2.f, 0.5f).
(If you want to fix the potential miscompile for 7.0, I'd accept a patch to just completely disable shrinking for pow.)
Just to make it clear, CheckRetType only matters for optimizeUnaryDoubleFP(); optimizeBinaryDoubleFP() doesn't have.
I agree with you, but wonder how this could ever work. It didn't, so probably nobody ever used it. So, why was it added? If it has no defensible purpose, then, by all means, it should be dropped.
So, why was it added?
I would assume someone added it alongside a bunch of other operations where it matters more, and didn't write enough tests.
In general, the point of the transform is to catch code where someone accidentally writes "float z = pow(x, y);" instead of "float z = powf(x, y);" (And sometimes, it isn't practical to fix the code.)
Alternatively, optimizeBinaryDoubleFP() could be fixed to shrink just float f = pow(), while keeping its other users, f{min,max}() and copysign() happy, which should be easy to satisfy, given how simple these functions are.
Refactored the helper functions for shrinking unary and binary functions into a single one, while keeping all their functionality, in rL338905.