[Bug tree-optimization/126631] New: Jump threading drops isinf() overflow check after finite * constant
pl0h0yp1 at gmail dot com via Gcc-bugs <[email protected]> Tue, 04 Aug 2026 07:27:45 +0000
| Newsgroups | gmane.comp.gcc.bugs |
|---|---|
| Message-ID | <[email protected]/bugzilla/> |
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=3D126631
Bug ID: 126631
Summary: Jump threading drops isinf() overflow check after
finite * constant
Product: gcc
Version: 15.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: tree-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: pl0h0yp1 at gmail dot com
Target Milestone: ---
Created attachment 65233
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=3D65233&action=3Dedit
Reduced testcase: -O2 drops isinf() check after finite * M_PI
Summary
=3D=3D=3D=3D=3D=3D=3D
At -O1 and above, jump threading (-fthread-jumps) can delete a live
overflow check of the form:
r =3D a * b;
if (isinf (r) && !isinf (a) && !isinf (b))
overflow_error ();
when both operands are known finite =E2=80=94 e.g. after an earlier identic=
al
check, or when one operand is a finite constant such as M_PI. The
generated code then returns +Inf instead of calling overflow_error().
This is wrong without -ffinite-math-only: IEEE 754 multiplication of two
finite values may overflow to infinity. In particular, if
a =E2=88=88 (DBL_MAX/=CF=80, DBL_MAX], then a * M_PI is +Inf.
Testcase
=3D=3D=3D=3D=3D=3D=3D=3D
Attach pr-float-mul-isinf.c.
gcc -O0 pr-float-mul-isinf.c -lm && ./a.out ; echo $?
# =3D> ERROR: value out of range: overflow
# exit 0
gcc -O2 pr-float-mul-isinf.c -lm && ./a.out ; echo $?
# =3D> prints Infinity, exit 1
gcc -O2 -fno-thread-jumps pr-float-mul-isinf.c -lm && ./a.out ; echo $?
# =3D> ERROR: value out of range: overflow
# exit 0
clang -O2 pr-float-mul-isinf.c -lm && ./a.out ; echo $?
# =3D> ERROR: value out of range: overflow
# exit 0
Analysis
=3D=3D=3D=3D=3D=3D=3D=3D
After inlining, before jump threading (e.g. after fre3), the CFG is still
correct (isinf(x) lowered to x u<=3D DBL_MAX):
result_8 =3D r * r;
if (result_8 u<=3D DBL_MAX) ... else maybe overflow_error();
result_5 =3D result_8 * M_PI;
if (result_5 u<=3D DBL_MAX)
return result_5;
else if (result_8 u<=3D DBL_MAX)
overflow_error (); // both operands finite =3D> must report overflow
else
return result_5; // Inf * pi stays Inf
After pass threadfull1, on the path where result_8 is already known
finite, the check of the product is dropped entirely:
result_5 =3D result_8 * M_PI;
return result_5; // unconditional; no isinf / u<=3D DBL_MAX test
i.e. threading treats "finite * M_PI overflows" as impossible. A later
thread1 pass removes the now-unreachable outer overflow_error().
Optimized gimple for circle_ar() keeps the inner r*r overflow check but
drops the outer one and returns Inf:
result_5 =3D result_8 * 3.14159...;
return result_5; // no isinf check
Related existing mitigation in range-op-float.cc (PR107608) avoids
folding an overflowing operation to a singleton =C2=B1Inf under
flag_trapping_math so that DOM does not elide a *hardware* FP
exception. That does not protect an explicit user-written isinf()
check + call, which is what this test relies on.
Workaround: -fno-thread-jumps (or marking the multiply result volatile).
How found
=3D=3D=3D=3D=3D=3D=3D=3D=3D
PostgreSQL area(circle) uses:
float8_mul (float8_mul (radius, radius), M_PI);
With gcc -O2, SELECT area(circle '<(0,0),1e154>') returns Infinity
instead of ERROR "value out of range: overflow". The equivalent SQL
expression 1e154::float8 * 1e154::float8 * pi() still errors, because
it is not subject to the same C-level inlining/threading.
Minimal source
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
See attached pr-float-mul-isinf.c (names mirror PostgreSQL float8_mul /
circle_ar; prints the same ERROR string / Infinity layout as psql).
Note: the radius must not be a compile-time constant in main(), or
gcc -O2 may constprop r*r and accidentally keep the outer check.
The attached test uses a volatile radius for that reason.=