https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128454
Bug ID: 128454
Summary: reassoc undistribute_ops_list peels a pow/powi with
negative exponent one factor at a time, generated code
grows linearly with |exponent|
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Keywords: missed-optimization
Severity: normal
Priority: P3
Component: tree-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: lzy20000419 at outlook dot com
Target Milestone: ---
Host: x86_64-pc-linux-gnu
Target: x86_64-pc-linux-gnu
Build: x86_64-pc-linux-gnu
Split out from PR128006 (issue 3 of comment 4) at the maintainer's request.
undistribute_ops_list peels one x per round from a pow/powi with a negative
exponent, so the exponent moves away from 0 and the emitted code is
O(|exponent|):
```c
#include <math.h>
double f (double x, double y, double z)
{
return (x * y) * __builtin_powi (x, 8000) + x * z * x * pow (x, -8000.0);
}
```
With `-O2 -fassociative-math -fno-signed-zeros -fno-trapping-math -S`, trunk
17.0.0 20261105 (2175b27369e5) emits 2041 asm lines for this file (291 lines
with exponent 1000, 1041 with 4000), growing linearly with the exponent. 16.2.1
and 14.2.0 give the same numbers. On Compiler Explorer 8.5.0, 14.2.0 and trunk
behave the same, and 7.5.0 is about twice as large (524 lines for 1000, 4024
for 8000), so this is not a regression. Godbolt (trunk and 14.2):
https://godbolt.org/z/33xEjhfx8
Each round factors x out of both addends, which decrements the pow (x, -8000.0)
exponent via decrement_power to -8001, -8002, ..., so the loop never reaches a
fixed point on the pow side and only stops when the powi (x, 8000) side runs
out, leaving a long chain of multiplications.
A possible one-line fix (not tested) is to make stmt_is_power_of_op (or
undistribute_ops_list) refuse to factor a base out of a pow/powi whose exponent
is not positive.
Disclaimer: This test case was found with LLM-assisted targeted testing, and
the report was written with LLM assistance.