diff mbox series

Add type arg to TARGET_LIBC_HAS_FUNCTION

Message ID 39b3a842-46d8-95ef-a2d8-4de7d3060bc1@suse.de
State New
Headers show
Series Add type arg to TARGET_LIBC_HAS_FUNCTION | expand

Commit Message

Tom de Vries Sept. 28, 2020, 5:26 p.m. UTC
[ was: Re: [Patch][nvptx] return true in libc_has_function for
function_sincos ]

On 9/26/20 6:47 PM, Tobias Burnus wrote:
> Found when looking at PR97203 (but having no effect there).
> 
> The GCC ME optimizes with -O1 (or higher) the
>   a = sinf(x)
>   b = cosf(x)
> to __builtin_cexpi(x, &a, &b)
> (...i as in internal; like cexp(z) but with with __real__ z == 0)
> 
> 
> In expand_builtin_cexpi, that is handles as:
>   if (optab_handler (sincos_optab, mode) != CODE_FOR_nothing)
>     ...
>   else if (targetm.libc_has_function (function_sincos))
>     ...
>   else
>         fn = builtin_decl_explicit (BUILT_IN_CEXPF);
> 
> And the latter is done. As newlib's cexpf does not know that
> __real__ z == 0, it calculates 'r = expf (__real__ z)' before
> invoking sinf and cosf on __imag__ z.
> 
> Thus, it is much faster to call 'sincosf', which also exists
> in newlib.
> 
> Solution: Return true for targetm.libc_has_function (function_sincos).
> 
> 
> NOTE: With -funsafe-math-optimizations (-O0 or higher),
> sinf/cosf and sincosf invoke .sin.approx/.cos/.approx instead of
> doing a library call.

This version takes care to enable sincos and sincosf, but not sincosl.

Target hook changes OK for trunk?

Thanks,
- Tom

Comments

Richard Biener Sept. 29, 2020, 6:59 a.m. UTC | #1
On Mon, Sep 28, 2020 at 7:28 PM Tom de Vries <tdevries@suse.de> wrote:
>
> [ was: Re: [Patch][nvptx] return true in libc_has_function for
> function_sincos ]
>
> On 9/26/20 6:47 PM, Tobias Burnus wrote:
> > Found when looking at PR97203 (but having no effect there).
> >
> > The GCC ME optimizes with -O1 (or higher) the
> >   a = sinf(x)
> >   b = cosf(x)
> > to __builtin_cexpi(x, &a, &b)
> > (...i as in internal; like cexp(z) but with with __real__ z == 0)
> >
> >
> > In expand_builtin_cexpi, that is handles as:
> >   if (optab_handler (sincos_optab, mode) != CODE_FOR_nothing)
> >     ...
> >   else if (targetm.libc_has_function (function_sincos))
> >     ...
> >   else
> >         fn = builtin_decl_explicit (BUILT_IN_CEXPF);
> >
> > And the latter is done. As newlib's cexpf does not know that
> > __real__ z == 0, it calculates 'r = expf (__real__ z)' before
> > invoking sinf and cosf on __imag__ z.
> >
> > Thus, it is much faster to call 'sincosf', which also exists
> > in newlib.
> >
> > Solution: Return true for targetm.libc_has_function (function_sincos).
> >
> >
> > NOTE: With -funsafe-math-optimizations (-O0 or higher),
> > sinf/cosf and sincosf invoke .sin.approx/.cos/.approx instead of
> > doing a library call.
>
> This version takes care to enable sincos and sincosf, but not sincosl.
>
> Target hook changes OK for trunk?

@@ -9770,7 +9770,7 @@ fold_builtin_sincos (location_t loc,
     }
   if (!call)
     {
-      if (!targetm.libc_has_function (function_c99_math_complex)
+      if (!targetm.libc_has_function (function_c99_math_complex, NULL_TREE)

why pass NULL_TREE and not 'type' here?

          || !builtin_decl_implicit_p (fn))
        return NULL_TREE;

similar for the builtins.def change for the cases where math functions
are affected?  I guess it's a bit awkward to make it work there, so OK.

 bool
-darwin_libc_has_function (enum function_class fn_class)
+darwin_libc_has_function (enum function_class fn_class, tree type)
 {
-  if (fn_class == function_sincos)
+  if (type != NULL_TREE)
+    {
+      switch (fn_class)
+       {
+       case function_sincos:
+         break;
+       default:
+         /* Not implemented.  */
+         gcc_unreachable ();
+       }
+    }

huh.  I think special-casing this just for sincos is a bit awkward,
esp. ICEing for other queries with a type.  Specifically

-@deftypefn {Target Hook} bool TARGET_LIBC_HAS_FUNCTION (enum
function_class @var{fn_class})
+@deftypefn {Target Hook} bool TARGET_LIBC_HAS_FUNCTION (enum
function_class @var{fn_class}, tree @var{type})
 This hook determines whether a function from a class of functions
-@var{fn_class} is present in the target C library.
+@var{fn_class} is present in the target C library.  The @var{type} argument
+can be used to distinguish between float, double and long double versions.
 @end deftypefn

This doesn't mention we'll ICE for anything but sincos.  A sensible
semantics would be that if TYPE is NULL the caller asks for support
for all standard (float, double, long double) types while with TYPE
non-NULL it can ask for a specific type including for example the
new _FloatN, etc. types.

Richard.

> Thanks,
> - Tom
Tom de Vries Sept. 29, 2020, 12:18 p.m. UTC | #2
On 9/29/20 8:59 AM, Richard Biener wrote:
> On Mon, Sep 28, 2020 at 7:28 PM Tom de Vries <tdevries@suse.de> wrote:
>>
>> [ was: Re: [Patch][nvptx] return true in libc_has_function for
>> function_sincos ]
>>
>> On 9/26/20 6:47 PM, Tobias Burnus wrote:
>>> Found when looking at PR97203 (but having no effect there).
>>>
>>> The GCC ME optimizes with -O1 (or higher) the
>>>   a = sinf(x)
>>>   b = cosf(x)
>>> to __builtin_cexpi(x, &a, &b)
>>> (...i as in internal; like cexp(z) but with with __real__ z == 0)
>>>
>>>
>>> In expand_builtin_cexpi, that is handles as:
>>>   if (optab_handler (sincos_optab, mode) != CODE_FOR_nothing)
>>>     ...
>>>   else if (targetm.libc_has_function (function_sincos))
>>>     ...
>>>   else
>>>         fn = builtin_decl_explicit (BUILT_IN_CEXPF);
>>>
>>> And the latter is done. As newlib's cexpf does not know that
>>> __real__ z == 0, it calculates 'r = expf (__real__ z)' before
>>> invoking sinf and cosf on __imag__ z.
>>>
>>> Thus, it is much faster to call 'sincosf', which also exists
>>> in newlib.
>>>
>>> Solution: Return true for targetm.libc_has_function (function_sincos).
>>>
>>>
>>> NOTE: With -funsafe-math-optimizations (-O0 or higher),
>>> sinf/cosf and sincosf invoke .sin.approx/.cos/.approx instead of
>>> doing a library call.
>>
>> This version takes care to enable sincos and sincosf, but not sincosl.
>>
>> Target hook changes OK for trunk?
> 
> @@ -9770,7 +9770,7 @@ fold_builtin_sincos (location_t loc,
>      }
>    if (!call)
>      {
> -      if (!targetm.libc_has_function (function_c99_math_complex)
> +      if (!targetm.libc_has_function (function_c99_math_complex, NULL_TREE)
> 
> why pass NULL_TREE and not 'type' here?
> 
>           || !builtin_decl_implicit_p (fn))
>         return NULL_TREE;
> 

I was trying to do the minimal, sincos-only implementation.

> similar for the builtins.def change for the cases where math functions
> are affected?  I guess it's a bit awkward to make it work there, so OK.
> 
>  bool
> -darwin_libc_has_function (enum function_class fn_class)
> +darwin_libc_has_function (enum function_class fn_class, tree type)
>  {
> -  if (fn_class == function_sincos)
> +  if (type != NULL_TREE)
> +    {
> +      switch (fn_class)
> +       {
> +       case function_sincos:
> +         break;
> +       default:
> +         /* Not implemented.  */
> +         gcc_unreachable ();
> +       }
> +    }
> 
> huh.  I think special-casing this just for sincos is a bit awkward,
> esp. ICEing for other queries with a type.  Specifically
> 
> -@deftypefn {Target Hook} bool TARGET_LIBC_HAS_FUNCTION (enum
> function_class @var{fn_class})
> +@deftypefn {Target Hook} bool TARGET_LIBC_HAS_FUNCTION (enum
> function_class @var{fn_class}, tree @var{type})
>  This hook determines whether a function from a class of functions
> -@var{fn_class} is present in the target C library.
> +@var{fn_class} is present in the target C library.  The @var{type} argument
> +can be used to distinguish between float, double and long double versions.
>  @end deftypefn
> 
> This doesn't mention we'll ICE for anything but sincos.  A sensible
> semantics would be that if TYPE is NULL the caller asks for support
> for all standard (float, double, long double) types while with TYPE
> non-NULL it can ask for a specific type including for example the
> new _FloatN, etc. types.
> 

Ack, updated accordingly and retested.

OK for trunk?

Thanks,
- Tom
Richard Biener Sept. 30, 2020, 12:23 p.m. UTC | #3
On Tue, Sep 29, 2020 at 2:18 PM Tom de Vries <tdevries@suse.de> wrote:
>
> On 9/29/20 8:59 AM, Richard Biener wrote:
> > On Mon, Sep 28, 2020 at 7:28 PM Tom de Vries <tdevries@suse.de> wrote:
> >>
> >> [ was: Re: [Patch][nvptx] return true in libc_has_function for
> >> function_sincos ]
> >>
> >> On 9/26/20 6:47 PM, Tobias Burnus wrote:
> >>> Found when looking at PR97203 (but having no effect there).
> >>>
> >>> The GCC ME optimizes with -O1 (or higher) the
> >>>   a = sinf(x)
> >>>   b = cosf(x)
> >>> to __builtin_cexpi(x, &a, &b)
> >>> (...i as in internal; like cexp(z) but with with __real__ z == 0)
> >>>
> >>>
> >>> In expand_builtin_cexpi, that is handles as:
> >>>   if (optab_handler (sincos_optab, mode) != CODE_FOR_nothing)
> >>>     ...
> >>>   else if (targetm.libc_has_function (function_sincos))
> >>>     ...
> >>>   else
> >>>         fn = builtin_decl_explicit (BUILT_IN_CEXPF);
> >>>
> >>> And the latter is done. As newlib's cexpf does not know that
> >>> __real__ z == 0, it calculates 'r = expf (__real__ z)' before
> >>> invoking sinf and cosf on __imag__ z.
> >>>
> >>> Thus, it is much faster to call 'sincosf', which also exists
> >>> in newlib.
> >>>
> >>> Solution: Return true for targetm.libc_has_function (function_sincos).
> >>>
> >>>
> >>> NOTE: With -funsafe-math-optimizations (-O0 or higher),
> >>> sinf/cosf and sincosf invoke .sin.approx/.cos/.approx instead of
> >>> doing a library call.
> >>
> >> This version takes care to enable sincos and sincosf, but not sincosl.
> >>
> >> Target hook changes OK for trunk?
> >
> > @@ -9770,7 +9770,7 @@ fold_builtin_sincos (location_t loc,
> >      }
> >    if (!call)
> >      {
> > -      if (!targetm.libc_has_function (function_c99_math_complex)
> > +      if (!targetm.libc_has_function (function_c99_math_complex, NULL_TREE)
> >
> > why pass NULL_TREE and not 'type' here?
> >
> >           || !builtin_decl_implicit_p (fn))
> >         return NULL_TREE;
> >
>
> I was trying to do the minimal, sincos-only implementation.
>
> > similar for the builtins.def change for the cases where math functions
> > are affected?  I guess it's a bit awkward to make it work there, so OK.
> >
> >  bool
> > -darwin_libc_has_function (enum function_class fn_class)
> > +darwin_libc_has_function (enum function_class fn_class, tree type)
> >  {
> > -  if (fn_class == function_sincos)
> > +  if (type != NULL_TREE)
> > +    {
> > +      switch (fn_class)
> > +       {
> > +       case function_sincos:
> > +         break;
> > +       default:
> > +         /* Not implemented.  */
> > +         gcc_unreachable ();
> > +       }
> > +    }
> >
> > huh.  I think special-casing this just for sincos is a bit awkward,
> > esp. ICEing for other queries with a type.  Specifically
> >
> > -@deftypefn {Target Hook} bool TARGET_LIBC_HAS_FUNCTION (enum
> > function_class @var{fn_class})
> > +@deftypefn {Target Hook} bool TARGET_LIBC_HAS_FUNCTION (enum
> > function_class @var{fn_class}, tree @var{type})
> >  This hook determines whether a function from a class of functions
> > -@var{fn_class} is present in the target C library.
> > +@var{fn_class} is present in the target C library.  The @var{type} argument
> > +can be used to distinguish between float, double and long double versions.
> >  @end deftypefn
> >
> > This doesn't mention we'll ICE for anything but sincos.  A sensible
> > semantics would be that if TYPE is NULL the caller asks for support
> > for all standard (float, double, long double) types while with TYPE
> > non-NULL it can ask for a specific type including for example the
> > new _FloatN, etc. types.
> >
>
> Ack, updated accordingly and retested.
>
> OK for trunk?

OK.

Thanks,
Richard.

> Thanks,
> - Tom
diff mbox series

Patch

[nvptx] Add type arg to TARGET_LIBC_HAS_FUNCTION

GCC has a target hook TARGET_LIBC_HAS_FUNCTION, which tells the compiler
which functions it can expect to be present in libc.

The default target hook does not include the sincos functions.

The nvptx port of newlib does include sincos and sincosf, but not sincosl.

The target hook TARGET_LIBC_HAS_FUNCTION does not distinguish between sincos,
sincosf and sincosl, so if we enable it for the sincos functions, then for
test.c:
...
long double x, a, b;
int main (void) {
  x = 0.5;
  a = sinl (x);
  b = cosl (x);
  printf ("a: %f\n", (double)a);
  printf ("b: %f\n", (double)b);
  return 0;
}
...
we introduce a regression:
...
$ gcc test.c -lm -O2
unresolved symbol sincosl
collect2: error: ld returned 1 exit status
...

Add a type argument to target hook TARGET_LIBC_HAS_FUNCTION_TYPE, and use it
in nvptx_libc_has_function_type to enable sincos and sincosf, but not sincosl.

For now, a non-null type argument is only supported for
fn_class == function_sincos.

Tested on nvptx.

2020-09-28  Tobias Burnus  <tobias@codesourcery.com>
	    Tom de Vries  <tdevries@suse.de>

	* builtins.c (expand_builtin_cexpi, fold_builtin_sincos): Update
	targetm.libc_has_function call.
	* builtins.def (DEF_C94_BUILTIN, DEF_C99_BUILTIN, DEF_C11_BUILTIN):
	(DEF_C2X_BUILTIN, DEF_C99_COMPL_BUILTIN, DEF_C99_C90RES_BUILTIN):
	Same.
	* config/darwin-protos.h (darwin_libc_has_function): Update prototype.
	* config/darwin.c (darwin_libc_has_function): Add arg.
	* config/linux-protos.h (linux_libc_has_function): Update prototype.
	* config/linux.c (linux_libc_has_function): Add arg.
	* config/i386/i386.c (ix86_libc_has_function): Update
	targetm.libc_has_function call.
	* config/nvptx/nvptx.c (nvptx_libc_has_function): New function.
	(TARGET_LIBC_HAS_FUNCTION): Redefine to nvptx_libc_has_function.
	* convert.c (convert_to_integer_1): Update targetm.libc_has_function
	call.
	* match.pd: Same.
	* target.def (libc_has_function): Add arg.
	* doc/tm.texi: Regenerate.
	* targhooks.c (default_libc_has_function, gnu_libc_has_function)
	(no_c99_libc_has_function): Add arg.
	* targhooks.h (default_libc_has_function,  no_c99_libc_has_function)
	(gnu_libc_has_function): Update prototype.
	* tree-ssa-math-opts.c (pass_cse_sincos::execute): Update
	targetm.libc_has_function call.

---
 gcc/builtins.c             |  4 ++--
 gcc/builtins.def           | 20 ++++++++++++++------
 gcc/config/darwin-protos.h |  2 +-
 gcc/config/darwin.c        | 16 ++++++++++++++--
 gcc/config/i386/i386.c     |  2 +-
 gcc/config/linux-protos.h  |  2 +-
 gcc/config/linux.c         | 16 ++++++++++++++--
 gcc/config/nvptx/nvptx.c   | 20 ++++++++++++++++++++
 gcc/convert.c              |  8 ++++----
 gcc/doc/tm.texi            |  5 +++--
 gcc/fortran/f95-lang.c     |  4 ++--
 gcc/match.pd               |  6 +++---
 gcc/target.def             |  5 +++--
 gcc/targhooks.c            | 44 +++++++++++++++++++++++++++++++++++++++++---
 gcc/targhooks.h            |  6 +++---
 gcc/tree-ssa-math-opts.c   |  8 +++++---
 16 files changed, 131 insertions(+), 37 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index cac842fd4a3..85f5f630a0f 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -2733,7 +2733,7 @@  expand_builtin_cexpi (tree exp, rtx target)
       /* Compute into op1 and op2.  */
       expand_twoval_unop (sincos_optab, op0, op2, op1, 0);
     }
-  else if (targetm.libc_has_function (function_sincos))
+  else if (targetm.libc_has_function (function_sincos, type))
     {
       tree call, fn = NULL_TREE;
       tree top1, top2;
@@ -9770,7 +9770,7 @@  fold_builtin_sincos (location_t loc,
     }
   if (!call)
     {
-      if (!targetm.libc_has_function (function_c99_math_complex)
+      if (!targetm.libc_has_function (function_c99_math_complex, NULL_TREE)
 	  || !builtin_decl_implicit_p (fn))
 	return NULL_TREE;
       fndecl = builtin_decl_explicit (fn);
diff --git a/gcc/builtins.def b/gcc/builtins.def
index 102322b7912..95428c010d9 100644
--- a/gcc/builtins.def
+++ b/gcc/builtins.def
@@ -138,34 +138,41 @@  along with GCC; see the file COPYING3.  If not see
 #undef DEF_C94_BUILTIN
 #define DEF_C94_BUILTIN(ENUM, NAME, TYPE, ATTRS)	\
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,	\
-	       true, true, !flag_isoc94, ATTRS, targetm.libc_has_function (function_c94), true)
+	       true, true, !flag_isoc94, ATTRS, \
+	       targetm.libc_has_function (function_c94, NULL_TREE), true)
 
 /* Like DEF_LIB_BUILTIN, except that the function is only a part of
    the standard in C99 or above.  */
 #undef DEF_C99_BUILTIN
 #define DEF_C99_BUILTIN(ENUM, NAME, TYPE, ATTRS)	\
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,	\
-	       true, true, !flag_isoc99, ATTRS, targetm.libc_has_function (function_c99_misc), true)
+	       true, true, !flag_isoc99, ATTRS, \
+	       targetm.libc_has_function (function_c99_misc, NULL_TREE), true)
 
 /* Like DEF_LIB_BUILTIN, except that the function is only a part of
    the standard in C11 or above.  */
 #undef DEF_C11_BUILTIN
 #define DEF_C11_BUILTIN(ENUM, NAME, TYPE, ATTRS)	\
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,	\
-	       true, true, !flag_isoc11, ATTRS, targetm.libc_has_function (function_c11_misc), true)
+	       true, true, !flag_isoc11, ATTRS, \
+	       targetm.libc_has_function (function_c11_misc, NULL_TREE), true)
 
 /* Like DEF_LIB_BUILTIN, except that the function is only a part of
    the standard in C2x or above.  */
 #undef DEF_C2X_BUILTIN
 #define DEF_C2X_BUILTIN(ENUM, NAME, TYPE, ATTRS)	\
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,	\
-	       true, true, !flag_isoc2x, ATTRS, targetm.libc_has_function (function_c2x_misc), true)
+	       true, true, !flag_isoc2x, ATTRS, \
+	       targetm.libc_has_function (function_c2x_misc, NULL_TREE), true)
 
 /* Like DEF_C99_BUILTIN, but for complex math functions.  */
 #undef DEF_C99_COMPL_BUILTIN
 #define DEF_C99_COMPL_BUILTIN(ENUM, NAME, TYPE, ATTRS)	\
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,	\
-	       true, true, !flag_isoc99, ATTRS, targetm.libc_has_function (function_c99_math_complex), true)
+	       true, true, !flag_isoc99, ATTRS, \
+	       targetm.libc_has_function (function_c99_math_complex, \
+					  NULL_TREE), \
+	       true)
 
 /* Builtin that is specified by C99 and C90 reserve the name for future use.
    We can still recognize the builtin in C90 mode but we can't produce it
@@ -173,7 +180,8 @@  along with GCC; see the file COPYING3.  If not see
 #undef DEF_C99_C90RES_BUILTIN
 #define DEF_C99_C90RES_BUILTIN(ENUM, NAME, TYPE, ATTRS)	\
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_NORMAL, TYPE, TYPE,	\
-	       true, true, !flag_isoc99, ATTRS, targetm.libc_has_function (function_c99_misc), true)
+	       true, true, !flag_isoc99, ATTRS, \
+	       targetm.libc_has_function (function_c99_misc, NULL_TREE), true)
 
 /* Builtin that C99 reserve the name for future use. We can still recognize
    the builtin in C99 mode but we can't produce it implicitly.  */
diff --git a/gcc/config/darwin-protos.h b/gcc/config/darwin-protos.h
index 54cd1e4c1a4..49c540fe08e 100644
--- a/gcc/config/darwin-protos.h
+++ b/gcc/config/darwin-protos.h
@@ -125,6 +125,6 @@  extern bool darwin_kextabi_p (void);
 extern void darwin_override_options (void);
 extern void darwin_patch_builtins (void);
 extern void darwin_rename_builtins (void);
-extern bool darwin_libc_has_function (enum function_class fn_class);
+extern bool darwin_libc_has_function (enum function_class fn_class, tree);
 
 #endif /* CONFIG_DARWIN_PROTOS_H */
diff --git a/gcc/config/darwin.c b/gcc/config/darwin.c
index c8edfb88b59..1645e4606c8 100644
--- a/gcc/config/darwin.c
+++ b/gcc/config/darwin.c
@@ -3542,9 +3542,21 @@  darwin_rename_builtins (void)
 }
 
 bool
-darwin_libc_has_function (enum function_class fn_class)
+darwin_libc_has_function (enum function_class fn_class, tree type)
 {
-  if (fn_class == function_sincos)
+  if (type != NULL_TREE)
+    {
+      switch (fn_class)
+	{
+	case function_sincos:
+	  break;
+	default:
+	  /* Not implemented.  */
+	  gcc_unreachable ();
+	}
+    }
+
+ if (fn_class == function_sincos)
     return (strverscmp (darwin_macosx_version_min, "10.9") >= 0);
 
   if (fn_class == function_c99_math_complex
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index c890a733293..f684954af81 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -1484,7 +1484,7 @@  ix86_reg_parm_stack_space (const_tree fndecl)
 bool
 ix86_libc_has_function (enum function_class fn_class)
 {
-  return targetm.libc_has_function (fn_class);
+  return targetm.libc_has_function (fn_class, NULL_TREE);
 }
 
 /* Returns value SYSV_ABI, MS_ABI dependent on fntype,
diff --git a/gcc/config/linux-protos.h b/gcc/config/linux-protos.h
index 3759187f1ab..c52778b4b35 100644
--- a/gcc/config/linux-protos.h
+++ b/gcc/config/linux-protos.h
@@ -19,4 +19,4 @@  along with GCC; see the file COPYING3.  If not see
 
 extern bool linux_has_ifunc_p (void);
 
-extern bool linux_libc_has_function (enum function_class fn_class);
+extern bool linux_libc_has_function (enum function_class fn_class, tree);
diff --git a/gcc/config/linux.c b/gcc/config/linux.c
index 98761536cf0..0449bd27a41 100644
--- a/gcc/config/linux.c
+++ b/gcc/config/linux.c
@@ -25,9 +25,21 @@  along with GCC; see the file COPYING3.  If not see
 #include "linux-protos.h"
 
 bool
-linux_libc_has_function (enum function_class fn_class)
+linux_libc_has_function (enum function_class fn_class, tree type)
 {
-  if (OPTION_GLIBC || OPTION_MUSL)
+  if (type != NULL_TREE)
+    {
+      switch (fn_class)
+	{
+	case function_sincos:
+	  break;
+	default:
+	  /* Not implemented.  */
+	  gcc_unreachable ();
+	}
+    }
+
+ if (OPTION_GLIBC || OPTION_MUSL)
     return true;
   if (OPTION_BIONIC)
     if (fn_class == function_c94
diff --git a/gcc/config/nvptx/nvptx.c b/gcc/config/nvptx/nvptx.c
index de82f9ab875..afac1bda45d 100644
--- a/gcc/config/nvptx/nvptx.c
+++ b/gcc/config/nvptx/nvptx.c
@@ -6536,6 +6536,23 @@  nvptx_set_current_function (tree fndecl)
   oacc_bcast_partition = 0;
 }
 
+/* Implement TARGET_LIBC_HAS_FUNCTION.  */
+
+bool
+nvptx_libc_has_function (enum function_class fn_class, tree type)
+{
+  if (fn_class == function_sincos)
+    {
+      if (type != NULL_TREE)
+	/* Currently, newlib does not support sincosl.  */
+	return type == float_type_node || type == double_type_node;
+      else
+	return true;
+    }
+
+  return default_libc_has_function (fn_class, type);
+}
+
 #undef TARGET_OPTION_OVERRIDE
 #define TARGET_OPTION_OVERRIDE nvptx_option_override
 
@@ -6681,6 +6698,9 @@  nvptx_set_current_function (tree fndecl)
 #undef TARGET_SET_CURRENT_FUNCTION
 #define TARGET_SET_CURRENT_FUNCTION nvptx_set_current_function
 
+#undef TARGET_LIBC_HAS_FUNCTION
+#define TARGET_LIBC_HAS_FUNCTION nvptx_libc_has_function
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-nvptx.h"
diff --git a/gcc/convert.c b/gcc/convert.c
index 292c5133974..f4dcbb5002b 100644
--- a/gcc/convert.c
+++ b/gcc/convert.c
@@ -501,7 +501,7 @@  convert_to_integer_1 (tree type, tree expr, bool dofold)
 	CASE_FLT_FN (BUILT_IN_CEIL):
 	CASE_FLT_FN_FLOATN_NX (BUILT_IN_CEIL):
 	  /* Only convert in ISO C99 mode.  */
-	  if (!targetm.libc_has_function (function_c99_misc))
+	  if (!targetm.libc_has_function (function_c99_misc, NULL_TREE))
 	    break;
 	  if (outprec < TYPE_PRECISION (integer_type_node)
 	      || (outprec == TYPE_PRECISION (integer_type_node)
@@ -518,7 +518,7 @@  convert_to_integer_1 (tree type, tree expr, bool dofold)
 	CASE_FLT_FN (BUILT_IN_FLOOR):
 	CASE_FLT_FN_FLOATN_NX (BUILT_IN_FLOOR):
 	  /* Only convert in ISO C99 mode.  */
-	  if (!targetm.libc_has_function (function_c99_misc))
+	  if (!targetm.libc_has_function (function_c99_misc, NULL_TREE))
 	    break;
 	  if (outprec < TYPE_PRECISION (integer_type_node)
 	      || (outprec == TYPE_PRECISION (integer_type_node)
@@ -535,7 +535,7 @@  convert_to_integer_1 (tree type, tree expr, bool dofold)
 	CASE_FLT_FN (BUILT_IN_ROUND):
 	CASE_FLT_FN_FLOATN_NX (BUILT_IN_ROUND):
 	  /* Only convert in ISO C99 mode and with -fno-math-errno.  */
-	  if (!targetm.libc_has_function (function_c99_misc)
+	  if (!targetm.libc_has_function (function_c99_misc, NULL_TREE)
 	      || flag_errno_math)
 	    break;
 	  if (outprec < TYPE_PRECISION (integer_type_node)
@@ -559,7 +559,7 @@  convert_to_integer_1 (tree type, tree expr, bool dofold)
 	CASE_FLT_FN (BUILT_IN_RINT):
 	CASE_FLT_FN_FLOATN_NX (BUILT_IN_RINT):
 	  /* Only convert in ISO C99 mode and with -fno-math-errno.  */
-	  if (!targetm.libc_has_function (function_c99_misc)
+	  if (!targetm.libc_has_function (function_c99_misc, NULL_TREE)
 	      || flag_errno_math)
 	    break;
 	  if (outprec < TYPE_PRECISION (integer_type_node)
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 8e9e7701531..222b82a4aed 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -5602,9 +5602,10 @@  refers to the global ``variable'' @code{errno}.  (On certain systems,
 macro, a reasonable default is used.
 @end defmac
 
-@deftypefn {Target Hook} bool TARGET_LIBC_HAS_FUNCTION (enum function_class @var{fn_class})
+@deftypefn {Target Hook} bool TARGET_LIBC_HAS_FUNCTION (enum function_class @var{fn_class}, tree @var{type})
 This hook determines whether a function from a class of functions
-@var{fn_class} is present in the target C library.
+@var{fn_class} is present in the target C library.  The @var{type} argument
+can be used to distinguish between float, double and long double versions.
 @end deftypefn
 
 @deftypefn {Target Hook} bool TARGET_LIBC_HAS_FAST_FUNCTION (int @var{fcode})
diff --git a/gcc/fortran/f95-lang.c b/gcc/fortran/f95-lang.c
index e3288d7f576..526b7219e4b 100644
--- a/gcc/fortran/f95-lang.c
+++ b/gcc/fortran/f95-lang.c
@@ -881,7 +881,7 @@  gfc_init_builtin_functions (void)
 		      BUILT_IN_POWIF, "powif", ATTR_CONST_NOTHROW_LEAF_LIST);
 
 
-  if (targetm.libc_has_function (function_c99_math_complex))
+  if (targetm.libc_has_function (function_c99_math_complex, NULL_TREE))
     {
       gfc_define_builtin ("__builtin_cbrtl", mfunc_longdouble[0],
 			  BUILT_IN_CBRTL, "cbrtl",
@@ -903,7 +903,7 @@  gfc_init_builtin_functions (void)
 			  ATTR_CONST_NOTHROW_LEAF_LIST);
     }
 
-  if (targetm.libc_has_function (function_sincos))
+  if (targetm.libc_has_function (function_sincos, NULL_TREE))
     {
       gfc_define_builtin ("__builtin_sincosl",
 			  func_longdouble_longdoublep_longdoublep,
diff --git a/gcc/match.pd b/gcc/match.pd
index e6dcdd0b855..ccbc84c14fc 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -5185,7 +5185,7 @@  DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
     (with {
        const REAL_VALUE_TYPE *const value = TREE_REAL_CST_PTR (@0);
        bool use_exp2 = false;
-       if (targetm.libc_has_function (function_c99_misc)
+       if (targetm.libc_has_function (function_c99_misc, NULL_TREE)
 	   && value->cl == rvc_normal)
 	 {
 	   REAL_VALUE_TYPE frac_rvt = *value;
@@ -5484,7 +5484,7 @@  DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
       cexpis (CEXPI)
   (simplify
    (cexps compositional_complex@0)
-   (if (targetm.libc_has_function (function_c99_math_complex))
+   (if (targetm.libc_has_function (function_c99_math_complex, NULL_TREE))
     (complex
      (mult (exps@1 (realpart @0)) (realpart (cexpis:type@2 (imagpart @0))))
      (mult @1 (imagpart @2)))))))
@@ -5536,7 +5536,7 @@  DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
  /* truncl(extend(x)) and trunc(extend(x)) -> extend(truncf(x)), etc.,
     if x is a float.  */
  (if (optimize && canonicalize_math_p ()
-      && targetm.libc_has_function (function_c99_misc))
+      && targetm.libc_has_function (function_c99_misc, NULL_TREE))
   (simplify
    (froms (convert float_value_p@0))
    (convert (tos @0)))))
diff --git a/gcc/target.def b/gcc/target.def
index fc4563d144c..881ed2a6ae5 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -2618,8 +2618,9 @@  set via @code{__attribute__}.",
 DEFHOOK
 (libc_has_function,
  "This hook determines whether a function from a class of functions\n\
-@var{fn_class} is present in the target C library.",
- bool, (enum function_class fn_class),
+@var{fn_class} is present in the target C library.  The @var{type} argument\n\
+can be used to distinguish between float, double and long double versions.",
+ bool, (enum function_class fn_class, tree type),
  default_libc_has_function)
 
 DEFHOOK
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index da4805d284e..fa594c45ceb 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -1641,8 +1641,20 @@  default_have_conditional_execution (void)
 /* By default we assume that c99 functions are present at the runtime,
    but sincos is not.  */
 bool
-default_libc_has_function (enum function_class fn_class)
+default_libc_has_function (enum function_class fn_class, tree type)
 {
+  if (type != NULL_TREE)
+    {
+      switch (fn_class)
+	{
+	case function_sincos:
+	  break;
+	default:
+	  /* Not implemented.  */
+	  gcc_unreachable ();
+	}
+    }
+
   if (fn_class == function_c94
       || fn_class == function_c99_misc
       || fn_class == function_c99_math_complex)
@@ -1660,14 +1672,40 @@  default_libc_has_fast_function (int fcode ATTRIBUTE_UNUSED)
 }
 
 bool
-gnu_libc_has_function (enum function_class fn_class ATTRIBUTE_UNUSED)
+gnu_libc_has_function (enum function_class fn_class ATTRIBUTE_UNUSED,
+		       tree type)
 {
+  if (type != NULL_TREE)
+    {
+      switch (fn_class)
+	{
+	case function_sincos:
+	  break;
+	default:
+	  /* Not implemented.  */
+	  gcc_unreachable ();
+	}
+    }
+
   return true;
 }
 
 bool
-no_c99_libc_has_function (enum function_class fn_class ATTRIBUTE_UNUSED)
+no_c99_libc_has_function (enum function_class fn_class ATTRIBUTE_UNUSED,
+			  tree type)
 {
+  if (type != NULL_TREE)
+    {
+      switch (fn_class)
+	{
+	case function_sincos:
+	  break;
+	default:
+	  /* Not implemented.  */
+	  gcc_unreachable ();
+	}
+    }
+
   return false;
 }
 
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index b572a36e8cf..44ab9262c2a 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -208,10 +208,10 @@  extern rtx default_addr_space_convert (rtx, tree, tree);
 extern unsigned int default_case_values_threshold (void);
 extern bool default_have_conditional_execution (void);
 
-extern bool default_libc_has_function (enum function_class);
+extern bool default_libc_has_function (enum function_class, tree);
 extern bool default_libc_has_fast_function (int fcode);
-extern bool no_c99_libc_has_function (enum function_class);
-extern bool gnu_libc_has_function (enum function_class);
+extern bool no_c99_libc_has_function (enum function_class, tree);
+extern bool gnu_libc_has_function (enum function_class, tree);
 
 extern tree default_builtin_tm_load_store (tree);
 
diff --git a/gcc/tree-ssa-math-opts.c b/gcc/tree-ssa-math-opts.c
index 8423caa3ee3..605212add5b 100644
--- a/gcc/tree-ssa-math-opts.c
+++ b/gcc/tree-ssa-math-opts.c
@@ -2176,12 +2176,14 @@  pass_cse_sincos::execute (function *fun)
 		CASE_CFN_COS:
 		CASE_CFN_SIN:
 		CASE_CFN_CEXPI:
+		  arg = gimple_call_arg (stmt, 0);
 		  /* Make sure we have either sincos or cexp.  */
-		  if (!targetm.libc_has_function (function_c99_math_complex)
-		      && !targetm.libc_has_function (function_sincos))
+		  if (!targetm.libc_has_function (function_c99_math_complex,
+						  NULL_TREE)
+		      && !targetm.libc_has_function (function_sincos,
+						     TREE_TYPE (arg)))
 		    break;
 
-		  arg = gimple_call_arg (stmt, 0);
 		  if (TREE_CODE (arg) == SSA_NAME)
 		    cfg_changed |= execute_cse_sincos_1 (arg);
 		  break;