diff mbox series

[1/9] PowerPC: Map long double built-in functions if IEEE 128-bit long double.

Message ID 20200924203159.GA31597@ibm-toto.the-meissners.org
State New
Headers show
Series PowerPC: Patches to enable changing the long double default to IEEE 128-bit on little endian PowerPC 64-bit Linux systems | expand

Commit Message

Michael Meissner Sept. 24, 2020, 8:31 p.m. UTC
PowerPC: Map long double built-in functions if IEEE 128-bit long double.

This patch goes through the built-in functions and changes the name that is
used to the name used for __float128 and _Float128 support in glibc if the
PowerPC long double type is IEEE 128-bit instead of IBM extended double.

Normally the mapping is done in the math.h and stdio.h files.  However, not
everybody uses these files, which means we also need to change the external
name for the built-in function within the compiler.

In addition, changing the name in GCC allows the Fortran compiler to
automatically use the correct name.

To map the math functions, typically this patch changes <name>l to <name>f128.
However there are some exceptions that are handled with this patch.

To map the printf functions, <name> is mapped to __<name>ieee128.

To map the scanf functions, <name> is mapped to __isoc99<name>ieee128.

gcc/
2020-09-23  Michael Meissner  <meissner@linux.ibm.com>

	* config/rs6000/rs6000.c (rs6000_mangle_decl_assembler_name): Add
	support for mapping built-in function names for long double
	built-in functions if long double is IEEE 128-bit.

gcc/testsuite/
2020-09-23  Michael Meissner  <meissner@linux.ibm.com>

	* gcc.target/powerpc/float128-longdouble-math.c: New test.
	* gcc.target/powerpc/float128-longdouble-stdio.c: New test.
---
 gcc/config/rs6000/rs6000.c                    | 153 ++++-
 .../powerpc/float128-longdouble-math.c        | 559 ++++++++++++++++++
 .../powerpc/float128-longdouble-stdio.c       |  37 ++
 3 files changed, 718 insertions(+), 31 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/powerpc/float128-longdouble-math.c
 create mode 100644 gcc/testsuite/gcc.target/powerpc/float128-longdouble-stdio.c

Comments

Joseph Myers Oct. 1, 2020, 11:05 p.m. UTC | #1
On Thu, 24 Sep 2020, Michael Meissner via Gcc-patches wrote:

> To map the math functions, typically this patch changes <name>l to <name>f128.
> However there are some exceptions that are handled with this patch.

glibc 2.32 added __*ieee128 names for the *f128 functions, to allow the 
long double functions to be called in a namespace-clean way (when the 
defined feature test macros do not enable the TS 18661-3 function names). 
So I think GCC should also prefer to map to those names where possible, 
rather than the *f128 names.
Michael Meissner Oct. 8, 2020, 5:20 a.m. UTC | #2
On Thu, Oct 01, 2020 at 11:05:04PM +0000, Joseph Myers wrote:
> On Thu, 24 Sep 2020, Michael Meissner via Gcc-patches wrote:
> 
> > To map the math functions, typically this patch changes <name>l to <name>f128.
> > However there are some exceptions that are handled with this patch.
> 
> glibc 2.32 added __*ieee128 names for the *f128 functions, to allow the 
> long double functions to be called in a namespace-clean way (when the 
> defined feature test macros do not enable the TS 18661-3 function names). 
> So I think GCC should also prefer to map to those names where possible, 
> rather than the *f128 names.

Good idea.  Thanks.
diff mbox series

Patch

diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c
index b589f4566c2..0ff0f31d552 100644
--- a/gcc/config/rs6000/rs6000.c
+++ b/gcc/config/rs6000/rs6000.c
@@ -26909,56 +26909,147 @@  rs6000_globalize_decl_name (FILE * stream, tree decl)
    library before you can switch the real*16 type at compile time.
 
    We use the TARGET_MANGLE_DECL_ASSEMBLER_NAME hook to change this name.  We
-   only do this if the default is that long double is IBM extended double, and
-   the user asked for IEEE 128-bit.  */
+   only do this transformation if the __float128 type is enabled.  This
+   prevents us from doing the transformation on older 32-bit ports that might
+   have enabled using IEEE 128-bit floating point as the default long double
+   type.  */
 
 static tree
 rs6000_mangle_decl_assembler_name (tree decl, tree id)
 {
-  if (!TARGET_IEEEQUAD_DEFAULT && TARGET_IEEEQUAD && TARGET_LONG_DOUBLE_128
-      && TREE_CODE (decl) == FUNCTION_DECL && DECL_IS_BUILTIN (decl) )
+  if (TARGET_FLOAT128_TYPE && TARGET_IEEEQUAD && TARGET_LONG_DOUBLE_128
+      && TREE_CODE (decl) == FUNCTION_DECL
+      && fndecl_built_in_p (decl, BUILT_IN_NORMAL))
     {
       size_t len = IDENTIFIER_LENGTH (id);
       const char *name = IDENTIFIER_POINTER (id);
+      const char *newname = NULL;
 
-      if (name[len - 1] == 'l')
+      /* See if it is one of the built-in functions with an unusual name.  */
+      switch (DECL_FUNCTION_CODE (decl))
 	{
-	  bool uses_ieee128_p = false;
-	  tree type = TREE_TYPE (decl);
-	  machine_mode ret_mode = TYPE_MODE (type);
+	default:
+	  break;
 
-	  /* See if the function returns a IEEE 128-bit floating point type or
-	     complex type.  */
-	  if (ret_mode == TFmode || ret_mode == TCmode)
-	    uses_ieee128_p = true;
-	  else
+	case BUILT_IN_DREML:
+	  newname = "remainderf128";
+	  break;
+
+	case BUILT_IN_GAMMAL:
+	  newname = "lgammaf128";
+	  break;
+
+	case BUILT_IN_GAMMAL_R:
+	case BUILT_IN_LGAMMAL_R:
+	  newname = "__lgammaieee128_r";
+	  break;
+
+	case BUILT_IN_NEXTTOWARD:
+	  newname = "__nexttoward_to_ieee128";
+	  break;
+
+	case BUILT_IN_NEXTTOWARDF:
+	  newname = "__nexttowardf_to_ieee128";
+	  break;
+
+	case BUILT_IN_NEXTTOWARDL:
+	  newname = "__nexttowardieee128";
+	  break;
+
+	case BUILT_IN_POW10L:
+	  newname = "exp10f128";
+	  break;
+
+	case BUILT_IN_SCALBL:
+	  newname = "__scalbnieee128";
+	  break;
+
+	case BUILT_IN_SIGNIFICANDL:
+	  newname = "__significandieee128";
+	  break;
+
+	case BUILT_IN_SINCOSL:
+	  newname = "__sincosieee128";
+	  break;
+	}
+
+      /* Update the __builtin_*printf && __builtin_*scanf functions.  */
+      if (!newname)
+	{
+	  const size_t printf_len = sizeof ("printf") - 1;
+	  const size_t scanf_len = sizeof ("scanf") - 1;
+	  const size_t printf_extra
+	    = sizeof ("__") - 1 + sizeof ("ieee128") - 1;
+	  const size_t scanf_extra
+	    = sizeof ("__isoc99_") - 1 + sizeof ("ieee128") - 1;
+
+	  if (len >= printf_len
+	      && strcmp (name + len - printf_len, "printf") == 0)
 	    {
-	      function_args_iterator args_iter;
-	      tree arg;
+	      char *name2 = (char *) alloca (len + 1 + printf_extra);
+	      strcpy (name2, "__");
+	      memcpy (name2 + 2, name, len);
+	      strcpy (name2 + 2 + len, "ieee128");
+	      newname = (const char *) name2;
+	    }
 
-	      /* See if the function passes a IEEE 128-bit floating point type
-		 or complex type.  */
-	      FOREACH_FUNCTION_ARGS (type, arg, args_iter)
+	  else if (len >= scanf_len
+		   && strcmp (name + len - scanf_len, "scanf") == 0)
+	    {
+	      char *name2 = (char *) alloca (len + 1 + scanf_extra);
+	      strcpy (name2, "__isoc99_");
+	      memcpy (name2 + sizeof ("__isoc99") - 1, name, len);
+	      strcpy (name2 + sizeof ("__isoc99") - 1 + len, "ieee128");
+	      newname = (const char *) name2;
+	    }
+
+	  else if (name[len - 1] == 'l')
+	    {
+	      bool uses_ieee128_p = false;
+	      tree type = TREE_TYPE (decl);
+	      machine_mode ret_mode = TYPE_MODE (type);
+
+	      /* See if the function returns a IEEE 128-bit floating point type or
+		 complex type.  */
+	      if (ret_mode == TFmode || ret_mode == TCmode)
+		uses_ieee128_p = true;
+	      else
 		{
-		  machine_mode arg_mode = TYPE_MODE (arg);
-		  if (arg_mode == TFmode || arg_mode == TCmode)
+		  function_args_iterator args_iter;
+		  tree arg;
+
+		  /* See if the function passes a IEEE 128-bit floating point type
+		     or complex type.  */
+		  FOREACH_FUNCTION_ARGS (type, arg, args_iter)
 		    {
-		      uses_ieee128_p = true;
-		      break;
+		      machine_mode arg_mode = TYPE_MODE (arg);
+		      if (arg_mode == TFmode || arg_mode == TCmode)
+			{
+			  uses_ieee128_p = true;
+			  break;
+			}
 		    }
 		}
-	    }
 
-	  /* If we passed or returned an IEEE 128-bit floating point type,
-	     change the name.  */
-	  if (uses_ieee128_p)
-	    {
-	      char *name2 = (char *) alloca (len + 4);
-	      memcpy (name2, name, len - 1);
-	      strcpy (name2 + len - 1, "f128");
-	      id = get_identifier (name2);
+	      /* If we passed or returned an IEEE 128-bit floating point type,
+		 change the name.  */
+	      if (uses_ieee128_p)
+		{
+		  char *name2 = (char *) alloca (len + 4);
+		  memcpy (name2, name, len - 1);
+		  strcpy (name2 + len - 1, "f128");
+		  newname = (const char *) name2;
+		}
 	    }
 	}
+
+      if (newname)
+	{
+	  if (TARGET_DEBUG_BUILTIN)
+	    fprintf (stderr, "Map %s => %s\n", name, newname);
+
+	  id = get_identifier (newname);
+	}
     }
 
   return id;
diff --git a/gcc/testsuite/gcc.target/powerpc/float128-longdouble-math.c b/gcc/testsuite/gcc.target/powerpc/float128-longdouble-math.c
new file mode 100644
index 00000000000..e7a79f5e4e9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/float128-longdouble-math.c
@@ -0,0 +1,559 @@ 
+/* { dg-require-effective-target ppc_float128_hw } */
+/* { dg-require-effective-target power10_ok } */
+/* { dg-options "-mdejagnu-cpu=power9 -mno-pcrel -O2 -Wno-psabi -mabi=ieeelongdouble" } */
+
+/* Test if switching long double to IEEE 128-bit maps all of the math built-in
+   function names correctly.  We explicitly turn off PC-relative support to
+   make it simpler to compare the call without having a @notoc qualifier.  */
+
+/* Debugging support to use 'name' instead of '__builtin_name'.  Note if you
+   enable this, you will likely need additional flags to get all of the
+   functions defined.  */
+#ifdef DO_FUNC
+#ifndef DO_MATH_H
+#define DO_MATH_H	1
+#endif
+
+#define BUILTIN0(FUNC)                   FUNC ()
+#define BUILTIN1(FUNC, ARG1)             FUNC (ARG1)
+#define BUILTIN2(FUNC, ARG1, ARG2)       FUNC (ARG1, ARG2)
+#define BUILTIN3(FUNC, ARG1, ARG2, ARG3) FUNC (ARG1, ARG2, ARG3)
+
+#else
+#define BUILTIN0(FUNC)                   __builtin_ ## FUNC ()
+#define BUILTIN1(FUNC, ARG1)             __builtin_ ## FUNC (ARG1)
+#define BUILTIN2(FUNC, ARG1, ARG2)       __builtin_ ## FUNC (ARG1, ARG2)
+#define BUILTIN3(FUNC, ARG1, ARG2, ARG3) __builtin_ ## FUNC (ARG1, ARG2, ARG3)
+#endif
+
+/* Debugging support to compare using math.h with the raw built-in functions.  */
+#ifdef DO_MATH_H
+#define __STDC_WANT_IEC_60559_TYPES_EXT__	1
+#define __STDC_WANT_IEC_60559_FUNCS_EXT__	1
+#define _GNU_SOURCE				1
+#define _XOPEN_SOURCE				1
+
+#include <math.h>
+#include <complex.h>
+#endif
+
+/* Built-in functions that returns a long double and take one long double
+   argument.  */
+
+void
+return_ld_arg_ld (long double *p,
+		  long double *q)
+{
+  /* { dg-final { scan-assembler {\macoshf128\M} } }  */
+  *p++ = BUILTIN1 (acoshl, *q++);
+
+  /* { dg-final { scan-assembler {\macosf128\M} } }  */
+  *p++ = BUILTIN1 (acosl, *q++);
+
+  /* { dg-final { scan-assembler {\masinhf128\M} } }  */
+  *p++ = BUILTIN1 (asinhl, *q++);
+
+  /* { dg-final { scan-assembler {\masinf128\M} } }  */
+  *p++ = BUILTIN1 (asinl, *q++);
+
+  /* { dg-final { scan-assembler {\matanhf128\M} } }  */
+  *p++ = BUILTIN1 (atanhl, *q++);
+
+  /* { dg-final { scan-assembler {\matanf128\M} } }  */
+  *p++ = BUILTIN1 (atanl, *q++);
+
+  /* { dg-final { scan-assembler {\mcbrtf128\M} } }  */
+  *p++ = BUILTIN1 (cbrtl, *q++);
+
+  /* inline code.  */
+  *p++ = BUILTIN1 (ceill, *q++);
+
+  /* { dg-final { scan-assembler {\mcoshf128\M} } }  */
+  *p++ = BUILTIN1 (coshl, *q++);
+
+  /* { dg-final { scan-assembler {\mcosf128\M} } }  */
+  *p++ = BUILTIN1 (cosl, *q++);
+
+  /* { dg-final { scan-assembler {\merfcf128\M} } }  */
+  *p++ = BUILTIN1 (erfcl, *q++);
+
+  /* { dg-final { scan-assembler {\merff128\M} } }  */
+  *p++ = BUILTIN1 (erfl, *q++);
+
+  /* { dg-final { scan-assembler {\mexp10f128\M} } }  */
+  *p++ = BUILTIN1 (exp10l, *q++);
+
+  /* { dg-final { scan-assembler {\mexp2f128\M} } }  */
+  *p++ = BUILTIN1 (exp2l, *q++);
+
+  /* { dg-final { scan-assembler {\mexpf128\M} } }  */
+  *p++ = BUILTIN1 (expl, *q++);
+
+  /* { dg-final { scan-assembler {\mexpm1f128\M} } }  */
+  *p++ = BUILTIN1 (expm1l, *q++);
+
+  /* inline code.  */
+  *p++ = BUILTIN1 (fabsl, *q++);
+
+  /* inline code.  */
+  *p++ = BUILTIN1 (floorl, *q++);
+
+  /* { dg-final { scan-assembler {\mlgammaf128\M} } }  */
+  *p++ = BUILTIN1 (gammal, *q++);
+
+  /* { dg-final { scan-assembler {\mj0f128\M} } }  */
+  *p++ = BUILTIN1 (j0l, *q++);
+
+  /* { dg-final { scan-assembler {\mj1f128\M} } }  */
+  *p++ = BUILTIN1 (j1l, *q++);
+
+  /* lgammaf128 mentioned previously.  */
+  *p++ = BUILTIN1 (lgammal, *q++);
+
+  /* { dg-final { scan-assembler {\mlog10f128\M} } }  */
+  *p++ = BUILTIN1 (log10l, *q++);
+
+  /* { dg-final { scan-assembler {\mlog1pf128\M} } }  */
+  *p++ = BUILTIN1 (log1pl, *q++);
+
+  /* { dg-final { scan-assembler {\mlog2f128\M} } }  */
+  *p++ = BUILTIN1 (log2l, *q++);
+
+  /* { dg-final { scan-assembler {\mlogbf128\M} } }  */
+  *p++ = BUILTIN1 (logbl, *q++);
+
+  /* { dg-final { scan-assembler {\mlogf128\M} } }  */
+  *p++ = BUILTIN1 (logl, *q++);
+
+  /* { dg-final { scan-assembler {\mnearbyintf128\M} } }  */
+  *p++ = BUILTIN1 (nearbyintl, *q++);
+
+  /* { dg-final { scan-assembler {\mexp10f128\M} } }  */
+  *p++ = BUILTIN1 (pow10l, *q++);
+
+  /* { dg-final { scan-assembler {\mrintf128\M} } }  */
+  *p++ = BUILTIN1 (rintl, *q++);
+
+  /* { dg-final { scan-assembler {\mroundevenf128\M} } }  */
+  *p++ = BUILTIN1 (roundevenl, *q++);
+
+  /* inline code.  */
+  *p++ = BUILTIN1 (roundl, *q++);
+
+  /* { dg-final { scan-assembler {\m__significandieee128\M} } }  */
+  *p++ = BUILTIN1 (significandl, *q++);
+
+  /* { dg-final { scan-assembler {\msinhf128\M} } }  */
+  *p++ = BUILTIN1 (sinhl, *q++);
+
+  /* { dg-final { scan-assembler {\msinf128\M} } }  */
+  *p++ = BUILTIN1 (sinl, *q++);
+
+  /* { dg-final { scan-assembler {\msqrtf128\M} } }  */
+  *p++ = BUILTIN1 (sqrtl, *q++);
+
+  /* { dg-final { scan-assembler {\mtanhf128\M} } }  */
+  *p++ = BUILTIN1 (tanhl, *q++);
+
+  /* { dg-final { scan-assembler {\mtanf128\M} } }  */
+  *p++ = BUILTIN1 (tanl, *q++);
+
+  /* { dg-final { scan-assembler {\mtgammaf128\M} } }  */
+  *p++ = BUILTIN1 (tgammal, *q++);
+
+  /* inline code.  */
+  *p++ = BUILTIN1 (truncl, *q++);
+
+  /* { dg-final { scan-assembler {\my0f128\M} } }  */
+  *p++ = BUILTIN1 (y0l, *q++);
+
+  /* { dg-final { scan-assembler {\my1f128\M} } }  */
+  *p   = BUILTIN1 (y1l, *q);  
+
+}
+
+/* Built-in functions that returns a long double and take two long double
+   arguments.  */
+
+void
+return_ld_arg_ld_ld (long double *p,
+		     long double *q,
+		     long double *r)
+{
+  /* { dg-final { scan-assembler {\matan2f128\M} } }  */
+  *p++ = BUILTIN2 (atan2l, *q++, *r++);
+
+  /* inline code.  */
+  *p++ = BUILTIN2 (copysignl, *q++, *r++);
+
+  /* { dg-final { scan-assembler {\mremainderf128\M} } }  */
+  *p++ = BUILTIN2 (dreml, *q++, *r++);
+
+  /* { dg-final { scan-assembler {\mfdimf128\M} } }  */
+  *p++ = BUILTIN2 (fdiml, *q++, *r++);
+
+  /* { dg-final { scan-assembler {\mfmaxf128\M} } }  */
+  *p++ = BUILTIN2 (fmaxl, *q++, *r++);
+
+  /* { dg-final { scan-assembler {\mfminf128\M} } }  */
+  *p++ = BUILTIN2 (fminl, *q++, *r++);
+
+  /* { dg-final { scan-assembler {\mfmodf128\M} } }  */
+  *p++ = BUILTIN2 (fmodl, *q++, *r++);
+
+  /* { dg-final { scan-assembler {\mhypotf128\M} } }  */
+  *p++ = BUILTIN2 (hypotl, *q++, *r++);
+
+  /* { dg-final { scan-assembler {\mnextafterf128\M} } }  */
+  *p++ = BUILTIN2 (nextafterl, *q++, *r++);
+
+  /* { dg-final { scan-assembler {\m__nexttowardieee128\M} } }  */
+  *p++ = BUILTIN2 (nexttowardl, *q++, *r++);
+
+  /* { dg-final { scan-assembler {\mpowf128\M} } }  */
+  *p++ = BUILTIN2 (powl, *q++, *r++);
+
+  /* remainderf128 mentioned previously.  */
+  *p++ = BUILTIN2 (remainderl, *q++, *r++);
+
+  /* { dg-final { scan-assembler {\m__scalbnieee128\M} } }  */
+  *p   = BUILTIN2 (scalbl, *q, *r);  
+}
+
+/* Built-in function that returns a long double and take three long double
+   arguments.  */
+
+void
+return_ld_arg_ld_ld_ld (long double *p,
+			long double *q,
+			long double *r,
+			long double *s)
+{
+ /* inline code.  */
+  *p = BUILTIN3 (fmal, *q, *r, *s);
+}
+
+/* Built-in functions that returns a long double and take one
+   _Complex long double argument.  */
+
+void
+return_ld_arg_cld (long double *p,
+		   _Complex long double *q)
+{
+  /* { dg-final { scan-assembler {\mcabsf128\M} } }  */
+  *p++ = BUILTIN1 (cabsl, *q++);
+
+  /* inline code.  */
+  *p++ = BUILTIN1 (cargl, *q++);
+
+  /* inline code.  */
+  *p++ = BUILTIN1 (cimagl, *q++);
+
+  /* inline code.  */
+  *p   = BUILTIN1 (creall, *q);  
+}
+
+/* Built-in functions that returns a _Complex long double and takes one
+   _Complex long double argument.  */
+
+void
+return_cld_arg_cld (_Complex long double *p,
+		    _Complex long double *q)
+{
+  /* { dg-final { scan-assembler {\mcacoshf128\M} } }  */
+  *p++ = BUILTIN1 (cacoshl, *q++);
+
+  /* { dg-final { scan-assembler {\mcacosf128\M} } }  */
+  *p++ = BUILTIN1 (cacosl, *q++);
+
+  /* { dg-final { scan-assembler {\mcasinhf128\M} } }  */
+  *p++ = BUILTIN1 (casinhl, *q++);
+
+  /* { dg-final { scan-assembler {\mcasinf128\M} } }  */
+  *p++ = BUILTIN1 (casinl, *q++);
+
+  /* { dg-final { scan-assembler {\mcatanhf128\M} } }  */
+  *p++ = BUILTIN1 (catanhl, *q++);
+
+  /* { dg-final { scan-assembler {\mcatanf128\M} } }  */
+  *p++ = BUILTIN1 (catanl, *q++);
+
+  /* { dg-final { scan-assembler {\mccoshf128\M} } }  */
+  *p++ = BUILTIN1 (ccoshl, *q++);
+
+  /* { dg-final { scan-assembler {\mccosf128\M} } }  */
+  *p++ = BUILTIN1 (ccosl, *q++);
+
+  /* { dg-final { scan-assembler {\mcexpf128\M} } }  */
+  *p++ = BUILTIN1 (cexpl, *q++);
+
+  /* { dg-final { scan-assembler {\mclogf128\M} } }  */
+  *p++ = BUILTIN1 (clogl, *q++);
+
+  /* { dg-final { scan-assembler {\mclog10f128\M} } }  */
+  *p++ = BUILTIN1 (clog10l, *q++);
+
+  /* inline code.  */
+  *p++ = BUILTIN1 (conjl, *q++);
+
+  /* { dg-final { scan-assembler {\mcprojf128\M} } }  */
+  *p++ = BUILTIN1 (cprojl, *q++);
+
+  /* { dg-final { scan-assembler {\mcsinhf128\M} } }  */
+  *p++ = BUILTIN1 (csinhl, *q++);
+
+  /* { dg-final { scan-assembler {\mcsinf128\M} } }  */
+  *p++ = BUILTIN1 (csinl, *q++);
+
+  /* { dg-final { scan-assembler {\mcsqrtf128\M} } }  */
+  *p++ = BUILTIN1 (csqrtl, *q++);
+
+  /* { dg-final { scan-assembler {\mctanhf128\M} } }  */
+  *p++ = BUILTIN1 (ctanhl, *q++);
+
+  /* { dg-final { scan-assembler {\mctanf128\M} } }  */
+  *p   = BUILTIN1 (ctanl, *q);  
+}
+
+/* Built-in functions that returns a _Complex long double and takes one
+   long double argument.  */
+
+void
+return_cld_arg_ld (_Complex long double *p,
+		   long double *q)
+{
+  /* { dg-final { scan-assembler {\m__sincosieee128\M} } }  */
+  *p = BUILTIN1 (cexpil, *q);
+}
+
+/* Built-in function that returns a _Complex long double and takes two
+   _Complex long double arguments.  */
+
+void
+return_cld_arg_cld_cld (_Complex long double *p,
+			_Complex long double *q,
+			_Complex long double *r)
+{
+  /* { dg-final { scan-assembler {\mcpowf128\M} } }  */
+  *p = BUILTIN2 (cpowl, *q, *r);
+}
+
+/* Built-in functions that returns a long double and takes a long double and a
+   pointer to an int arguments.  */
+
+void
+return_ld_arg_ld_pi (long double *p,
+		     long double *q,
+		     int **r)
+{
+  /* { dg-final { scan-assembler {\mfrexpf128\M} } }  */
+  *p++ = BUILTIN2 (frexpl, *q++, *r++);
+
+  /* { dg-final { scan-assembler {\m__lgammaieee128_r\M} } }  */
+  *p++ = BUILTIN2 (gammal_r, *q++, *r++);
+
+  /* __lgammaieee128_r mentioned previously.  */
+  *p   = BUILTIN2 (lgammal_r, *q, *r);  
+}
+
+/* Built-in functions that returns a long double and takes a long double and an
+   int arguments.  */
+
+void
+return_ld_arg_ld_i (long double *p,
+		    long double *q,
+		    int *r)
+{
+  /* { dg-final { scan-assembler {\mldexpf128\M} } }  */
+  *p++ = BUILTIN2 (ldexpl, *q++, *r++);
+
+  /* { dg-final { scan-assembler {\m__powikf2\M} } }  */
+  *p++ = BUILTIN2 (powil, *q++, *r++);
+
+  /* { dg-final { scan-assembler {\mscalbnf128\M} } }  */
+  *p   = BUILTIN2 (scalbnl, *q, *r);  
+}
+
+/* Built-in function that returns a long double and takes a long double and a
+   long arguments.  */
+
+void
+return_ld_arg_ld_l (long double *p,
+		    long double *q,
+		    long *r)
+{
+  /* { dg-final { scan-assembler {\mscalblnf128\M} } }  */
+  *p = BUILTIN2 (scalblnl, *q, *r);
+}
+
+/* Built-in functions that returns a long double and takes a long double and a
+   long long arguments.  */
+
+void
+return_ld_arg_i_ld (long double *p,
+		    int *q,
+		    long double *r)
+{
+  /* { dg-final { scan-assembler {\mjnf128\M} } }  */
+  *p++ = BUILTIN2 (jnl, *q++, *r++);
+
+  /* { dg-final { scan-assembler {\mynf128\M} } }  */
+  *p   = BUILTIN2 (ynl, *q, *r);  
+}
+
+/* Built-in functions that returns a long double and takes a long double and a
+   pointer to a long double arguments.  */
+
+void
+return_ld_arg_ld_pld (long double *p,
+		      long double *q,
+		      long double **r)
+{
+  /* { dg-final { scan-assembler {\mmodff128\M} } }  */
+  *p = BUILTIN2 (modfl, *q, *r);
+}
+
+/* Built-in function that returns a long double and takes two long double and a
+   pointer to an int arguments.  */
+
+void
+return_ld_arg_ld_ld_pi (long double *p,
+			long double *q,
+			long double *r,
+			int **s)
+{
+  /* { dg-final { scan-assembler {\mremquof128\M} } }  */
+  *p = BUILTIN3 (remquol, *q, *r, *s);
+}
+
+/* Built-in functions that returns a long double and take no arguments.  */
+
+void
+return_ld_no_arg (long double *p)
+{
+  /* inline code.  */
+  *p++ = BUILTIN0 (huge_vall);
+
+  /* inline code.  */
+  *p   = BUILTIN0 (infl);     
+}
+
+/* Built-in functions that return san int and takes one long double argument.  */
+
+void
+return_i_arg_ld (int *p,
+		 long double *q)
+{
+  /* { dg-final { scan-assembler {\mceilf128\M} } }  */
+  *p++ = BUILTIN1 (iceill, *q++);
+
+  /* { dg-final { scan-assembler {\mfloorf128\M} } }  */
+  *p++ = BUILTIN1 (ifloorl, *q++);
+
+  /* { dg-final { scan-assembler {\milogbf128\M} } }  */
+  *p++ = BUILTIN1 (ilogbl, *q++);
+
+  /* { dg-final { scan-assembler {\mlrintf128\M} } }  */
+  *p++ = BUILTIN1 (irintl, *q++);
+
+  /* { dg-final { scan-assembler {\mlroundf128\M} } }  */
+  *p++ = BUILTIN1 (iroundl, *q++);
+
+  /* inline code.  */
+  *p++ = BUILTIN1 (signbitl, *q++);
+
+  /* inline code.  */
+  *p++ = BUILTIN1 (finitel, *q++);
+
+  /* inline code.  */
+  *p++ = BUILTIN1 (isinfl, *q++);
+
+  /* inline code.  */
+  *p   = BUILTIN1 (isnanl, *q);  
+}
+
+/* Built-in functions that returns a long and takes one long double argument.  */
+
+void
+return_l_arg_ld (long *p,
+		 long double *q)
+{
+  /* ceilf128 mentioned previouly.  */
+  *p++ = BUILTIN1 (lceill, *q++);
+
+  /* floorf128 mentioned previously.  */
+  *p++ = BUILTIN1 (lfloorl, *q++);
+
+  /* lrintf128 mentioned previously.  */
+  *p++ = BUILTIN1 (lrintl, *q++);
+
+  /* lroundf128 mentioned previously.  */
+  *p   = BUILTIN1 (lroundl, *q);  
+}
+
+/* Built-in functions that returns a long long and takes one long double
+   argument.  */
+
+void
+return_ll_arg_ld (long long *p,
+		  long double *r)
+{
+  /* ceilf128 mentioned previous.  */
+  *p++ = BUILTIN1 (llceill, *r++);
+
+  /* floorf128 mentioned previously.  */
+  *p++ = BUILTIN1 (llfloorl, *r++);
+
+  /* llrintf128 mentioned previously.  */
+  *p++ = BUILTIN1 (llrintl, *r++);
+
+  /* llroundf128 mentioned previously.  */
+  *p   = BUILTIN1 (llroundl, *r);  
+}
+
+/* Built-in function that returns a double and takes one double and one long
+   double arguments.  */
+
+void
+return_d_arg_d_ld (double *p,
+		   double *q,
+		   long double *r)
+{
+  /* { dg-final { scan-assembler {\m__nexttoward_to_ieee128\M} } }  */
+  *p = BUILTIN2 (nexttoward, *q, *r);
+}
+
+/* Built-in function that returns a float and takes one float and one long
+   double arguments.  */
+
+void
+return_f_arg_f_ld (float *p,
+		   float *q,
+		   long double *r)
+{
+  /* { dg-final { scan-assembler {\m__nexttowardf_to_ieee128\M} } }  */
+  *p = BUILTIN2 (nexttowardf, *q, *r);
+}
+
+/* Built-in function that returns void and takes one long double and two
+   pointers to long double arguments.  */
+
+void
+return_void_arg_ld_pld_pld (long double *p,
+			    long double **q,
+			    long double **r)
+{
+  /* __sincosieee128 mentioned previously.  */
+  BUILTIN3 (sincosl, *p, *q, *r);
+}
+
+/* Debug main program to determine if the library has all of the mapped
+   external functions.  Note, you need a new enough glibc to provide all of the
+   f128 functions.  */
+#ifdef DO_MAIN
+int
+main (void)
+{
+  return 0;
+}
+#endif
diff --git a/gcc/testsuite/gcc.target/powerpc/float128-longdouble-stdio.c b/gcc/testsuite/gcc.target/powerpc/float128-longdouble-stdio.c
new file mode 100644
index 00000000000..77c7b41cec9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/powerpc/float128-longdouble-stdio.c
@@ -0,0 +1,37 @@ 
+/* { dg-require-effective-target ppc_float128_hw } */
+/* { dg-require-effective-target power10_ok } */
+/* { dg-options "-mdejagnu-cpu=power9 -mno-pcrel -O2 -Wno-psabi -mabi=ieeelongdouble" } */
+
+/* Test if switching long double to IEEE 128-bit maps the printf and scanf
+   function names correctly.  We explicitly turn off PC-relative support to
+   make it simpler to compare the call without having a @notoc qualifier.  */
+
+#include <stdlib.h>
+
+volatile long double x = 1.0L;
+volatile long double y, z;
+
+int
+main (void)
+{
+  char buffer[100];
+
+  /* { dg-final { scan-assembler {\m__sprintfieee128\M} } }  */
+  __builtin_sprintf (buffer, "%Lg", x);
+
+  /* { dg-final { scan-assembler {\m__printfieee128\M} } }  */
+  __builtin_printf ("x is %Lg [%s]\n", x, buffer);
+
+  /* { dg-final { scan-assembler {\m__isoc99sscanfieee128\M} } }  */
+  __builtin_sscanf (buffer, "%Lg", &y);
+
+  __builtin_printf ("Type 1.0: ");
+
+  /* { dg-final { scan-assembler {\m__isoc99scanfieee128\M} } }  */
+  __builtin_scanf ("%Lg", &z);
+
+  if (x != y || x != z)
+    abort ();
+
+  return 0;
+}