diff mbox series

fold-const: do not fold 'inf/inf' with -ftrapping-math [PR95115]

Message ID 8b49a9906a0d1019bd877bf526f71ab5321550fe.camel@mengyan1223.wang
State New
Headers show
Series fold-const: do not fold 'inf/inf' with -ftrapping-math [PR95115] | expand

Commit Message

Xi Ruoyao Jan. 30, 2022, 6:39 p.m. UTC
'inf/inf' should raise an invalid operation exception at runtime.  So it
should not be folded during compilation unless -fno-trapping-math is
used.

gcc/
	PR middle-end/95115
	* fold-const.cc (const_binop): Do not fold "inf/inf".

gcc/testsuite
	* gcc.dg/pr95115.c: New test.
---
 gcc/fold-const.cc              |  8 ++++++++
 gcc/testsuite/gcc.dg/pr95115.c | 25 +++++++++++++++++++++++++
 2 files changed, 33 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/pr95115.c

Comments

Richard Biener Jan. 31, 2022, 9:35 a.m. UTC | #1
On Sun, Jan 30, 2022 at 7:39 PM Xi Ruoyao via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> 'inf/inf' should raise an invalid operation exception at runtime.  So it
> should not be folded during compilation unless -fno-trapping-math is
> used.

I wonder if it would make sense to handle it similar to

      inexact = real_arithmetic (&value, code, &d1, &d2);
      real_convert (&result, mode, &value);

      /* Don't constant fold this floating point operation if
         the result has overflowed and flag_trapping_math.  */
      if (flag_trapping_math
          && MODE_HAS_INFINITIES (mode)
          && REAL_VALUE_ISINF (result)
          && !REAL_VALUE_ISINF (d1)
          && !REAL_VALUE_ISINF (d2))
        return NULL_TREE;

thus whenever we fold to NaN but flag_trapping_math is set,
do not fold?  We already exclude NaN operands earlier
(but unconditionally return NaN even for NaN + 1. though
that should raise invalid as well, no?)

I suppose Joseph should know best what the correct semantics
are here.

Thanks,
Richard.

> gcc/
>         PR middle-end/95115
>         * fold-const.cc (const_binop): Do not fold "inf/inf".
>
> gcc/testsuite
>         * gcc.dg/pr95115.c: New test.
> ---
>  gcc/fold-const.cc              |  8 ++++++++
>  gcc/testsuite/gcc.dg/pr95115.c | 25 +++++++++++++++++++++++++
>  2 files changed, 33 insertions(+)
>  create mode 100644 gcc/testsuite/gcc.dg/pr95115.c
>
> diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
> index fd9c6352d4f..0e99d5a2e3d 100644
> --- a/gcc/fold-const.cc
> +++ b/gcc/fold-const.cc
> @@ -1283,6 +1283,14 @@ const_binop (enum tree_code code, tree arg1, tree arg2)
>           && (flag_trapping_math || ! MODE_HAS_INFINITIES (mode)))
>         return NULL_TREE;
>
> +      /* Don't perform "inf/inf", because it would raise an invalid
> +        operation exception (IEEE 754 section 7.2 clause d).  */
> +      if (code == RDIV_EXPR
> +         && flag_trapping_math
> +         && REAL_VALUE_ISINF (d1)
> +         && REAL_VALUE_ISINF (d2))
> +       return NULL_TREE;
> +
>        /* If either operand is a NaN, just return it.  Otherwise, set up
>          for floating-point trap; we return an overflow.  */
>        if (REAL_VALUE_ISNAN (d1))
> diff --git a/gcc/testsuite/gcc.dg/pr95115.c b/gcc/testsuite/gcc.dg/pr95115.c
> new file mode 100644
> index 00000000000..46a95dfb698
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/pr95115.c
> @@ -0,0 +1,25 @@
> +/* { dg-do run } */
> +/* { dg-options "-O2 -ftrapping-math" } */
> +/* { dg-add-options ieee } */
> +/* { dg-require-effective-target fenv_exceptions } */
> +
> +#include <fenv.h>
> +#include <stdlib.h>
> +
> +double
> +x (void)
> +{
> +  double d = __builtin_inf ();
> +  return d / d;
> +}
> +
> +int
> +main (void)
> +{
> +  double r = x ();
> +  if (!__builtin_isnan (r))
> +       abort ();
> +  if (!fetestexcept (FE_INVALID))
> +       abort ();
> +  exit (0);
> +}
> --
> 2.35.1
>
>
Xi Ruoyao Jan. 31, 2022, 10:07 a.m. UTC | #2
On Mon, 2022-01-31 at 10:35 +0100, Richard Biener wrote:
> On Sun, Jan 30, 2022 at 7:39 PM Xi Ruoyao via Gcc-patches
> <gcc-patches@gcc.gnu.org> wrote:
> > 
> > 'inf/inf' should raise an invalid operation exception at runtime. 
> > So it
> > should not be folded during compilation unless -fno-trapping-math is
> > used.
> 
> I wonder if it would make sense to handle it similar to
> 
>       inexact = real_arithmetic (&value, code, &d1, &d2);
>       real_convert (&result, mode, &value);
> 
>       /* Don't constant fold this floating point operation if
>          the result has overflowed and flag_trapping_math.  */
>       if (flag_trapping_math
>           && MODE_HAS_INFINITIES (mode)
>           && REAL_VALUE_ISINF (result)
>           && !REAL_VALUE_ISINF (d1)
>           && !REAL_VALUE_ISINF (d2))
>         return NULL_TREE;

> thus whenever we fold to NaN but flag_trapping_math is set,
> do not fold?

Like this?

       inexact = real_arithmetic (&value, code, &d1, &d2);
       real_convert (&result, mode, &value);
 
+      /* Don't constant fold this floating point operation if
+	 both operands are not NaN but the result is NaN, and
+	 flag_trapping_math.  Such operations should raise an
+	 invalid operation exception.  */
+      if (flag_trapping_math
+	  && MODE_HAS_NANS (mode)
+	  && REAL_VALUE_ISNAN (result)
+	  && !REAL_VALUE_ISNAN (d1)
+	  && !REAL_VALUE_ISNAN (d2))
+	return NULL_TREE;
+
       /* Don't constant fold this floating point operation if
 	 the result has overflowed and flag_trapping_math.  */
       if (flag_trapping_math

> We already exclude NaN operands earlier
> (but unconditionally return NaN even for NaN + 1. though
> that should raise invalid as well, no?)

qNaN + 1 (or even qNaN + qNaN) should produce a qNaN but should not
raise any exception. OTOH sNaN + 1 should produce a qNaN and raise an
invalid operation exception.  For sNaN operands we already ruled out the
const folding earlier:

>       /* Don't perform operation if we honor signaling NaNs and
>          either operand is a signaling NaN.  */
>       if (HONOR_SNANS (mode)
>           && (REAL_VALUE_ISSIGNALING_NAN (d1)
>               || REAL_VALUE_ISSIGNALING_NAN (d2)))
>         return NULL_TREE;

> I suppose Joseph should know best what the correct semantics
> are here.
> 
> Thanks,
> Richard.
> 
> > gcc/
> >         PR middle-end/95115
> >         * fold-const.cc (const_binop): Do not fold "inf/inf".
> > 
> > gcc/testsuite
> >         * gcc.dg/pr95115.c: New test.
> > ---
> >  gcc/fold-const.cc              |  8 ++++++++
> >  gcc/testsuite/gcc.dg/pr95115.c | 25 +++++++++++++++++++++++++
> >  2 files changed, 33 insertions(+)
> >  create mode 100644 gcc/testsuite/gcc.dg/pr95115.c
> > 
> > diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
> > index fd9c6352d4f..0e99d5a2e3d 100644
> > --- a/gcc/fold-const.cc
> > +++ b/gcc/fold-const.cc
> > @@ -1283,6 +1283,14 @@ const_binop (enum tree_code code, tree arg1,
> > tree arg2)
> >           && (flag_trapping_math || ! MODE_HAS_INFINITIES (mode)))
> >         return NULL_TREE;
> > 
> > +      /* Don't perform "inf/inf", because it would raise an invalid
> > +        operation exception (IEEE 754 section 7.2 clause d).  */
> > +      if (code == RDIV_EXPR
> > +         && flag_trapping_math
> > +         && REAL_VALUE_ISINF (d1)
> > +         && REAL_VALUE_ISINF (d2))
> > +       return NULL_TREE;
> > +
> >        /* If either operand is a NaN, just return it.  Otherwise,
> > set up
> >          for floating-point trap; we return an overflow.  */
> >        if (REAL_VALUE_ISNAN (d1))
> > diff --git a/gcc/testsuite/gcc.dg/pr95115.c
> > b/gcc/testsuite/gcc.dg/pr95115.c
> > new file mode 100644
> > index 00000000000..46a95dfb698
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/pr95115.c
> > @@ -0,0 +1,25 @@
> > +/* { dg-do run } */
> > +/* { dg-options "-O2 -ftrapping-math" } */
> > +/* { dg-add-options ieee } */
> > +/* { dg-require-effective-target fenv_exceptions } */
> > +
> > +#include <fenv.h>
> > +#include <stdlib.h>
> > +
> > +double
> > +x (void)
> > +{
> > +  double d = __builtin_inf ();
> > +  return d / d;
> > +}
> > +
> > +int
> > +main (void)
> > +{
> > +  double r = x ();
> > +  if (!__builtin_isnan (r))
> > +       abort ();
> > +  if (!fetestexcept (FE_INVALID))
> > +       abort ();
> > +  exit (0);
> > +}
> > --
> > 2.35.1
> > 
> >
Richard Biener Jan. 31, 2022, 2:57 p.m. UTC | #3
On Mon, Jan 31, 2022 at 11:07 AM Xi Ruoyao <xry111@mengyan1223.wang> wrote:
>
> On Mon, 2022-01-31 at 10:35 +0100, Richard Biener wrote:
> > On Sun, Jan 30, 2022 at 7:39 PM Xi Ruoyao via Gcc-patches
> > <gcc-patches@gcc.gnu.org> wrote:
> > >
> > > 'inf/inf' should raise an invalid operation exception at runtime.
> > > So it
> > > should not be folded during compilation unless -fno-trapping-math is
> > > used.
> >
> > I wonder if it would make sense to handle it similar to
> >
> >       inexact = real_arithmetic (&value, code, &d1, &d2);
> >       real_convert (&result, mode, &value);
> >
> >       /* Don't constant fold this floating point operation if
> >          the result has overflowed and flag_trapping_math.  */
> >       if (flag_trapping_math
> >           && MODE_HAS_INFINITIES (mode)
> >           && REAL_VALUE_ISINF (result)
> >           && !REAL_VALUE_ISINF (d1)
> >           && !REAL_VALUE_ISINF (d2))
> >         return NULL_TREE;
>
> > thus whenever we fold to NaN but flag_trapping_math is set,
> > do not fold?
>
> Like this?
>
>        inexact = real_arithmetic (&value, code, &d1, &d2);
>        real_convert (&result, mode, &value);
>
> +      /* Don't constant fold this floating point operation if
> +        both operands are not NaN but the result is NaN, and
> +        flag_trapping_math.  Such operations should raise an
> +        invalid operation exception.  */
> +      if (flag_trapping_math
> +         && MODE_HAS_NANS (mode)
> +         && REAL_VALUE_ISNAN (result)
> +         && !REAL_VALUE_ISNAN (d1)
> +         && !REAL_VALUE_ISNAN (d2))
> +       return NULL_TREE;
> +
>        /* Don't constant fold this floating point operation if
>          the result has overflowed and flag_trapping_math.  */
>        if (flag_trapping_math
>
> > We already exclude NaN operands earlier
> > (but unconditionally return NaN even for NaN + 1. though
> > that should raise invalid as well, no?)
>
> qNaN + 1 (or even qNaN + qNaN) should produce a qNaN but should not
> raise any exception. OTOH sNaN + 1 should produce a qNaN and raise an
> invalid operation exception.

Ah, I see.  So yes, like that.

Joseph, do you agree?

Thanks,
Richard.

>  For sNaN operands we already ruled out the
> const folding earlier:
>
> >       /* Don't perform operation if we honor signaling NaNs and
> >          either operand is a signaling NaN.  */
> >       if (HONOR_SNANS (mode)
> >           && (REAL_VALUE_ISSIGNALING_NAN (d1)
> >               || REAL_VALUE_ISSIGNALING_NAN (d2)))
> >         return NULL_TREE;
>
> > I suppose Joseph should know best what the correct semantics
> > are here.
> >
> > Thanks,
> > Richard.
> >
> > > gcc/
> > >         PR middle-end/95115
> > >         * fold-const.cc (const_binop): Do not fold "inf/inf".
> > >
> > > gcc/testsuite
> > >         * gcc.dg/pr95115.c: New test.
> > > ---
> > >  gcc/fold-const.cc              |  8 ++++++++
> > >  gcc/testsuite/gcc.dg/pr95115.c | 25 +++++++++++++++++++++++++
> > >  2 files changed, 33 insertions(+)
> > >  create mode 100644 gcc/testsuite/gcc.dg/pr95115.c
> > >
> > > diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
> > > index fd9c6352d4f..0e99d5a2e3d 100644
> > > --- a/gcc/fold-const.cc
> > > +++ b/gcc/fold-const.cc
> > > @@ -1283,6 +1283,14 @@ const_binop (enum tree_code code, tree arg1,
> > > tree arg2)
> > >           && (flag_trapping_math || ! MODE_HAS_INFINITIES (mode)))
> > >         return NULL_TREE;
> > >
> > > +      /* Don't perform "inf/inf", because it would raise an invalid
> > > +        operation exception (IEEE 754 section 7.2 clause d).  */
> > > +      if (code == RDIV_EXPR
> > > +         && flag_trapping_math
> > > +         && REAL_VALUE_ISINF (d1)
> > > +         && REAL_VALUE_ISINF (d2))
> > > +       return NULL_TREE;
> > > +
> > >        /* If either operand is a NaN, just return it.  Otherwise,
> > > set up
> > >          for floating-point trap; we return an overflow.  */
> > >        if (REAL_VALUE_ISNAN (d1))
> > > diff --git a/gcc/testsuite/gcc.dg/pr95115.c
> > > b/gcc/testsuite/gcc.dg/pr95115.c
> > > new file mode 100644
> > > index 00000000000..46a95dfb698
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.dg/pr95115.c
> > > @@ -0,0 +1,25 @@
> > > +/* { dg-do run } */
> > > +/* { dg-options "-O2 -ftrapping-math" } */
> > > +/* { dg-add-options ieee } */
> > > +/* { dg-require-effective-target fenv_exceptions } */
> > > +
> > > +#include <fenv.h>
> > > +#include <stdlib.h>
> > > +
> > > +double
> > > +x (void)
> > > +{
> > > +  double d = __builtin_inf ();
> > > +  return d / d;
> > > +}
> > > +
> > > +int
> > > +main (void)
> > > +{
> > > +  double r = x ();
> > > +  if (!__builtin_isnan (r))
> > > +       abort ();
> > > +  if (!fetestexcept (FE_INVALID))
> > > +       abort ();
> > > +  exit (0);
> > > +}
> > > --
> > > 2.35.1
> > >
> > >
>
> --
> Xi Ruoyao <xry111@mengyan1223.wang>
> School of Aerospace Science and Technology, Xidian University
Joseph Myers Jan. 31, 2022, 11:32 p.m. UTC | #4
On Mon, 31 Jan 2022, Richard Biener via Gcc-patches wrote:

> I wonder if it would make sense to handle it similar to
> 
>       inexact = real_arithmetic (&value, code, &d1, &d2);
>       real_convert (&result, mode, &value);
> 
>       /* Don't constant fold this floating point operation if
>          the result has overflowed and flag_trapping_math.  */
>       if (flag_trapping_math
>           && MODE_HAS_INFINITIES (mode)
>           && REAL_VALUE_ISINF (result)
>           && !REAL_VALUE_ISINF (d1)
>           && !REAL_VALUE_ISINF (d2))
>         return NULL_TREE;
> 
> thus whenever we fold to NaN but flag_trapping_math is set,
> do not fold?  We already exclude NaN operands earlier

Yes, it's true in general that a NaN result for non-NaN operands means 
"invalid" is raised.  While an infinite result for finite operands means 
either "overflow" (if inexact) or "divide by zero" (if exact).  
(Underflow is more complicated.)
diff mbox series

Patch

diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
index fd9c6352d4f..0e99d5a2e3d 100644
--- a/gcc/fold-const.cc
+++ b/gcc/fold-const.cc
@@ -1283,6 +1283,14 @@  const_binop (enum tree_code code, tree arg1, tree arg2)
 	  && (flag_trapping_math || ! MODE_HAS_INFINITIES (mode)))
 	return NULL_TREE;
 
+      /* Don't perform "inf/inf", because it would raise an invalid
+	 operation exception (IEEE 754 section 7.2 clause d).  */
+      if (code == RDIV_EXPR
+	  && flag_trapping_math
+	  && REAL_VALUE_ISINF (d1)
+	  && REAL_VALUE_ISINF (d2))
+	return NULL_TREE;
+
       /* If either operand is a NaN, just return it.  Otherwise, set up
 	 for floating-point trap; we return an overflow.  */
       if (REAL_VALUE_ISNAN (d1))
diff --git a/gcc/testsuite/gcc.dg/pr95115.c b/gcc/testsuite/gcc.dg/pr95115.c
new file mode 100644
index 00000000000..46a95dfb698
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr95115.c
@@ -0,0 +1,25 @@ 
+/* { dg-do run } */
+/* { dg-options "-O2 -ftrapping-math" } */
+/* { dg-add-options ieee } */
+/* { dg-require-effective-target fenv_exceptions } */
+
+#include <fenv.h>
+#include <stdlib.h>
+
+double
+x (void)
+{
+  double d = __builtin_inf ();
+  return d / d;
+}
+
+int
+main (void)
+{
+  double r = x ();
+  if (!__builtin_isnan (r))
+	abort ();
+  if (!fetestexcept (FE_INVALID))
+	abort ();
+  exit (0);
+}