From ce243c9eefc2728cb8743ebbcb84c3e08236f06a Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Tue, 4 Apr 2023 16:13:06 +0200 Subject: [PATCH] range-op-float: Fix reverse ops of comparisons [PR109386] I've missed one of my recent range-op-float.cc changes (likely the r13-6967 one) caused FAIL: libphobos.phobos/std/math/algebraic.d execution test FAIL: libphobos.phobos_shared/std/math/algebraic.d execution test regressions, distilled into a C testcase below. In the testcase, we have !(u >= v) condition where both u and v are results of fabs*, which guards t1 = u u<= __FLT_MAX__; and t2 = v u<= __FLT_MAX__; comparisons. From false u >= v where u and v have [0.0, +Inf] NAN ranges we (incorrectly deduce that one of them is [nextafterf (0.0, 1.0), +Inf] NAN and the other is [0.0, nextafterf (+Inf, 0.0)] NAN and from that deduce that one of the comparisons is always true, because UNLE_EXPR with the maximum representable number are false only if the value is +Inf and our ranges tell that is not possible. The bug is that the u >= v comparison determines a sensible range only when it is true - we then know neither operand can be NAN and it behaves correctly. But when the comparison is false, our current code gives sensible answers only if the other op can't be NAN. If it can be NAN, whenever it is NAN, the comparison is always false regardless of the other value, so the other value needs to be VARYING. Now, foperator_unordered_lt::op1_range etc. had code to deal with that for op?.known_nan (), but as the testcase shows, it is enough if it may be a NAN at runtime to make it VARYING. So, the following patch replaces for all those BRS_FALSE cases of the normal non-equality comparisons if (opOTHER.known_isnan ()) r.set_varying (type); to do it also if maybe_isnan (). For the unordered or ... comparisons, it is similar for BRS_TRUE. Those comparisons are true whenever either operand is NAN, or if neither is NAN, the corresponding normal comparison. So, if those comparisons are true and other operand might be a NAN, we can't tell (VARYING), if it is false, currently handling is correct. 2023-04-04 Jakub Jelinek PR tree-optimization/109386 * range-op-float.cc (foperator_lt::op1_range, foperator_lt::op2_range, foperator_le::op1_range, foperator_le::op2_range, foperator_gt::op1_range, foperator_gt::op2_range, foperator_ge::op1_range, foperator_ge::op2_range): Make r varying for BRS_FALSE case even if the other op is maybe_isnan, not just known_isnan. (foperator_unordered_lt::op1_range, foperator_unordered_lt::op2_range, foperator_unordered_le::op1_range, foperator_unordered_le::op2_range, foperator_unordered_gt::op1_range, foperator_unordered_gt::op2_range, foperator_unordered_ge::op1_range, foperator_unordered_ge::op2_range): Make r varying for BRS_TRUE case even if the other op is maybe_isnan, not just known_isnan. * gcc.c-torture/execute/ieee/pr109386.c: New test. --- gcc/range-op-float.cc | 32 +++++++++---------- .../gcc.c-torture/execute/ieee/pr109386.c | 21 ++++++++++++ 2 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 gcc/testsuite/gcc.c-torture/execute/ieee/pr109386.c diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc index 41d497e96ade..e0e91bad44d6 100644 --- a/gcc/range-op-float.cc +++ b/gcc/range-op-float.cc @@ -886,7 +886,7 @@ foperator_lt::op1_range (frange &r, case BRS_FALSE: // On the FALSE side of x < NAN, we know nothing about x. - if (op2.known_isnan ()) + if (op2.known_isnan () || op2.maybe_isnan ()) r.set_varying (type); else build_ge (r, type, op2); @@ -923,7 +923,7 @@ foperator_lt::op2_range (frange &r, case BRS_FALSE: // On the FALSE side of NAN < x, we know nothing about x. - if (op1.known_isnan ()) + if (op1.known_isnan () || op1.maybe_isnan ()) r.set_varying (type); else build_le (r, type, op1); @@ -998,7 +998,7 @@ foperator_le::op1_range (frange &r, case BRS_FALSE: // On the FALSE side of x <= NAN, we know nothing about x. - if (op2.known_isnan ()) + if (op2.known_isnan () || op2.maybe_isnan ()) r.set_varying (type); else build_gt (r, type, op2); @@ -1031,7 +1031,7 @@ foperator_le::op2_range (frange &r, case BRS_FALSE: // On the FALSE side of NAN <= x, we know nothing about x. - if (op1.known_isnan ()) + if (op1.known_isnan () || op1.maybe_isnan ()) r.set_varying (type); else if (op1.undefined_p ()) return false; @@ -1112,7 +1112,7 @@ foperator_gt::op1_range (frange &r, case BRS_FALSE: // On the FALSE side of x > NAN, we know nothing about x. - if (op2.known_isnan ()) + if (op2.known_isnan () || op2.maybe_isnan ()) r.set_varying (type); else if (op2.undefined_p ()) return false; @@ -1151,7 +1151,7 @@ foperator_gt::op2_range (frange &r, case BRS_FALSE: // On The FALSE side of NAN > x, we know nothing about x. - if (op1.known_isnan ()) + if (op1.known_isnan () || op1.maybe_isnan ()) r.set_varying (type); else if (op1.undefined_p ()) return false; @@ -1228,7 +1228,7 @@ foperator_ge::op1_range (frange &r, case BRS_FALSE: // On the FALSE side of x >= NAN, we know nothing about x. - if (op2.known_isnan ()) + if (op2.known_isnan () || op2.maybe_isnan ()) r.set_varying (type); else if (op2.undefined_p ()) return false; @@ -1262,7 +1262,7 @@ foperator_ge::op2_range (frange &r, tree type, case BRS_FALSE: // On the FALSE side of NAN >= x, we know nothing about x. - if (op1.known_isnan ()) + if (op1.known_isnan () || op1.maybe_isnan ()) r.set_varying (type); else if (op1.undefined_p ()) return false; @@ -1618,7 +1618,7 @@ foperator_unordered_lt::op1_range (frange &r, tree type, switch (get_bool_state (r, lhs, type)) { case BRS_TRUE: - if (op2.known_isnan ()) + if (op2.known_isnan () || op2.maybe_isnan ()) r.set_varying (type); else if (op2.undefined_p ()) return false; @@ -1652,7 +1652,7 @@ foperator_unordered_lt::op2_range (frange &r, tree type, switch (get_bool_state (r, lhs, type)) { case BRS_TRUE: - if (op1.known_isnan ()) + if (op1.known_isnan () || op1.maybe_isnan ()) r.set_varying (type); else if (op1.undefined_p ()) return false; @@ -1726,7 +1726,7 @@ foperator_unordered_le::op1_range (frange &r, tree type, switch (get_bool_state (r, lhs, type)) { case BRS_TRUE: - if (op2.known_isnan ()) + if (op2.known_isnan () || op2.maybe_isnan ()) r.set_varying (type); else if (op2.undefined_p ()) return false; @@ -1759,7 +1759,7 @@ foperator_unordered_le::op2_range (frange &r, switch (get_bool_state (r, lhs, type)) { case BRS_TRUE: - if (op1.known_isnan ()) + if (op1.known_isnan () || op1.maybe_isnan ()) r.set_varying (type); else if (op1.undefined_p ()) return false; @@ -1835,7 +1835,7 @@ foperator_unordered_gt::op1_range (frange &r, switch (get_bool_state (r, lhs, type)) { case BRS_TRUE: - if (op2.known_isnan ()) + if (op2.known_isnan () || op2.maybe_isnan ()) r.set_varying (type); else if (op2.undefined_p ()) return false; @@ -1870,7 +1870,7 @@ foperator_unordered_gt::op2_range (frange &r, switch (get_bool_state (r, lhs, type)) { case BRS_TRUE: - if (op1.known_isnan ()) + if (op1.known_isnan () || op1.maybe_isnan ()) r.set_varying (type); else if (op1.undefined_p ()) return false; @@ -1946,7 +1946,7 @@ foperator_unordered_ge::op1_range (frange &r, switch (get_bool_state (r, lhs, type)) { case BRS_TRUE: - if (op2.known_isnan ()) + if (op2.known_isnan () || op2.maybe_isnan ()) r.set_varying (type); else if (op2.undefined_p ()) return false; @@ -1980,7 +1980,7 @@ foperator_unordered_ge::op2_range (frange &r, tree type, switch (get_bool_state (r, lhs, type)) { case BRS_TRUE: - if (op1.known_isnan ()) + if (op1.known_isnan () || op1.maybe_isnan ()) r.set_varying (type); else if (op1.undefined_p ()) return false; diff --git a/gcc/testsuite/gcc.c-torture/execute/ieee/pr109386.c b/gcc/testsuite/gcc.c-torture/execute/ieee/pr109386.c new file mode 100644 index 000000000000..98895d1e8aee --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/ieee/pr109386.c @@ -0,0 +1,21 @@ +/* PR tree-optimization/109386 */ + +static inline float +foo (float x, float y) +{ + float u = __builtin_fabsf (x); + float v = __builtin_fabsf (y); + if (!(u >= v)) + { + if (__builtin_isinf (v)) return v; + if (__builtin_isinf (u)) return u; + } + return 42.0f; +} + +int +main () +{ + if (!__builtin_isinf (foo (__builtin_inff (), __builtin_nanf ("")))) + __builtin_abort (); +} -- 2.47.2