--- gcc/target.def.jj	2023-04-26 08:32:38.118070594 +0200
+++ gcc/target.def	2023-04-26 08:33:01.752718215 +0200
@@ -2670,6 +2670,23 @@ DEFHOOK
  bool, (int fcode),
  default_libc_has_fast_function)
 
+DEFHOOK
+(libm_function_max_error,
+ "This hook determines expected maximum errors for math functions measured\n\
+in ulps (units of the last place).  0 means 0.5ulps precision (correctly\n\
+rounded).  ~0U means unknown errors.  The @code{combined_fn} @var{cfn}\n\
+argument should identify just which math built-in function it is rather than\n\
+its variant, @var{mode} the variant in terms of floating-point machine mode.\n\
+The hook should also take into account @code{flag_rounding_math} whether it\n\
+is maximum error just in default rounding mode, or in all possible rounding\n\
+modes.  @var{boundary_p} is @code{true} for maximum errors on intrinsic math\n\
+boundaries of functions rather than errors inside of the usual result ranges\n\
+of the functions.  E.g.@ the sin/cos function finite result is in between\n\
+-1.0 and 1.0 inclusive, with @var{boundary_p} true the function returns how\n\
+many ulps below or above those boundaries result could be.",
+ unsigned, (unsigned cfn, machine_mode mode, bool boundary_p),
+ default_libm_function_max_error)
+
 /* True if new jumps cannot be created, to replace existing ones or
    not, at the current point in the compilation.  */
 DEFHOOK
--- gcc/doc/tm.texi.in.jj	2023-04-26 08:32:38.116070623 +0200
+++ gcc/doc/tm.texi.in	2023-04-26 08:33:01.751718229 +0200
@@ -4004,6 +4004,8 @@ macro, a reasonable default is used.
 
 @hook TARGET_LIBC_HAS_FAST_FUNCTION
 
+@hook TARGET_LIBM_FUNCTION_MAX_ERROR
+
 @defmac NEXT_OBJC_RUNTIME
 Set this macro to 1 to use the "NeXT" Objective-C message sending conventions
 by default.  This calling convention involves passing the object, the selector
--- gcc/doc/tm.texi.jj	2023-04-26 08:32:38.090071011 +0200
+++ gcc/doc/tm.texi	2023-04-26 08:33:01.750718244 +0200
@@ -5760,6 +5760,21 @@ This hook determines whether a function
 @code{(enum function_class)}@var{fcode} has a fast implementation.
 @end deftypefn
 
+@deftypefn {Target Hook} unsigned TARGET_LIBM_FUNCTION_MAX_ERROR (unsigned @var{cfn}, machine_mode @var{mode}, bool @var{boundary_p})
+This hook determines expected maximum errors for math functions measured
+in ulps (units of the last place).  0 means 0.5ulps precision (correctly
+rounded).  ~0U means unknown errors.  The @code{combined_fn} @var{cfn}
+argument should identify just which math built-in function it is rather than
+its variant, @var{mode} the variant in terms of floating-point machine mode.
+The hook should also take into account @code{flag_rounding_math} whether it
+is maximum error just in default rounding mode, or in all possible rounding
+modes.  @var{boundary_p} is @code{true} for maximum errors on intrinsic math
+boundaries of functions rather than errors inside of the usual result ranges
+of the functions.  E.g.@ the sin/cos function finite result is in between
+-1.0 and 1.0 inclusive, with @var{boundary_p} true the function returns how
+many ulps below or above those boundaries result could be.
+@end deftypefn
+
 @defmac NEXT_OBJC_RUNTIME
 Set this macro to 1 to use the "NeXT" Objective-C message sending conventions
 by default.  This calling convention involves passing the object, the selector
--- gcc/targhooks.h.jj	2023-04-26 08:32:38.118070594 +0200
+++ gcc/targhooks.h	2023-04-26 08:33:01.748718274 +0200
@@ -219,6 +219,9 @@ extern bool default_libc_has_fast_functi
 extern bool no_c99_libc_has_function (enum function_class, tree);
 extern bool gnu_libc_has_function (enum function_class, tree);
 extern bool bsd_libc_has_function (enum function_class, tree);
+extern unsigned default_libm_function_max_error (unsigned, machine_mode, bool);
+extern unsigned glibc_linux_libm_function_max_error (unsigned, machine_mode,
+						     bool);
 
 extern tree default_builtin_tm_load_store (tree);
 
--- gcc/targhooks.cc.jj	2023-04-26 08:32:38.118070594 +0200
+++ gcc/targhooks.cc	2023-04-26 13:21:26.776752299 +0200
@@ -94,6 +94,7 @@ along with GCC; see the file COPYING3.
 #include "cfgloop.h"
 #include "tree-vectorizer.h"
 #include "options.h"
+#include "case-cfn-macros.h"
 
 bool
 default_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
@@ -1903,6 +1904,70 @@ bsd_libc_has_function (enum function_cla
   return false;
 }
 
+unsigned
+default_libm_function_max_error (unsigned, machine_mode, bool)
+{
+  return ~0U;
+}
+
+unsigned
+glibc_linux_libm_function_max_error (unsigned cfn, machine_mode mode,
+				     bool boundary_p)
+{
+  /* Let's use
+     https://www.gnu.org/software/libc/manual/2.22/html_node/Errors-in-Math-Functions.html
+     https://www.gnu.org/software/libc/manual/html_node/Errors-in-Math-Functions.html
+     with usual values recorded here and significant outliers handled in
+     target CPU specific overriders.  The tables only record default
+     rounding to nearest, for -frounding-math let's add some extra ulps.
+     For boundary_p values (say finite results outside of [-1.,1.] for
+     sin/cos, or [-0.,+Inf] for sqrt etc. let's use custom random testers.  */
+  int rnd = flag_rounding_math ? 4 : 0;
+  bool sf = (REAL_MODE_FORMAT (mode) == &ieee_single_format
+	     || REAL_MODE_FORMAT (mode) == &mips_single_format
+	     || REAL_MODE_FORMAT (mode) == &motorola_single_format);
+  bool df = (REAL_MODE_FORMAT (mode) == &ieee_double_format
+	     || REAL_MODE_FORMAT (mode) == &mips_double_format
+	     || REAL_MODE_FORMAT (mode) == &motorola_double_format);
+  bool xf = (REAL_MODE_FORMAT (mode) == &ieee_extended_intel_96_format
+	     || REAL_MODE_FORMAT (mode) == &ieee_extended_intel_128_format
+	     || REAL_MODE_FORMAT (mode) == &ieee_extended_motorola_format);
+  bool tf = (REAL_MODE_FORMAT (mode) == &ieee_quad_format
+	     || REAL_MODE_FORMAT (mode) == &mips_quad_format);
+
+  switch (cfn)
+    {
+    CASE_CFN_SQRT_ALL:
+      if (boundary_p)
+	/* https://gcc.gnu.org/pipermail/gcc-patches/2023-April/616595.html */
+	return 0;
+      if (sf || df || xf || tf)
+	return 0 + rnd;
+      break;
+    CASE_CFN_COS_ALL:
+      /* cos is generally errors like sin, but far more arches have 2ulps
+	 for double.  */
+      if (!boundary_p && df)
+	return 2 + rnd;
+      gcc_fallthrough ();
+    CASE_CFN_SIN_ALL:
+      if (boundary_p)
+	/* According to
+	   https://sourceware.org/pipermail/gcc-patches/2023-April/616315.html
+	   seems default rounding sin/cos stay strictly in [-1.,1.] range,
+	   with rounding to infinity it can be 1ulp larger/smaller.  */
+	return flag_rounding_math ? 1 : 0;
+      if (sf || df)
+	return 1 + rnd;
+      if (xf || tf)
+	return 2 + rnd;
+      break;
+    default:
+      break;
+    }
+
+  return default_libm_function_max_error (cfn, mode, boundary_p);
+}
 
 tree
 default_builtin_tm_load_store (tree ARG_UNUSED (type))
--- gcc/config/linux.h.jj	2023-04-26 08:32:38.055071533 +0200
+++ gcc/config/linux.h	2023-04-26 08:33:01.747718289 +0200
@@ -212,4 +212,7 @@ see the files COPYING3 and COPYING.RUNTI
 # undef TARGET_LIBC_HAS_FUNCTION
 # define TARGET_LIBC_HAS_FUNCTION linux_libc_has_function
 
+# undef TARGET_LIBM_FUNCTION_MAX_ERROR
+# define TARGET_LIBM_FUNCTION_MAX_ERROR linux_libm_function_max_error
+
 #endif
--- gcc/config/linux-protos.h.jj	2023-04-26 08:32:38.055071533 +0200
+++ gcc/config/linux-protos.h	2023-04-26 08:33:01.747718289 +0200
@@ -20,3 +20,5 @@ along with GCC; see the file COPYING3.
 extern bool linux_has_ifunc_p (void);
 
 extern bool linux_libc_has_function (enum function_class fn_class, tree);
+
+extern unsigned linux_libm_function_max_error (unsigned, machine_mode, bool);
--- gcc/config/linux.cc.jj	2023-04-26 08:32:38.055071533 +0200
+++ gcc/config/linux.cc	2023-04-26 08:33:01.747718289 +0200
@@ -23,6 +23,8 @@ along with GCC; see the file COPYING3.
 #include "tm.h"
 #include "tree.h"
 #include "linux-protos.h"
+#include "target.h"
+#include "targhooks.h"
 
 bool
 linux_libc_has_function (enum function_class fn_class,
@@ -38,3 +40,12 @@ linux_libc_has_function (enum function_c
 
   return false;
 }
+
+unsigned
+linux_libm_function_max_error (unsigned cfn, machine_mode mode,
+			       bool boundary_p)
+{
+  if (OPTION_GLIBC)
+    return glibc_linux_libm_function_max_error (cfn, mode, boundary_p);
+  return default_libm_function_max_error (cfn, mode, boundary_p);
+}
--- gcc/config/arc/arc.cc.jj	2023-01-02 09:33:00.219738627 +0100
+++ gcc/config/arc/arc.cc	2023-04-26 14:59:20.440175058 +0200
@@ -68,6 +68,8 @@ along with GCC; see the file COPYING3.
 #include "alias.h"
 #include "opts.h"
 #include "hw-doloop.h"
+#include "targhooks.h"
+#include "case-cfn-macros.h"
 
 /* Which cpu we're compiling for (ARC600, ARC601, ARC700).  */
 static char arc_cpu_name[10] = "";
@@ -11808,6 +11810,35 @@ arc_insn_cost (rtx_insn *insn, bool spee
   return cost;
 }
 
+static unsigned
+arc_libm_function_max_error (unsigned cfn, machine_mode mode,
+			     bool boundary_p)
+{
+#ifdef OPTION_GLIBC
+  bool glibc_p = OPTION_GLIBC;
+#else
+  bool glibc_p = false;
+#endif
+  if (glibc_p)
+    {
+      int rnd = flag_rounding_math ? 4 : 0;
+      switch (cfn)
+	{
+	CASE_CFN_SIN_ALL:
+	  if (!boundary_p && mode == DFmode)
+	    return 7 + rnd;
+	  break;
+	CASE_CFN_COS_ALL:
+	  if (!boundary_p && mode == DFmode)
+	    return 4 + rnd;
+	default:
+	  break;
+	}
+      return glibc_linux_libm_function_max_error (cfn, mode, boundary_p);
+    }
+  return default_libm_function_max_error (cfn, mode, boundary_p);
+}
+
 #undef TARGET_USE_ANCHORS_FOR_SYMBOL_P
 #define TARGET_USE_ANCHORS_FOR_SYMBOL_P arc_use_anchors_for_symbol_p
 
@@ -11832,6 +11863,9 @@ arc_insn_cost (rtx_insn *insn, bool spee
 #undef  TARGET_INSN_COST
 #define TARGET_INSN_COST arc_insn_cost
 
+#undef  TARGET_LIBM_FUNCTION_MAX_ERROR
+#define TARGET_LIBM_FUNCTION_MAX_ERROR arc_libm_function_max_error
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-arc.h"
--- gcc/config/i386/i386.cc.jj	2023-04-22 10:23:40.539613820 +0200
+++ gcc/config/i386/i386.cc	2023-04-26 13:37:56.273154402 +0200
@@ -25250,7 +25250,8 @@ ix86_libgcc_floating_mode_supported_p
 #undef TARGET_MEMTAG_TAG_SIZE
 #define TARGET_MEMTAG_TAG_SIZE ix86_memtag_tag_size
 
-static bool ix86_libc_has_fast_function (int fcode ATTRIBUTE_UNUSED)
+static bool
+ix86_libc_has_fast_function (int fcode ATTRIBUTE_UNUSED)
 {
 #ifdef OPTION_GLIBC
   if (OPTION_GLIBC)
@@ -25265,6 +25266,56 @@ static bool ix86_libc_has_fast_function
 #undef TARGET_LIBC_HAS_FAST_FUNCTION
 #define TARGET_LIBC_HAS_FAST_FUNCTION ix86_libc_has_fast_function
 
+static unsigned
+ix86_libm_function_max_error (unsigned cfn, machine_mode mode,
+			      bool boundary_p)
+{
+#ifdef OPTION_GLIBC
+  bool glibc_p = OPTION_GLIBC;
+#else
+  bool glibc_p = false;
+#endif
+  if (glibc_p)
+    {
+      /* If __FAST_MATH__ is defined, glibc provides libmvec.  */
+      unsigned int libmvec_ret = 0;
+      if (!flag_trapping_math
+	  && flag_unsafe_math_optimizations
+	  && flag_finite_math_only
+	  && !flag_signed_zeros
+	  && !flag_errno_math)
+	switch (cfn)
+	  {
+	  CASE_CFN_COS_ALL:
+	  CASE_CFN_SIN_ALL:
+	    if (!boundary_p)
+	      {
+		/* With non-default rounding modes, libmvec provides
+		   complete garbage in results.  E.g.
+		   _ZGVcN8v_sinf for 1.40129846e-45f in FE_UPWARD
+		   returns 0.00333309174f rather than 1.40129846e-45f.  */
+		if (flag_rounding_math)
+		  return ~0U;
+		/* https://www.gnu.org/software/libc/manual/html_node/Errors-in-Math-Functions.html
+		   claims libmvec maximum error is 4ulps.
+		   My own random testing indicates 2ulps for SFmode and
+		   0.5ulps for DFmode, but let's go with the 4ulps.  */
+		libmvec_ret = 4;
+	      }
+	    break;
+	  default:
+	    break;
+	  }
+      unsigned int ret = glibc_linux_libm_function_max_error (cfn, mode,
+							      boundary_p);
+      return MAX (ret, libmvec_ret);
+    }
+  return default_libm_function_max_error (cfn, mode, boundary_p);
+}
+
+#undef TARGET_LIBM_FUNCTION_MAX_ERROR
+#define TARGET_LIBM_FUNCTION_MAX_ERROR ix86_libm_function_max_error
+
 #if CHECKING_P
 #undef TARGET_RUN_TARGET_SELFTESTS
 #define TARGET_RUN_TARGET_SELFTESTS selftest::ix86_run_selftests
--- gcc/config/rs6000/rs6000-protos.h.jj	2023-04-26 08:32:38.072071280 +0200
+++ gcc/config/rs6000/rs6000-protos.h	2023-04-26 08:33:01.748718274 +0200
@@ -334,6 +334,8 @@ extern unsigned char rs6000_class_max_nr
 extern unsigned char rs6000_hard_regno_nregs[][FIRST_PSEUDO_REGISTER];
 
 extern bool rs6000_linux_float_exceptions_rounding_supported_p (void);
+extern unsigned rs6000_linux_libm_function_max_error (unsigned, machine_mode,
+						      bool);
 
 /* Pass management.  */
 namespace gcc { class context; }
--- gcc/config/rs6000/rs6000-linux.cc.jj	2023-04-26 08:32:38.055071533 +0200
+++ gcc/config/rs6000/rs6000-linux.cc	2023-04-26 13:30:57.231335122 +0200
@@ -23,6 +23,10 @@ along with GCC; see the file COPYING3.
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
+#include "target.h"
+#include "targhooks.h"
+#include "tree.h"
+#include "case-cfn-macros.h"
 
 /* Implement TARGET_FLOAT_EXCEPTIONS_ROUNDING_SUPPORTED_P.  */
 
@@ -36,3 +40,36 @@ rs6000_linux_float_exceptions_rounding_s
   else
     return TARGET_HARD_FLOAT;
 }
+
+/* Implement TARGET_LIBM_FUNCTION_MAX_ERROR.  */
+
+unsigned
+rs6000_linux_libm_function_max_error (unsigned cfn, machine_mode mode,
+				      bool boundary_p)
+{
+  if (OPTION_GLIBC)
+    {
+      int rnd = flag_rounding_math ? 4 : 0;
+      switch (cfn)
+	{
+	CASE_CFN_SQRT_ALL:
+	  if (!boundary_p && MODE_COMPOSITE_P (mode))
+	    return 1 + rnd;
+	  break;
+	CASE_CFN_COS_ALL:
+	  if (!boundary_p && mode == SFmode)
+	    return 3 + rnd;
+	  if (!boundary_p && MODE_COMPOSITE_P (mode))
+	    return 4 + rnd;
+	  break;
+	CASE_CFN_SIN_ALL:
+	  if (!boundary_p && MODE_COMPOSITE_P (mode))
+	    return 1 + rnd;
+	  break;
+	default:
+	  break;
+	}
+      return glibc_linux_libm_function_max_error (cfn, mode, boundary_p);
+    }
+  return default_libm_function_max_error (cfn, mode, boundary_p);
+}
--- gcc/config/rs6000/linux.h.jj	2023-01-16 11:52:16.022734963 +0100
+++ gcc/config/rs6000/linux.h	2023-04-26 15:57:23.636976113 +0200
@@ -50,6 +50,9 @@
 #undef TARGET_LIBC_HAS_FUNCTION
 #define TARGET_LIBC_HAS_FUNCTION linux_libc_has_function
 
+#undef TARGET_LIBM_FUNCTION_MAX_ERROR
+#define TARGET_LIBM_FUNCTION_MAX_ERROR rs6000_linux_libm_function_max_error
+
 #undef  TARGET_OS_CPP_BUILTINS
 #define TARGET_OS_CPP_BUILTINS()			\
   do							\
--- gcc/config/rs6000/linux64.h.jj	2023-01-16 11:52:16.022734963 +0100
+++ gcc/config/rs6000/linux64.h	2023-04-26 15:57:32.019853082 +0200
@@ -288,6 +288,9 @@ extern int dot_symbols;
 #undef TARGET_LIBC_HAS_FUNCTION
 #define TARGET_LIBC_HAS_FUNCTION linux_libc_has_function
 
+#undef TARGET_LIBM_FUNCTION_MAX_ERROR
+#define TARGET_LIBM_FUNCTION_MAX_ERROR rs6000_linux_libm_function_max_error
+
 #undef  TARGET_OS_CPP_BUILTINS
 #define TARGET_OS_CPP_BUILTINS()			\
   do							\
--- gcc/config/or1k/or1k.cc.jj	2023-01-16 11:52:16.004735229 +0100
+++ gcc/config/or1k/or1k.cc	2023-04-26 15:02:23.434480484 +0200
@@ -44,6 +44,8 @@
 #include "explow.h"
 #include "cfgrtl.h"
 #include "alias.h"
+#include "targhooks.h"
+#include "case-cfn-macros.h"
 
 /* These 4 are needed to allow using satisfies_constraint_J.  */
 #include "insn-config.h"
@@ -2191,6 +2193,31 @@ or1k_output_mi_thunk (FILE *file, tree t
   epilogue_completed = 0;
 }
 
+static unsigned
+or1k_libm_function_max_error (unsigned cfn, machine_mode mode,
+			      bool boundary_p)
+{
+#ifdef OPTION_GLIBC
+  bool glibc_p = OPTION_GLIBC;
+#else
+  bool glibc_p = false;
+#endif
+  if (glibc_p)
+    {
+      switch (cfn)
+	{
+	CASE_CFN_SIN_ALL:
+	  if (!boundary_p && mode == DFmode && flag_rounding_math)
+	    return 7;
+	  break;
+	default:
+	  break;
+	}
+      return glibc_linux_libm_function_max_error (cfn, mode, boundary_p);
+    }
+  return default_libm_function_max_error (cfn, mode, boundary_p);
+}
+
 #undef  TARGET_ASM_OUTPUT_MI_THUNK
 #define TARGET_ASM_OUTPUT_MI_THUNK or1k_output_mi_thunk
 #undef  TARGET_ASM_CAN_OUTPUT_MI_THUNK
@@ -2214,6 +2241,9 @@ or1k_output_mi_thunk (FILE *file, tree t
 #undef  TARGET_HAVE_SPECULATION_SAFE_VALUE
 #define TARGET_HAVE_SPECULATION_SAFE_VALUE speculation_safe_value_not_needed
 
+#undef  TARGET_LIBM_FUNCTION_MAX_ERROR
+#define TARGET_LIBM_FUNCTION_MAX_ERROR or1k_libm_function_max_error
+
 /* Calling Conventions.  */
 #undef  TARGET_FUNCTION_VALUE
 #define TARGET_FUNCTION_VALUE or1k_function_value
