Re: [PATCH] range-op: Implement floating point division fold_range [PR107569]

2022-11-30 Thread Iain Buclaw via Gcc-patches
Excerpts from Jakub Jelinek via Gcc-patches's message of November 11, 2022 
10:09 am:
> Hi!
> 
> Here is the floating point division fold_range implementation,
> as I wrote in the last mail, we could outline some of the common parts
> into static methods with descriptive names and share them between
> foperator_div and foperator_mult.
> 
> Bootstrapped/regtested on top of the earlier version of the multiplication
> fold_range on x86_64-linux and i686-linux, regressions are
> +FAIL: gcc.dg/pr95115.c execution test
> +FAIL: libphobos.phobos/std/math/hardware.d execution test
> +FAIL: libphobos.phobos_shared/std/math/hardware.d execution test

I've had some time to look at the Phobos failures, and seems to me that
it's a poorly written test.

pragma(inline, false) static void blockopt(ref real x) {}
real a = 3.5;
// Set all the flags to zero
resetIeeeFlags();
assert(!ieeeFlags.divByZero);
blockopt(a); // avoid constant propagation by the optimizer
// Perform a division by zero.
a /= 0.0L;
assert(a == real.infinity);
assert(ieeeFlags.divByZero);
blockopt(a); // avoid constant propagation by the optimizer


1. Since this patch, that `a /= 0.0L` operation no longer appears in the
final assembly - so no divide-by-zero flags are raised.

2. Whoever introduced blockopt() perhaps did not understand that
`a /= 0.0L` is not safe from constant propagation just because it is
surrounded by some uninlinable call.

I'll fix the test in upstream, it should really be something like:

pragma(inline, false)
static real forceDiv(real x, real y) { return x / y; }
a = forceDiv(a, 0.0L);
assert(a == real.infinity);
assert(ieeeFlags.divByZero);


Regards,
Iain.


Re: [PATCH] range-op: Implement floating point division fold_range [PR107569]

2022-11-22 Thread Joseph Myers
On Tue, 22 Nov 2022, Jan-Benedict Glaw wrote:

> I'm running a slightly hacked [glibc]/scripts/build-many-glibcs.py to
> to CI builds for glibc as well by now (hacked to allow for GCC master
> being used) and this GCC commit
> (2d5c4a16dd833aa083f13dd3e78e3ef38afe6ebb) triggers glibc's
> elf/check-localplt testcase to fail, though just for
> sparc64-linux-gnu. (As I just started with glibc checks, it took me a
> while to realize this was a real regression and not a flaw in my
> setup.)

I think the appropriate fix is to update the relevant localplt.data (to 
add the relevant libgcc symbol marked with "?" as optional), I don't think 
there's a GCC bug here.

-- 
Joseph S. Myers
jos...@codesourcery.com


Re: [PATCH] range-op: Implement floating point division fold_range [PR107569]

2022-11-21 Thread Jan-Benedict Glaw
Hi Jakub,

On Fri, 2022-11-11 10:09:42 +0100, Jakub Jelinek via Gcc-patches 
 wrote:
> Here is the floating point division fold_range implementation,
> as I wrote in the last mail, we could outline some of the common parts
> into static methods with descriptive names and share them between
> foperator_div and foperator_mult.
[...]

I'm running a slightly hacked [glibc]/scripts/build-many-glibcs.py to
to CI builds for glibc as well by now (hacked to allow for GCC master
being used) and this GCC commit
(2d5c4a16dd833aa083f13dd3e78e3ef38afe6ebb) triggers glibc's
elf/check-localplt testcase to fail, though just for
sparc64-linux-gnu. (As I just started with glibc checks, it took me a
while to realize this was a real regression and not a flaw in my
setup.)

MfG, JBG

-- 


signature.asc
Description: PGP signature


[PATCH] range-op: Implement floating point division fold_range [PR107569]

2022-11-11 Thread Jakub Jelinek via Gcc-patches
Hi!

Here is the floating point division fold_range implementation,
as I wrote in the last mail, we could outline some of the common parts
into static methods with descriptive names and share them between
foperator_div and foperator_mult.

Bootstrapped/regtested on top of the earlier version of the multiplication
fold_range on x86_64-linux and i686-linux, regressions are
+FAIL: gcc.dg/pr95115.c execution test
+FAIL: libphobos.phobos/std/math/hardware.d execution test
+FAIL: libphobos.phobos_shared/std/math/hardware.d execution test
The first test is we have:
  # RANGE [frange] double [] +-NAN
  _3 =  Inf /  Inf;
  if (_3 ord _3)
goto ; [INV]
  else
goto ; [INV]

   :
  abort ();

   :
before evrp, the range is correct, Inf / Inf is known NAN of unknown
sign.  evrp correctly folds _3 ord _3 into false and the
  _3 =  Inf /  Inf;
remains in the IL, but then comes dse1 and removes it as dead
statement.  So, I think yet another example of the PR107608 problems
where DCE? removes dead statements which raise floating point exceptions.
And -fno-delete-dead-exceptions doesn't help.

2022-11-11  Jakub Jelinek  

PR tree-optimization/107569
* range-op-float.cc (foperator_div): New class.
(floating_op_table::floating_op_table): Use foperator_div
for RDIV_EXPR.

--- gcc/range-op-float.cc.jj2022-11-10 12:31:57.987917289 +0100
+++ gcc/range-op-float.cc   2022-11-10 17:04:35.743056880 +0100
@@ -2027,6 +2027,183 @@ class foperator_mult : public range_oper
   }
 } fop_mult;
 
+class foperator_div : public range_operator_float
+{
+  void rv_fold (REAL_VALUE_TYPE , REAL_VALUE_TYPE , bool _nan,
+   tree type,
+   const REAL_VALUE_TYPE _lb,
+   const REAL_VALUE_TYPE _ub,
+   const REAL_VALUE_TYPE _lb,
+   const REAL_VALUE_TYPE _ub,
+   relation_kind) const final override
+  {
+// +-0.0 / +-0.0 or +-INF / +-INF is a known NAN.
+if ((real_iszero (_lb)
+&& real_iszero (_ub)
+&& real_iszero (_lb)
+&& real_iszero (_ub))
+   || (real_isinf (_lb)
+   && real_isinf (_ub, real_isneg (_lb))
+   && real_isinf (_lb)
+   && real_isinf (_ub, real_isneg (_lb
+  {
+   real_nan (, "", 0, TYPE_MODE (type));
+   ub = lb;
+   maybe_nan = true;
+   return;
+  }
+
+bool both_maybe_zero = false;
+bool both_maybe_inf = false;
+bool must_have_signbit_zero = false;
+bool must_have_signbit_nonzero = false;
+
+// If +-0.0 is in both ranges, it is a maybe NAN.
+if (real_compare (LE_EXPR, _lb, )
+   && real_compare (GE_EXPR, _ub, )
+   && real_compare (LE_EXPR, _lb, )
+   && real_compare (GE_EXPR, _ub, ))
+  {
+   both_maybe_zero = true;
+   maybe_nan = true;
+  }
+// If +-INF is in both ranges, it is a maybe NAN.
+else if ((real_isinf (_lb) || real_isinf (_ub))
+&& (real_isinf (_lb) || real_isinf (_ub)))
+  {
+   both_maybe_inf = true;
+   maybe_nan = true;
+  }
+else
+  maybe_nan = false;
+
+if (real_isneg (_lb) == real_isneg (_ub)
+   && real_isneg (_lb) == real_isneg (_ub))
+  {
+   if (real_isneg (_lb) == real_isneg (_ub))
+ must_have_signbit_zero = true;
+   else
+ must_have_signbit_nonzero = true;
+  }
+
+// If dividend must be zero, the range is just +-0
+// (including if the divisor is +-INF).
+// If divisor must be +-INF, the range is just +-0
+// (including if the dividend is zero).
+if ((real_iszero (_lb) && real_iszero (_ub))
+   || real_isinf (_lb, false)
+   || real_isinf (_ub, true))
+  {
+   ub = lb = dconst0;
+   // If all the boundary signs are the same, [+0.0, +0.0].
+   if (must_have_signbit_zero)
+ ;
+   // If divisor and dividend must have different signs,
+   // [-0.0, -0.0].
+   else if (must_have_signbit_nonzero)
+ ub = lb = real_value_negate ();
+   // Otherwise -> [-0.0, +0.0].
+   else
+ lb = real_value_negate ();
+   return;
+  }
+
+// If divisor must be zero, the range is just +-INF
+// (including if the dividend is +-INF).
+// If dividend must be +-INF, the range is just +-INF
+// (including if the dividend is zero).
+if ((real_iszero (_lb) && real_iszero (_ub))
+   || real_isinf (_lb, false)
+   || real_isinf (_ub, true))
+  {
+   // If all the boundary signs are the same, [+INF, +INF].
+   if (must_have_signbit_zero)
+ ub = lb = dconstinf;
+   // If divisor and dividend must have different signs,
+   // [-INF, -INF].
+   else if (must_have_signbit_nonzero)
+ ub = lb = dconstninf;
+   // Otherwise -> [-INF, +INF] (-INF or +INF).
+   else
+ {
+   lb = dconstninf;
+   ub = dconstinf;
+ }
+   return;
+  }
+
+// Otherwise if both operands may be zero, divisor could be
+//