https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128006

liu zhenyu <lzy20000419 at outlook dot com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |lzy20000419 at outlook dot com

--- Comment #4 from liu zhenyu <lzy20000419 at outlook dot com> ---
r17-5395 fixes the comment 0 testcase, but three more problems in the same
pow/powi handling of tree-ssa-reassoc.cc are still there on trunk (17.0.0
20261105, bd921def14a4). All testcases are UB-free (x = -1 keeps every
intermediate at +-1).

1) decrement_power wraps for the integer-minimum exponent. The exponent is now
known to be an integer, but `power = real_to_integer (&c) - 1`
(tree-ssa-reassoc.cc:1242) overflows for c = -2^63, and the powi case
(TREE_INT_CST_LOW - 1) does the same for INT_MIN:

```c
#include <math.h>
__attribute__((noipa)) double f (double x, double y, double z)
{
  return (x * y) * pow (x, -9223372036854775808.0) + z * x;
}
int main (void)
{
  if (f (-1.0, 1.0, 0.0) != -1.0) __builtin_abort ();
  if (f (-1.0, 1.0, 1.0) != -2.0) __builtin_abort ();
  return 0;
}
```

```
$ gcc -O2 -fassociative-math -fno-signed-zeros -fno-trapping-math t1.c -lm &&
./a.out
Aborted
```

The reassoc1 dump has `_12 = pow (x_5(D), 9.223372036854775807e+18);`, i.e. the
exponent went from -2^63 to +2^63-1. -fno-tree-reassoc gives the expected
result.

2) attempt_builtin_powi truncates repeat counts. add_repeat_to_ops_vec
(tree-ssa-reassoc.cc:629) stores a HOST_WIDE_INT repeat into
operand_entry::count, which is unsigned int (tree-ssa-reassoc.h:29), so pow (x,
2^32) contributes 0 and disappears from the product:

```c
#include <math.h>
__attribute__((noipa)) double f (double x, double y)
{
  return x * x * x * y * x * x * x * pow (x, 4294967296.0);
}
int main (void)
{
  if (f (-1.0, 1.0) != 1.0) __builtin_abort ();
  return 0;
}
```

```
$ gcc -O2 -ffast-math t2.c -lm && ./a.out
Aborted
```

The generated code has no pow call left. The accumulated count is later emitted
as an int powi exponent, so sums above INT_MAX wrap as well.

3) 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
emits about 2040 asm lines for this function (290 lines for exponent 1000, 1040
for 4000), growing linearly with the exponent.

Tested: 1) and 2) abort with 14.2.0 and 17.0.0 (20261105); 3) is linear on
both.

Possible fixes: reject exponents whose decrement is not representable in
stmt_is_power_of_op (or let decrement_power fail), cap acceptable_pow_call
(tree-ssa-reassoc.cc:6109) exponents at INT_MAX and give operand_entry::count a
wide enough type, and factor a pow/powi with a negative exponent at most once
in undistribute_ops_list.

Disclaimer: This test case was found with LLM-assisted targeted testing, and
the report was written with LLM assistance.

Reply via email to