v2: Add targetm.libm_function_max_error
Checks
Commit Message
Hi!
On Thu, Apr 27, 2023 at 10:34:59AM +0000, Richard Biener wrote:
> OK. As said the patch itself looks good to me, let's go ahead. We
> have plenty of time to backtrack until GCC 14.
Thanks. Unfortunately when I started using it, I've discovered that the
CASE_CFN_xxx_ALL macros don't include the CFN_xxx cases, just
CFN_BUILT_IN_xxx* cases.
So here is an updated version of the patch I'll bootstrap/regtest tonight
which instead uses CASE_CFN_xxx: CASE_CFN_xxx_FN:
2023-04-27 Jakub Jelinek <jakub@redhat.com>
* target.def (libm_function_max_error): New target hook.
* doc/tm.texi.in (TARGET_LIBM_FUNCTION_MAX_ERROR): Add.
* doc/tm.texi: Regenerated.
* targhooks.h (default_libm_function_max_error,
glibc_linux_libm_function_max_error): Declare.
* targhooks.cc: Include case-cfn-macros.h.
(default_libm_function_max_error,
glibc_linux_libm_function_max_error): New functions.
* config/linux.h (TARGET_LIBM_FUNCTION_MAX_ERROR): Redefine.
* config/linux-protos.h (linux_libm_function_max_error): Declare.
* config/linux.cc: Include target.h and targhooks.h.
(linux_libm_function_max_error): New function.
* config/arc/arc.cc: Include targhooks.h and case-cfn-macros.h.
(arc_libm_function_max_error): New function.
(TARGET_LIBM_FUNCTION_MAX_ERROR): Redefine.
* config/i386/i386.cc (ix86_libc_has_fast_function): Formatting fix.
(ix86_libm_function_max_error): New function.
(TARGET_LIBM_FUNCTION_MAX_ERROR): Redefine.
* config/rs6000/rs6000-protos.h
(rs6000_linux_libm_function_max_error): Declare.
* config/rs6000/rs6000-linux.cc: Include target.h, targhooks.h, tree.h
and case-cfn-macros.h.
(rs6000_linux_libm_function_max_error): New function.
* config/rs6000/linux.h (TARGET_LIBM_FUNCTION_MAX_ERROR): Redefine.
* config/rs6000/linux64.h (TARGET_LIBM_FUNCTION_MAX_ERROR): Redefine.
* config/or1k/or1k.cc: Include targhooks.h and case-cfn-macros.h.
(or1k_libm_function_max_error): New function.
(TARGET_LIBM_FUNCTION_MAX_ERROR): Redefine.
Jakub
Comments
Jakub Jelinek via Gcc-patches <gcc-patches@gcc.gnu.org> writes:
> Hi!
>
> On Thu, Apr 27, 2023 at 10:34:59AM +0000, Richard Biener wrote:
>> OK. As said the patch itself looks good to me, let's go ahead. We
>> have plenty of time to backtrack until GCC 14.
>
> Thanks. Unfortunately when I started using it, I've discovered that the
> CASE_CFN_xxx_ALL macros don't include the CFN_xxx cases, just
> CFN_BUILT_IN_xxx* cases.
>
> So here is an updated version of the patch I'll bootstrap/regtest tonight
> which instead uses CASE_CFN_xxx: CASE_CFN_xxx_FN:
Shouldn't we change something in that case? The point of these macros
is to wrap things up a single easy-to-use name, so something feels wrong
if we're having to use a repeated pattern like this.
Thanks,
Richard
> 2023-04-27 Jakub Jelinek <jakub@redhat.com>
>
> * target.def (libm_function_max_error): New target hook.
> * doc/tm.texi.in (TARGET_LIBM_FUNCTION_MAX_ERROR): Add.
> * doc/tm.texi: Regenerated.
> * targhooks.h (default_libm_function_max_error,
> glibc_linux_libm_function_max_error): Declare.
> * targhooks.cc: Include case-cfn-macros.h.
> (default_libm_function_max_error,
> glibc_linux_libm_function_max_error): New functions.
> * config/linux.h (TARGET_LIBM_FUNCTION_MAX_ERROR): Redefine.
> * config/linux-protos.h (linux_libm_function_max_error): Declare.
> * config/linux.cc: Include target.h and targhooks.h.
> (linux_libm_function_max_error): New function.
> * config/arc/arc.cc: Include targhooks.h and case-cfn-macros.h.
> (arc_libm_function_max_error): New function.
> (TARGET_LIBM_FUNCTION_MAX_ERROR): Redefine.
> * config/i386/i386.cc (ix86_libc_has_fast_function): Formatting fix.
> (ix86_libm_function_max_error): New function.
> (TARGET_LIBM_FUNCTION_MAX_ERROR): Redefine.
> * config/rs6000/rs6000-protos.h
> (rs6000_linux_libm_function_max_error): Declare.
> * config/rs6000/rs6000-linux.cc: Include target.h, targhooks.h, tree.h
> and case-cfn-macros.h.
> (rs6000_linux_libm_function_max_error): New function.
> * config/rs6000/linux.h (TARGET_LIBM_FUNCTION_MAX_ERROR): Redefine.
> * config/rs6000/linux64.h (TARGET_LIBM_FUNCTION_MAX_ERROR): Redefine.
> * config/or1k/or1k.cc: Include targhooks.h and case-cfn-macros.h.
> (or1k_libm_function_max_error): New function.
> (TARGET_LIBM_FUNCTION_MAX_ERROR): Redefine.
>
> --- gcc/target.def.jj 2023-04-27 10:17:32.598686398 +0200
> +++ gcc/target.def 2023-04-27 10:26:58.361490211 +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-27 10:17:32.596686427 +0200
> +++ gcc/doc/tm.texi.in 2023-04-27 10:26:58.362490196 +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-27 10:17:32.593686470 +0200
> +++ gcc/doc/tm.texi 2023-04-27 10:26:58.364490167 +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-27 10:17:32.598686398 +0200
> +++ gcc/targhooks.h 2023-04-27 10:26:58.364490167 +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-27 10:17:32.598686398 +0200
> +++ gcc/targhooks.cc 2023-04-27 12:19:32.831330868 +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,73 @@ 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:
> + CASE_CFN_SQRT_FN:
> + 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:
> + CASE_CFN_COS_FN:
> + /* 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:
> + CASE_CFN_SIN_FN:
> + 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-27 10:17:32.551687077 +0200
> +++ gcc/config/linux.h 2023-04-27 10:26:58.365490153 +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-27 10:17:32.551687077 +0200
> +++ gcc/config/linux-protos.h 2023-04-27 10:26:58.365490153 +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-27 10:17:32.551687077 +0200
> +++ gcc/config/linux.cc 2023-04-27 10:26:58.365490153 +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-04-27 10:17:32.546687149 +0200
> +++ gcc/config/arc/arc.cc 2023-04-27 12:20:28.155525126 +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,37 @@ 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:
> + CASE_CFN_SIN_FN:
> + if (!boundary_p && mode == DFmode)
> + return 7 + rnd;
> + break;
> + CASE_CFN_COS:
> + CASE_CFN_COS_FN:
> + 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 +11865,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-27 10:17:32.550687091 +0200
> +++ gcc/config/i386/i386.cc 2023-04-27 12:20:53.102161814 +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,58 @@ 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:
> + CASE_CFN_COS_FN:
> + CASE_CFN_SIN:
> + CASE_CFN_SIN_FN:
> + 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-27 10:17:32.589686528 +0200
> +++ gcc/config/rs6000/rs6000-protos.h 2023-04-27 10:26:58.370490081 +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-27 10:17:32.588686542 +0200
> +++ gcc/config/rs6000/rs6000-linux.cc 2023-04-27 12:21:29.024638633 +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,39 @@ 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:
> + CASE_CFN_SQRT_FN:
> + if (!boundary_p && MODE_COMPOSITE_P (mode))
> + return 1 + rnd;
> + break;
> + CASE_CFN_COS:
> + CASE_CFN_COS_FN:
> + if (!boundary_p && mode == SFmode)
> + return 3 + rnd;
> + if (!boundary_p && MODE_COMPOSITE_P (mode))
> + return 4 + rnd;
> + break;
> + CASE_CFN_SIN:
> + CASE_CFN_SIN_FN:
> + 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-04-27 10:17:32.588686542 +0200
> +++ gcc/config/rs6000/linux.h 2023-04-27 10:26:58.370490081 +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-04-27 10:17:32.588686542 +0200
> +++ gcc/config/rs6000/linux64.h 2023-04-27 10:26:58.370490081 +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-04-27 10:17:32.551687077 +0200
> +++ gcc/config/or1k/or1k.cc 2023-04-27 12:22:02.886145476 +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,32 @@ 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:
> + CASE_CFN_SIN_FN:
> + 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 +2242,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
>
>
> Jakub
On Fri, Apr 28, 2023 at 12:29:58PM +0100, Richard Sandiford wrote:
> Jakub Jelinek via Gcc-patches <gcc-patches@gcc.gnu.org> writes:
> > Hi!
> >
> > On Thu, Apr 27, 2023 at 10:34:59AM +0000, Richard Biener wrote:
> >> OK. As said the patch itself looks good to me, let's go ahead. We
> >> have plenty of time to backtrack until GCC 14.
> >
> > Thanks. Unfortunately when I started using it, I've discovered that the
> > CASE_CFN_xxx_ALL macros don't include the CFN_xxx cases, just
> > CFN_BUILT_IN_xxx* cases.
> >
> > So here is an updated version of the patch I'll bootstrap/regtest tonight
> > which instead uses CASE_CFN_xxx: CASE_CFN_xxx_FN:
>
> Shouldn't we change something in that case? The point of these macros
> is to wrap things up a single easy-to-use name, so something feels wrong
> if we're having to use a repeated pattern like this.
Maybe. But unfortunately not all builtins have those CFN_xxx enumerators,
some have just CFN_BUILT_IN_xxx{,L,F}, otherwise have
CFN_BUILT_IN_xxx{,L,F,F16,F32,F64,F128} and others have that plus CFN_xxx.
So we'd perhaps need some other macros for the all but CFN_xxx and perhaps
use ALL only for the cases where it is really all of them.
Jakub
@@ -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
@@ -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
@@ -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
@@ -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);
@@ -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,73 @@ 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:
+ CASE_CFN_SQRT_FN:
+ 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:
+ CASE_CFN_COS_FN:
+ /* 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:
+ CASE_CFN_SIN_FN:
+ 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))
@@ -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
@@ -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);
@@ -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);
+}
@@ -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,37 @@ 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:
+ CASE_CFN_SIN_FN:
+ if (!boundary_p && mode == DFmode)
+ return 7 + rnd;
+ break;
+ CASE_CFN_COS:
+ CASE_CFN_COS_FN:
+ 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 +11865,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"
@@ -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,58 @@ 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:
+ CASE_CFN_COS_FN:
+ CASE_CFN_SIN:
+ CASE_CFN_SIN_FN:
+ 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
@@ -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; }
@@ -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,39 @@ 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:
+ CASE_CFN_SQRT_FN:
+ if (!boundary_p && MODE_COMPOSITE_P (mode))
+ return 1 + rnd;
+ break;
+ CASE_CFN_COS:
+ CASE_CFN_COS_FN:
+ if (!boundary_p && mode == SFmode)
+ return 3 + rnd;
+ if (!boundary_p && MODE_COMPOSITE_P (mode))
+ return 4 + rnd;
+ break;
+ CASE_CFN_SIN:
+ CASE_CFN_SIN_FN:
+ 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);
+}
@@ -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 \
@@ -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 \
@@ -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,32 @@ 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:
+ CASE_CFN_SIN_FN:
+ 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 +2242,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