[3/6] MIPS/math: Implement optimized issignaling(f)

Message ID 20240513081429.1749898-4-syq@gcc.gnu.org
State New
Headers
Series MIPS: Improve math |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Testing passed

Commit Message

YunQiang Su May 13, 2024, 8:14 a.m. UTC
  MIPSr6 introduces class.fmt instructions, which can help us to
determine whether a number is sNAN.

We define __mips_issignaling(f) as always inline in mips/math_private.h,
and call them in s_issignaling(f).c.  Issignaling operation is also used
by some other functions, such as fmax.  Inlining it can introduce better
codesize and performance, due to libcall may issue some stack operations.

	* sysdeps/mips/fpu_control.h: Define FCLASS constants.
	* sysdeps/mips/ieee754/s_issignaling.c
	* sysdeps/mips/ieee754/s_issignalingf.c
	* sysdeps/mips/math_private.h: Define __mips_issignaling(f).

Signed-off-by: YunQiang Su <syq@gcc.gnu.org>
---
 sysdeps/mips/fpu_control.h            | 17 +++++++
 sysdeps/mips/ieee754/s_issignaling.c  | 28 ++++++++++++
 sysdeps/mips/ieee754/s_issignalingf.c | 27 +++++++++++
 sysdeps/mips/math_private.h           | 65 +++++++++++++++++++++++++++
 4 files changed, 137 insertions(+)
 create mode 100644 sysdeps/mips/ieee754/s_issignaling.c
 create mode 100644 sysdeps/mips/ieee754/s_issignalingf.c
  

Comments

Adhemerval Zanella Netto May 23, 2024, 2:37 p.m. UTC | #1
On 13/05/24 05:14, YunQiang Su wrote:
> MIPSr6 introduces class.fmt instructions, which can help us to
> determine whether a number is sNAN.
> 
> We define __mips_issignaling(f) as always inline in mips/math_private.h,
> and call them in s_issignaling(f).c.  Issignaling operation is also used
> by some other functions, such as fmax.  Inlining it can introduce better
> codesize and performance, due to libcall may issue some stack operations.

We used to provide such internal inlines optimizations, which requires a lot 
of boilerplate code to inline alternative implementation of C standard symbols 
(such as isnan/isinf).  This tend of get really complicate to maintain, and it 
leads to worse outcomes once compilers start to optimize it way (by providing
proper builtins) since we need to either reimplement internally the symbol
or use a different name (that compiler can not map to the builtin).

Also recently, we started to use builtins more to implement such math symbol
(check the math-use-builtins-*), so the idea would to add support for the builtin
use on both dbl-64 and flt-32 issignaling, and enable it to use if compiler supports
it (afaik it was enabled as arch-independent way on gcc 13). Another possible
optimization is also to add a similar enablement on math/math.h, where if compiler
supports also route issignaling to its builtin (similar to isnan/isinf/etc.).

Then you can focus on optimizing the compiler code generation of issignaling
to the emit the class.s.

With all these in place there will be no need to internally re-implement the
symbol and it would be future-proof regarding compiler optimizations.

> 
> 	* sysdeps/mips/fpu_control.h: Define FCLASS constants.
> 	* sysdeps/mips/ieee754/s_issignaling.c
> 	* sysdeps/mips/ieee754/s_issignalingf.c
> 	* sysdeps/mips/math_private.h: Define __mips_issignaling(f).
> 
> Signed-off-by: YunQiang Su <syq@gcc.gnu.org>
> ---
>  sysdeps/mips/fpu_control.h            | 17 +++++++
>  sysdeps/mips/ieee754/s_issignaling.c  | 28 ++++++++++++
>  sysdeps/mips/ieee754/s_issignalingf.c | 27 +++++++++++
>  sysdeps/mips/math_private.h           | 65 +++++++++++++++++++++++++++
>  4 files changed, 137 insertions(+)
>  create mode 100644 sysdeps/mips/ieee754/s_issignaling.c
>  create mode 100644 sysdeps/mips/ieee754/s_issignalingf.c
> 
> diff --git a/sysdeps/mips/fpu_control.h b/sysdeps/mips/fpu_control.h
> index 3ceb34fc25..086293117e 100644
> --- a/sysdeps/mips/fpu_control.h
> +++ b/sysdeps/mips/fpu_control.h
> @@ -127,6 +127,23 @@ extern void __mips_fpu_setcw (fpu_control_t) __THROW;
>  /* Default control word set at startup.  */
>  extern fpu_control_t __fpu_control;
>  
> +#  define _FCLASS_SNAN (1 << 0)
> +#  define _FCLASS_QNAN (1 << 1)
> +#  define _FCLASS_MINF (1 << 2)
> +#  define _FCLASS_MNORM (1 << 3)
> +#  define _FCLASS_MSUBNORM (1 << 4)
> +#  define _FCLASS_MZERO (1 << 5)
> +#  define _FCLASS_PINF (1 << 6)
> +#  define _FCLASS_PNORM (1 << 7)
> +#  define _FCLASS_PSUBNORM (1 << 8)
> +#  define _FCLASS_PZERO (1 << 9)
> +
> +#  define _FCLASS_ZERO (_FCLASS_MZERO | _FCLASS_PZERO)
> +#  define _FCLASS_SUBNORM (_FCLASS_MSUBNORM | _FCLASS_PSUBNORM)
> +#  define _FCLASS_NORM (_FCLASS_MNORM | _FCLASS_PNORM)
> +#  define _FCLASS_INF (_FCLASS_MINF | _FCLASS_PINF)
> +#  define _FCLASS_NAN (_FCLASS_SNAN | _FCLASS_QNAN)
> +
>  #endif /* __mips_soft_float */
>  
>  #endif	/* fpu_control.h */
> diff --git a/sysdeps/mips/ieee754/s_issignaling.c b/sysdeps/mips/ieee754/s_issignaling.c
> new file mode 100644
> index 0000000000..3bf65f07a5
> --- /dev/null
> +++ b/sysdeps/mips/ieee754/s_issignaling.c
> @@ -0,0 +1,28 @@
> +/* issignaling().  MIPS version.
> +   Copyright (C) 2024 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library.  If not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <math.h>
> +#include <fenv_private.h>
> +#include <math_private.h>
> +
> +int
> +__issignaling (double x)
> +{
> +  return __mips_issignaling (x);
> +}
> +libm_hidden_def (__issignaling)
> diff --git a/sysdeps/mips/ieee754/s_issignalingf.c b/sysdeps/mips/ieee754/s_issignalingf.c
> new file mode 100644
> index 0000000000..14863595bc
> --- /dev/null
> +++ b/sysdeps/mips/ieee754/s_issignalingf.c
> @@ -0,0 +1,27 @@
> +/* issignalingf().  MIPS version.
> +   Copyright (C) 2024 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library.  If not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <math.h>
> +#include <math_private.h>
> +
> +int
> +__issignalingf (float x)
> +{
> +  return __mips_issignalingf (x);
> +}
> +libm_hidden_def (__issignalingf)
> diff --git a/sysdeps/mips/math_private.h b/sysdeps/mips/math_private.h
> index 4da8b0c2d9..6c388ddb64 100644
> --- a/sysdeps/mips/math_private.h
> +++ b/sysdeps/mips/math_private.h
> @@ -53,4 +53,69 @@
>  #  endif
>  #endif /* __mips_hard_float && !__mips_single_float */
>  
> +/* Copy from sysdeps/ieee754/flt-32/s_issignalingf.c.
> +   Function call can introduce lots for stack operations.  Inline can even
> +   reduce codesize.  */
> +static __always_inline int
> +__mips_issignalingf (float x)
> +{
> +#if __mips_isa_rev >= 6 && defined(__mips_hard_float)
> +  float c;
> +  int ret;
> +  asm volatile("class.s %0, %1" : "=f"(c) : "f"(x));
> +  asm volatile("mfc1 %0, %1" : "=r"(ret) : "f"(c));
> +  return ret & _FCLASS_SNAN;
> +#else
> +  uint32_t xi;
> +  GET_FLOAT_WORD (xi, x);
> +#  if HIGH_ORDER_BIT_IS_SET_FOR_SNAN
> +  /* We only have to care about the high-order bit of x's significand, because
> +     having it set (sNaN) already makes the significand different from that
> +     used to designate infinity.  */
> +  return (xi & 0x7fc00000) == 0x7fc00000;
> +#  else
> +  /* To keep the following comparison simple, toggle the quiet/signaling bit,
> +     so that it is set for sNaNs.  This is inverse to IEEE 754-2008 (as well as
> +     common practice for IEEE 754-1985).  */
> +  xi ^= 0x00400000;
> +  /* We have to compare for greater (instead of greater or equal), because x's
> +     significand being all-zero designates infinity not NaN.  */
> +  return (xi & 0x7fffffff) > 0x7fc00000;
> +#  endif
> +#endif
> +}
> +
> +/* Copy from sysdeps/ieee754/dbl-64/s_issignaling.c.
> +   Function call can introduce lots for stack operations.  Inline can even
> +   reduce codesize.  */
> +static __always_inline int
> +__mips_issignaling (double x)
> +{
> +#if __mips_isa_rev >= 6 && defined(__mips_hard_float)                         \
> +    && !defined(__mips_single_float)
> +  double c;
> +  int ret;
> +  asm volatile("class.d %0, %1" : "=f"(c) : "f"(x));
> +  asm volatile("mfc1 %0, %1" : "=r"(ret) : "f"(c));
> +  return ret & _FCLASS_SNAN;
> +#else
> +  uint32_t xi;
> +  GET_HIGH_WORD (xi, x);
> +#  if HIGH_ORDER_BIT_IS_SET_FOR_SNAN
> +  /* We only have to care about the high-order bit of x's significand, because
> +     having it set (sNaN) already makes the significand different from that
> +     used to designate infinity.  */
> +  return (xi & UINT32_C (0x7ff80000)) == UINT32_C (0x7ff80000);
> +#  else
> +  /* To keep the following comparison simple, toggle the quiet/signaling bit,
> +     so that it is set for sNaNs.  This is inverse to IEEE 754-2008 (as well as
> +     common practice for IEEE 754-1985).  */
> +  xi ^= UINT32_C (0x00080000);
> +  /* We have to compare for greater (instead of greater or equal), because x's
> +     significand being all-zero designates infinity not NaN.  */
> +  return (xi & UINT32_C (0x7fffffff)) > UINT32_C (0x7ff80000);
> +#  endif
> +#endif
> +}
> +
>  #endif /* MIPS_MATH_PRIVATE_H */
  

Patch

diff --git a/sysdeps/mips/fpu_control.h b/sysdeps/mips/fpu_control.h
index 3ceb34fc25..086293117e 100644
--- a/sysdeps/mips/fpu_control.h
+++ b/sysdeps/mips/fpu_control.h
@@ -127,6 +127,23 @@  extern void __mips_fpu_setcw (fpu_control_t) __THROW;
 /* Default control word set at startup.  */
 extern fpu_control_t __fpu_control;
 
+#  define _FCLASS_SNAN (1 << 0)
+#  define _FCLASS_QNAN (1 << 1)
+#  define _FCLASS_MINF (1 << 2)
+#  define _FCLASS_MNORM (1 << 3)
+#  define _FCLASS_MSUBNORM (1 << 4)
+#  define _FCLASS_MZERO (1 << 5)
+#  define _FCLASS_PINF (1 << 6)
+#  define _FCLASS_PNORM (1 << 7)
+#  define _FCLASS_PSUBNORM (1 << 8)
+#  define _FCLASS_PZERO (1 << 9)
+
+#  define _FCLASS_ZERO (_FCLASS_MZERO | _FCLASS_PZERO)
+#  define _FCLASS_SUBNORM (_FCLASS_MSUBNORM | _FCLASS_PSUBNORM)
+#  define _FCLASS_NORM (_FCLASS_MNORM | _FCLASS_PNORM)
+#  define _FCLASS_INF (_FCLASS_MINF | _FCLASS_PINF)
+#  define _FCLASS_NAN (_FCLASS_SNAN | _FCLASS_QNAN)
+
 #endif /* __mips_soft_float */
 
 #endif	/* fpu_control.h */
diff --git a/sysdeps/mips/ieee754/s_issignaling.c b/sysdeps/mips/ieee754/s_issignaling.c
new file mode 100644
index 0000000000..3bf65f07a5
--- /dev/null
+++ b/sysdeps/mips/ieee754/s_issignaling.c
@@ -0,0 +1,28 @@ 
+/* issignaling().  MIPS version.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library.  If not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <math.h>
+#include <fenv_private.h>
+#include <math_private.h>
+
+int
+__issignaling (double x)
+{
+  return __mips_issignaling (x);
+}
+libm_hidden_def (__issignaling)
diff --git a/sysdeps/mips/ieee754/s_issignalingf.c b/sysdeps/mips/ieee754/s_issignalingf.c
new file mode 100644
index 0000000000..14863595bc
--- /dev/null
+++ b/sysdeps/mips/ieee754/s_issignalingf.c
@@ -0,0 +1,27 @@ 
+/* issignalingf().  MIPS version.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library.  If not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <math.h>
+#include <math_private.h>
+
+int
+__issignalingf (float x)
+{
+  return __mips_issignalingf (x);
+}
+libm_hidden_def (__issignalingf)
diff --git a/sysdeps/mips/math_private.h b/sysdeps/mips/math_private.h
index 4da8b0c2d9..6c388ddb64 100644
--- a/sysdeps/mips/math_private.h
+++ b/sysdeps/mips/math_private.h
@@ -53,4 +53,69 @@ 
 #  endif
 #endif /* __mips_hard_float && !__mips_single_float */
 
+/* Copy from sysdeps/ieee754/flt-32/s_issignalingf.c.
+   Function call can introduce lots for stack operations.  Inline can even
+   reduce codesize.  */
+static __always_inline int
+__mips_issignalingf (float x)
+{
+#if __mips_isa_rev >= 6 && defined(__mips_hard_float)
+  float c;
+  int ret;
+  asm volatile("class.s %0, %1" : "=f"(c) : "f"(x));
+  asm volatile("mfc1 %0, %1" : "=r"(ret) : "f"(c));
+  return ret & _FCLASS_SNAN;
+#else
+  uint32_t xi;
+  GET_FLOAT_WORD (xi, x);
+#  if HIGH_ORDER_BIT_IS_SET_FOR_SNAN
+  /* We only have to care about the high-order bit of x's significand, because
+     having it set (sNaN) already makes the significand different from that
+     used to designate infinity.  */
+  return (xi & 0x7fc00000) == 0x7fc00000;
+#  else
+  /* To keep the following comparison simple, toggle the quiet/signaling bit,
+     so that it is set for sNaNs.  This is inverse to IEEE 754-2008 (as well as
+     common practice for IEEE 754-1985).  */
+  xi ^= 0x00400000;
+  /* We have to compare for greater (instead of greater or equal), because x's
+     significand being all-zero designates infinity not NaN.  */
+  return (xi & 0x7fffffff) > 0x7fc00000;
+#  endif
+#endif
+}
+
+/* Copy from sysdeps/ieee754/dbl-64/s_issignaling.c.
+   Function call can introduce lots for stack operations.  Inline can even
+   reduce codesize.  */
+static __always_inline int
+__mips_issignaling (double x)
+{
+#if __mips_isa_rev >= 6 && defined(__mips_hard_float)                         \
+    && !defined(__mips_single_float)
+  double c;
+  int ret;
+  asm volatile("class.d %0, %1" : "=f"(c) : "f"(x));
+  asm volatile("mfc1 %0, %1" : "=r"(ret) : "f"(c));
+  return ret & _FCLASS_SNAN;
+#else
+  uint32_t xi;
+  GET_HIGH_WORD (xi, x);
+#  if HIGH_ORDER_BIT_IS_SET_FOR_SNAN
+  /* We only have to care about the high-order bit of x's significand, because
+     having it set (sNaN) already makes the significand different from that
+     used to designate infinity.  */
+  return (xi & UINT32_C (0x7ff80000)) == UINT32_C (0x7ff80000);
+#  else
+  /* To keep the following comparison simple, toggle the quiet/signaling bit,
+     so that it is set for sNaNs.  This is inverse to IEEE 754-2008 (as well as
+     common practice for IEEE 754-1985).  */
+  xi ^= UINT32_C (0x00080000);
+  /* We have to compare for greater (instead of greater or equal), because x's
+     significand being all-zero designates infinity not NaN.  */
+  return (xi & UINT32_C (0x7fffffff)) > UINT32_C (0x7ff80000);
+#  endif
+#endif
+}
+
 #endif /* MIPS_MATH_PRIVATE_H */