diff options
author | Joseph Myers <joseph@codesourcery.com> | 2019-08-21 12:06:44 +0000 |
---|---|---|
committer | Joseph Myers <joseph@codesourcery.com> | 2019-08-21 12:06:44 +0000 |
commit | f9fabc1b02d252d5d9b2e0df406bb394b8a7b46a (patch) | |
tree | 94dc8834a10f10cf0b464e7b0c49b2abffadda84 | |
parent | b72971845ae1be24724f8f94e70d5e7f1c5e15fc (diff) | |
download | glibc-f9fabc1b02d252d5d9b2e0df406bb394b8a7b46a.tar.gz glibc-f9fabc1b02d252d5d9b2e0df406bb394b8a7b46a.tar.xz glibc-f9fabc1b02d252d5d9b2e0df406bb394b8a7b46a.zip |
Add tgmath.h macros for narrowing functions.
When adding some of the TS 18661 narrowing functions for glibc 2.28, I deferred adding corresponding <tgmath.h> support because of unresolved questions about the specification for those type-generic macros, especially in relation to _FloatN and _FloatNx types. Those issues are now clarified in the response to Clarification Request 13 to TS 18661-3, and this patch adds the deferred tgmath.h support. As with other tgmath.h macros, there are fairly straightforward implementations based on __builtin_tgmath for GCC 8 and later, which result in exactly the right function being called in each case, and more complicated implementations for GCC 7 and earlier, which generally result in a function being called whose arguments have the right format (i.e. an alias for the right function), but which might not be exactly the function name specified by TS 18661. In one case with older compilers (f32x* macros, where the type _Float64x exists and all the arguments have type _Float32 or _Float32x), there is a further relaxation and the function called may have arguments narrower than the one specified by the TS, but still wide enough to represent the arguments exactly, so the result of the call is unchanged (as this does not affect any case where rounding of integer arguments might be involved). With GCC 6 or before this is inherently unavoidable (but still harmless and not detectable by how the compiled program behaves, unless it redefines the functions in question like the testcases do) because _Float32x and _Float64 are both typedefs for double in that case but the specified semantics result in different functions, with different argument formats, being called for those two argument types. Tests for the new macros are handled through gen-tgmath-tests.py, which deals with the special-case handling for older GCC. Tested as follows: with the full glibc testsuite on x86_64 and x86 (with GCC 6, 7 and 8); with the math/ tests on aarch64 and arm (with GCC 6, 7 and 8); with build-many-glibcs.py (with GCC 6, 7 and 9). * math/tgmath.h [__HAVE_FLOAT128X]: Give error. [(__HAVE_FLOAT64X && !__HAVE_FLOAT128) || (__HAVE_FLOAT128 && !__HAVE_FLOAT64X)]: Likewise. (__TGMATH_2_NARROW_F): Likewise. (__TGMATH_2_NARROW_D): New macro. (__TGMATH_2_NARROW_F16): Likewise. (__TGMATH_2_NARROW_F32): Likewise. (__TGMATH_2_NARROW_F64): Likewise. (__TGMATH_2_NARROW_F32X): Likewise. (__TGMATH_2_NARROW_F64X): Likewise. [__HAVE_BUILTIN_TGMATH] (__TGMATH_NARROW_FUNCS_F): Likewise. [__HAVE_BUILTIN_TGMATH] (__TGMATH_NARROW_FUNCS_F16): Likewise. [__HAVE_BUILTIN_TGMATH] (__TGMATH_NARROW_FUNCS_F32): Likewise. [__HAVE_BUILTIN_TGMATH] (__TGMATH_NARROW_FUNCS_F64): Likewise. [__HAVE_BUILTIN_TGMATH] (__TGMATH_NARROW_FUNCS_F32X): Likewise. [__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (fadd): Likewise. [__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (dadd): Likewise. [__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (fdiv): Likewise. [__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (ddiv): Likewise. [__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (fmul): Likewise. [__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (dmul): Likewise. [__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (fsub): Likewise. [__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (dsub): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT16] (f16add): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT16] (f16div): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT16] (f16mul): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT16] (f16sub): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32] (f32add): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32] (f32div): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32] (f32mul): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32] (f32sub): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64 && (__HAVE_FLOAT64X || __HAVE_FLOAT128)] (f64add): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64 && (__HAVE_FLOAT64X || __HAVE_FLOAT128)] (f64div): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64 && (__HAVE_FLOAT64X || __HAVE_FLOAT128)] (f64mul): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64 && (__HAVE_FLOAT64X || __HAVE_FLOAT128)] (f64sub): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32X] (f32xadd): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32X] (f32xdiv): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32X] (f32xmul): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32X] (f32xsub): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64X && (__HAVE_FLOAT128X || __HAVE_FLOAT128)] (f64xadd): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64X && (__HAVE_FLOAT128X || __HAVE_FLOAT128)] (f64xdiv): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64X && (__HAVE_FLOAT128X || __HAVE_FLOAT128)] (f64xmul): Likewise. [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64X && (__HAVE_FLOAT128X || __HAVE_FLOAT128)] (f64xsub): Likewise. * math/gen-tgmath-tests.py (Type): Add members non_standard_real_argument_types_list, long_double_type, complex_float64_type and float32x_ext_type. (Type.__init__): Set the new members. (Type.floating_type): Add new argument floatn. (Type.real_floating_type): Likewise. (Type.can_combine_types): Likewise. (Type.combine_types): Likewise. (Type.init_types): Create internal Float32x_ext type. (Tests.__init__): Define Float32x_ext in generated C code. (Tests.add_tests): Handle narrowing functions. (Tests.add_all_tests): Likewise. (Tests.tests_text): Allow variation in mant_dig for narrowing functions with compilers before GCC 8. * math/Makefile (tgmath3-narrow-types): New variable. (tgmath3-narrow-macros): Likewise. (tgmath3-macros): Add $(tgmath3-narrow-macros).
-rw-r--r-- | ChangeLog | 83 | ||||
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | math/Makefile | 6 | ||||
-rwxr-xr-x | math/gen-tgmath-tests.py | 184 | ||||
-rw-r--r-- | math/tgmath.h | 167 |
5 files changed, 425 insertions, 20 deletions
diff --git a/ChangeLog b/ChangeLog index 2f8b8761ca..ee96090964 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,86 @@ +2019-08-21 Joseph Myers <joseph@codesourcery.com> + + * math/tgmath.h [__HAVE_FLOAT128X]: Give error. + [(__HAVE_FLOAT64X && !__HAVE_FLOAT128) + || (__HAVE_FLOAT128 && !__HAVE_FLOAT64X)]: Likewise. + (__TGMATH_2_NARROW_F): Likewise. + (__TGMATH_2_NARROW_D): New macro. + (__TGMATH_2_NARROW_F16): Likewise. + (__TGMATH_2_NARROW_F32): Likewise. + (__TGMATH_2_NARROW_F64): Likewise. + (__TGMATH_2_NARROW_F32X): Likewise. + (__TGMATH_2_NARROW_F64X): Likewise. + [__HAVE_BUILTIN_TGMATH] (__TGMATH_NARROW_FUNCS_F): Likewise. + [__HAVE_BUILTIN_TGMATH] (__TGMATH_NARROW_FUNCS_F16): Likewise. + [__HAVE_BUILTIN_TGMATH] (__TGMATH_NARROW_FUNCS_F32): Likewise. + [__HAVE_BUILTIN_TGMATH] (__TGMATH_NARROW_FUNCS_F64): Likewise. + [__HAVE_BUILTIN_TGMATH] (__TGMATH_NARROW_FUNCS_F32X): Likewise. + [__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (fadd): Likewise. + [__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (dadd): Likewise. + [__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (fdiv): Likewise. + [__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (ddiv): Likewise. + [__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (fmul): Likewise. + [__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (dmul): Likewise. + [__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (fsub): Likewise. + [__GLIBC_USE (IEC_60559_BFP_EXT_C2X)] (dsub): Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT16] (f16add): + Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT16] (f16div): + Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT16] (f16mul): + Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT16] (f16sub): + Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32] (f32add): + Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32] (f32div): + Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32] (f32mul): + Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32] (f32sub): + Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64 + && (__HAVE_FLOAT64X || __HAVE_FLOAT128)] (f64add): Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64 + && (__HAVE_FLOAT64X || __HAVE_FLOAT128)] (f64div): Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64 + && (__HAVE_FLOAT64X || __HAVE_FLOAT128)] (f64mul): Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64 + && (__HAVE_FLOAT64X || __HAVE_FLOAT128)] (f64sub): Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32X] (f32xadd): + Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32X] (f32xdiv): + Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32X] (f32xmul): + Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT32X] (f32xsub): + Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64X + && (__HAVE_FLOAT128X || __HAVE_FLOAT128)] (f64xadd): Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64X + && (__HAVE_FLOAT128X || __HAVE_FLOAT128)] (f64xdiv): Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64X + && (__HAVE_FLOAT128X || __HAVE_FLOAT128)] (f64xmul): Likewise. + [__GLIBC_USE (IEC_60559_TYPES_EXT) && __HAVE_FLOAT64X + && (__HAVE_FLOAT128X || __HAVE_FLOAT128)] (f64xsub): Likewise. + * math/gen-tgmath-tests.py (Type): Add members + non_standard_real_argument_types_list, long_double_type, + complex_float64_type and float32x_ext_type. + (Type.__init__): Set the new members. + (Type.floating_type): Add new argument floatn. + (Type.real_floating_type): Likewise. + (Type.can_combine_types): Likewise. + (Type.combine_types): Likewise. + (Type.init_types): Create internal Float32x_ext type. + (Tests.__init__): Define Float32x_ext in generated C code. + (Tests.add_tests): Handle narrowing functions. + (Tests.add_all_tests): Likewise. + (Tests.tests_text): Allow variation in mant_dig for narrowing + functions with compilers before GCC 8. + * math/Makefile (tgmath3-narrow-types): New variable. + (tgmath3-narrow-macros): Likewise. + (tgmath3-macros): Add $(tgmath3-narrow-macros). + 2019-08-20 Andreas Schwab <schwab@suse.de> * sysdeps/i386/fpu/libm-test-ulps: Update. diff --git a/NEWS b/NEWS index 045720b3fb..a64b89986a 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,11 @@ Major new features: liable to change. Features from C2X are also enabled by _GNU_SOURCE, or by compiling with "gcc -std=gnu2x". +* The <math.h> functions that round their results to a narrower type now + have corresponding type-generic macros in <tgmath.h>, as defined in TS + 18661-1:2014 and TS 18661-3:2015 as amended by the resolution of + Clarification Request 13 to TS 18661-3. + Deprecated and removed features, and other changes affecting compatibility: * The totalorder and totalordermag functions, and the corresponding diff --git a/math/Makefile b/math/Makefile index 15c864c98b..247e31d7b9 100644 --- a/math/Makefile +++ b/math/Makefile @@ -368,6 +368,10 @@ $(foreach t,$(libm-tests-base),\ $(foreach t,$(libm-tests-base),\ $(objpfx)$(t)-compat_totalordermag.o): $(objpfx)libm-test-totalordermag.c +# _Float128x omitted as not supported by gen-tgmath-tests.py. +tgmath3-narrow-types = f d f16 f32 f64 f128 f32x f64x +tgmath3-narrow-macros = $(foreach t,$(tgmath3-narrow-types), \ + $(foreach f,$(libm-narrow-fns),$(t)$(f))) tgmath3-macros = atan2 cbrt ceil copysign erf erfc exp2 expm1 fdim floor \ fma fmax fmin fmod frexp hypot ilogb ldexp lgamma llrint \ llround log10 log1p log2 logb lrint lround nearbyint \ @@ -375,7 +379,7 @@ tgmath3-macros = atan2 cbrt ceil copysign erf erfc exp2 expm1 fdim floor \ scalbln tgamma trunc acos asin atan acosh asinh atanh cos \ sin tan cosh sinh tanh exp log pow sqrt fabs carg cimag conj \ cproj creal roundeven nextup nextdown fminmag fmaxmag llogb \ - fromfp fromfpx ufromfp ufromfpx scalb + fromfp fromfpx ufromfp ufromfpx scalb $(tgmath3-narrow-macros) tgmath3-macro-tests = $(addprefix test-tgmath3-,$(tgmath3-macros)) tests += $(tgmath3-macro-tests) generated += $(addsuffix .c,$(tgmath3-macro-tests)) diff --git a/math/gen-tgmath-tests.py b/math/gen-tgmath-tests.py index 2a7d94e737..1c098440f4 100755 --- a/math/gen-tgmath-tests.py +++ b/math/gen-tgmath-tests.py @@ -69,17 +69,27 @@ class Type(object): # Real argument types that correspond to a standard floating type # (float, double or long double; not _FloatN or _FloatNx). standard_real_argument_types_list = [] + # Real argument types other than float, double and long double + # (i.e., those that are valid as arguments to narrowing macros + # returning _FloatN or _FloatNx). + non_standard_real_argument_types_list = [] # The real floating types by their order properties (which are # tuples giving the positions in both the possible orders above). real_types_order = {} # The type double. double_type = None + # The type long double. + long_double_type = None # The type _Complex double. complex_double_type = None # The type _Float64. float64_type = None + # The type _Complex _Float64. + complex_float64_type = None # The type _Float64x. float64x_type = None + # The type _Float64x if available, otherwise _Float64. + float32x_ext_type = None def __init__(self, name, suffix=None, mant_dig=None, condition='1', order=None, integer=False, complex=False, real_type=None): @@ -109,16 +119,24 @@ class Type(object): Type.real_argument_types_list.append(self) if not self.name.startswith('_Float'): Type.standard_real_argument_types_list.append(self) + if self.name not in ('float', 'double', 'long double'): + Type.non_standard_real_argument_types_list.append(self) if self.order is not None: Type.real_types_order[self.order] = self if self.name == 'double': Type.double_type = self + if self.name == 'long double': + Type.long_double_type = self if self.name == '_Complex double': Type.complex_double_type = self if self.name == '_Float64': Type.float64_type = self + if self.name == '_Complex _Float64': + Type.complex_float64_type = self if self.name == '_Float64x': Type.float64x_type = self + if self.name == 'Float32x_ext': + Type.float32x_ext_type = self @staticmethod def create_type(name, suffix=None, mant_dig=None, condition='1', order=None, @@ -142,18 +160,23 @@ class Type(object): if complex_type is not None: complex_type.register_type(internal) - def floating_type(self): + def floating_type(self, floatn): """Return the corresponding floating type.""" if self.integer: - return (Type.complex_double_type - if self.complex - else Type.double_type) + if floatn: + return (Type.complex_float64_type + if self.complex + else Type.float64_type) + else: + return (Type.complex_double_type + if self.complex + else Type.double_type) else: return self - def real_floating_type(self): + def real_floating_type(self, floatn): """Return the corresponding real floating type.""" - return self.real_type.floating_type() + return self.real_type.floating_type(floatn) def __str__(self): """Return string representation of a type.""" @@ -212,15 +235,21 @@ class Type(object): complex_name='complex_long_double_Float64x', condition='defined HUGE_VAL_F64X', order=(7, 7), internal=True) + # An internal type for the argument type used by f32x* + # narrowing macros (_Float64x if available, otherwise + # _Float64). + Type.create_type('Float32x_ext', None, 'FLT32X_EXT_MANT_DIG', + complex_name='complex_Float32x_ext', + condition='1', internal=True) @staticmethod - def can_combine_types(types): + def can_combine_types(types, floatn): """Return a C preprocessor conditional for whether the given list of types can be used together as type-generic macro arguments.""" have_long_double = False have_float128 = False for t in types: - t = t.real_floating_type() + t = t.real_floating_type(floatn) if t.name == 'long double': have_long_double = True if t.name == '_Float128' or t.name == '_Float64x': @@ -233,14 +262,14 @@ class Type(object): return '1' @staticmethod - def combine_types(types): + def combine_types(types, floatn): """Return the result of combining a set of types.""" have_complex = False combined = None for t in types: if t.complex: have_complex = True - t = t.real_floating_type() + t = t.real_floating_type(floatn) if combined is None: combined = t else: @@ -316,6 +345,7 @@ class Tests(object): ' const char *func_name;\n' ' const char *test_name;\n' ' int mant_dig;\n' + ' int narrow_mant_dig;\n' ' };\n' 'int num_pass, num_fail;\n' 'volatile int called_mant_dig;\n' @@ -345,8 +375,18 @@ class Tests(object): '# endif\n') float64x_text = if_cond_text([Type.float64x_type.condition], float64x_text) + float32x_ext_text = ('#ifdef HUGE_VAL_F64X\n' + 'typedef _Float64x Float32x_ext;\n' + 'typedef __CFLOAT64X complex_Float32x_ext;\n' + '# define FLT32X_EXT_MANT_DIG FLT64X_MANT_DIG\n' + '#else\n' + 'typedef _Float64 Float32x_ext;\n' + 'typedef __CFLOAT64 complex_Float32x_ext;\n' + '# define FLT32X_EXT_MANT_DIG FLT64_MANT_DIG\n' + '#endif\n') self.header_list.append(float64_text) self.header_list.append(float64x_text) + self.header_list.append(float32x_ext_text) self.types_seen = set() for t in Type.all_types_list: self.add_type_var(t.name, t.condition) @@ -377,6 +417,8 @@ class Tests(object): return have_complex = False func = macro + narrowing = False + narrowing_std = False if ret == 'c' or 'c' in args: # Complex-only. have_complex = True @@ -387,6 +429,49 @@ class Tests(object): have_complex = True if complex_func == None: complex_func = 'c%s' % func + # For narrowing macros, compute narrow_args, the list of + # argument types for which there is an actual corresponding + # function. If none of those types exist, or the return type + # does not exist, then the macro is not defined and no tests + # of it can be run. + if ret == 'float': + narrowing = True + narrowing_std = True + narrow_cond = '1' + narrow_args = [Type.double_type, Type.long_double_type] + narrow_fallback = Type.double_type + elif ret == 'double': + narrowing = True + narrowing_std = True + narrow_cond = '1' + narrow_args = [Type.long_double_type] + narrow_fallback = Type.long_double_type + elif ret.startswith('_Float'): + narrowing = True + narrow_args = [] + nret_type = None + narrow_fallback = None + for order, real_type in sorted(Type.real_types_order.items()): + if real_type.name == ret: + nret_type = real_type + elif nret_type and real_type.name.startswith('_Float'): + narrow_args.append(real_type) + if (narrow_fallback is None + and ret.endswith('x') == real_type.name.endswith('x')): + narrow_fallback = real_type + if narrow_args: + narrow_cond = ('(%s && (%s))' + % (nret_type.condition, + ' || '.join(t.condition + for t in narrow_args))) + if narrow_fallback is None: + narrow_fallback = narrow_args[0] + if ret == '_Float32x': + narrow_fallback = Type.float32x_ext_type + else: + # No possible argument types, even conditionally. + narrow_cond = '0' + narrowing_nonstd = narrowing and not narrowing_std types = [ret] + args for t in types: if t != 'c' and t != 'g' and t != 'r' and t != 's': @@ -400,6 +485,8 @@ class Tests(object): continue if ret == 's' and t.name.startswith('_Float'): continue + if narrowing and t not in narrow_args: + continue if ret == 'c': ret_name = t.complex_type.name elif ret == 'g': @@ -432,23 +519,56 @@ class Tests(object): '}\n' % (ret_name, dummy_func_name, t.real_type.suffix, ', '.join(arg_list), t.real_type.mant_dig, dummy_func_name)) - dummy_func = if_cond_text([t.condition], dummy_func) + if narrowing: + dummy_cond = [narrow_cond, t.condition] + else: + dummy_cond = [t.condition] + dummy_func = if_cond_text(dummy_cond, dummy_func) self.test_text_list.append(dummy_func) arg_types = [] for t in args: if t == 'g' or t == 'c': arg_types.append(Type.argument_types_list) elif t == 'r': - arg_types.append(Type.real_argument_types_list) + if narrowing_std: + arg_types.append(Type.standard_real_argument_types_list) + elif narrowing: + arg_types.append( + Type.non_standard_real_argument_types_list) + else: + arg_types.append(Type.real_argument_types_list) elif t == 's': arg_types.append(Type.standard_real_argument_types_list) arg_types_product = list_product(arg_types) test_num = 0 for this_args in arg_types_product: - comb_type = Type.combine_types(this_args) - can_comb = Type.can_combine_types(this_args) + comb_type = Type.combine_types(this_args, narrowing_nonstd) + if narrowing: + # As long as there are no integer arguments, and as + # long as the chosen argument type is as wide as all + # the floating-point arguments passed, the semantics + # of the macro call do not depend on the exact + # function chosen. In particular, for f32x functions + # when _Float64x exists, the chosen type should differ + # for _Float32x and _Float64 arguments, but it is not + # always possible to distinguish those types before + # GCC 7 and the implementation does not attempt to do + # so before GCC 8. + narrow_mant_dig = comb_type.real_type.mant_dig + for arg_type in this_args: + if arg_type.integer: + narrow_mant_dig = 0 + else: + narrow_mant_dig = 0 + if (narrowing + and comb_type not in narrow_args + and narrow_fallback is not None): + comb_type = narrow_fallback + can_comb = Type.can_combine_types(this_args, narrowing_nonstd) all_conds = [t.condition for t in this_args] all_conds.append(can_comb) + if narrowing: + all_conds.append(narrow_cond) any_complex = func == None for t in this_args: if t.complex: @@ -459,8 +579,9 @@ class Tests(object): test_func_name = 'test_%s_%d' % (macro, test_num) test_num += 1 mant_dig = comb_type.real_type.mant_dig - test_text = '%s, "%s", "%s", %s' % (test_func_name, func_name, - test_name, mant_dig) + test_text = '%s, "%s", "%s", %s, %s' % (test_func_name, func_name, + test_name, mant_dig, + narrow_mant_dig) test_text = ' { %s },\n' % test_text test_text = if_cond_text(all_conds, test_text) self.test_array_list.append(test_text) @@ -575,9 +696,16 @@ class Tests(object): self.add_tests('fromfpx', 'intmax_t', ['r', 'int', 'unsigned int']) self.add_tests('ufromfp', 'uintmax_t', ['r', 'int', 'unsigned int']) self.add_tests('ufromfpx', 'uintmax_t', ['r', 'int', 'unsigned int']) - # The functions that round their result to a narrower type, - # and the associated type-generic macros, are not yet - # supported by this script or by glibc. + for fn in ('add', 'div', 'mul', 'sub'): + for ret, prefix in (('float', 'f'), + ('double', 'd'), + ('_Float16', 'f16'), + ('_Float32', 'f32'), + ('_Float64', 'f64'), + ('_Float128', 'f128'), + ('_Float32x', 'f32x'), + ('_Float64x', 'f64x')): + self.add_tests(prefix + fn, ret, ['r', 'r']) # Miscellaneous functions. self.add_tests('scalb', 's', ['s', 's']) @@ -602,6 +730,24 @@ class Tests(object): ' && strcmp (called_func_name,\n' ' tests[i].func_name) == 0)\n' ' num_pass++;\n' + '#if !__GNUC_PREREQ (8, 0)\n' + ' else if (tests[i].narrow_mant_dig > 0\n' + ' && (called_mant_dig\n' + ' >= tests[i].narrow_mant_dig)\n' + ' && strcmp (called_func_name,\n' + ' tests[i].func_name) == 0)\n' + ' {\n' + ' num_pass++;\n' + ' printf ("Test %zu (%s):\\n"\n' + ' " Expected: %s precision %d\\n"\n' + ' " Actual: %s precision %d\\n"\n' + ' " (OK with old GCC)\\n\\n",\n' + ' i, tests[i].test_name,\n' + ' tests[i].func_name,\n' + ' tests[i].mant_dig,\n' + ' called_func_name, called_mant_dig);\n' + ' }\n' + '#endif\n' ' else\n' ' {\n' ' num_fail++;\n' diff --git a/math/tgmath.h b/math/tgmath.h index d9dfca459b..0c58cc9326 100644 --- a/math/tgmath.h +++ b/math/tgmath.h @@ -43,6 +43,25 @@ #if __GNUC_PREREQ (2, 7) +/* Certain cases of narrowing macros only need to call a single + function so cannot use __builtin_tgmath and do not need any + complicated logic. */ +# if __HAVE_FLOAT128X +# error "Unsupported _Float128x type for <tgmath.h>." +# endif +# if ((__HAVE_FLOAT64X && !__HAVE_FLOAT128) \ + || (__HAVE_FLOAT128 && !__HAVE_FLOAT64X)) +# error "Unsupported combination of types for <tgmath.h>." +# endif +# define __TGMATH_2_NARROW_D(F, X, Y) \ + (F ## l (X, Y)) +# define __TGMATH_2_NARROW_F64X(F, X, Y) \ + (F ## f128 (X, Y)) +# if !__HAVE_FLOAT128 +# define __TGMATH_2_NARROW_F32X(F, X, Y) \ + (F ## f64 (X, Y)) +# endif + # if __HAVE_BUILTIN_TGMATH # if __HAVE_FLOAT16 && __GLIBC_USE (IEC_60559_TYPES_EXT) @@ -94,6 +113,33 @@ # define __TGMATH_2C(F, C, X, Y) __builtin_tgmath (__TGMATH_RCFUNCS (F, C) \ (X), (Y)) +# define __TGMATH_NARROW_FUNCS_F(X) X, X ## l, +# define __TGMATH_NARROW_FUNCS_F16(X) \ + __TG_F32_ARG (X) __TG_F64_ARG (X) __TG_F128_ARG (X) \ + __TG_F32X_ARG (X) __TG_F64X_ARG (X) __TG_F128X_ARG (X) +# define __TGMATH_NARROW_FUNCS_F32(X) \ + __TG_F64_ARG (X) __TG_F128_ARG (X) \ + __TG_F32X_ARG (X) __TG_F64X_ARG (X) __TG_F128X_ARG (X) +# define __TGMATH_NARROW_FUNCS_F64(X) \ + __TG_F128_ARG (X) \ + __TG_F64X_ARG (X) __TG_F128X_ARG (X) +# define __TGMATH_NARROW_FUNCS_F32X(X) \ + __TG_F64X_ARG (X) __TG_F128X_ARG (X) \ + __TG_F64_ARG (X) __TG_F128_ARG (X) + +# define __TGMATH_2_NARROW_F(F, X, Y) \ + __builtin_tgmath (__TGMATH_NARROW_FUNCS_F (F) (X), (Y)) +# define __TGMATH_2_NARROW_F16(F, X, Y) \ + __builtin_tgmath (__TGMATH_NARROW_FUNCS_F16 (F) (X), (Y)) +# define __TGMATH_2_NARROW_F32(F, X, Y) \ + __builtin_tgmath (__TGMATH_NARROW_FUNCS_F32 (F) (X), (Y)) +# define __TGMATH_2_NARROW_F64(F, X, Y) \ + __builtin_tgmath (__TGMATH_NARROW_FUNCS_F64 (F) (X), (Y)) +# if __HAVE_FLOAT128 +# define __TGMATH_2_NARROW_F32X(F, X, Y) \ + __builtin_tgmath (__TGMATH_NARROW_FUNCS_F32X (F) (X), (Y)) +# endif + # else /* !__HAVE_BUILTIN_TGMATH. */ # ifdef __NO_LONG_DOUBLE_MATH @@ -493,6 +539,65 @@ : (__typeof ((__tgmath_complex_type (Val1)) 0 \ + (__tgmath_complex_type (Val2)) 0)) \ Cfct##f (Val1, Val2)))) + +# define __TGMATH_2_NARROW_F(F, X, Y) \ + (__extension__ (sizeof ((__tgmath_real_type (X)) 0 \ + + (__tgmath_real_type (Y)) 0) > sizeof (double) \ + ? F ## l (X, Y) \ + : F (X, Y))) +/* In most cases, these narrowing macro definitions based on sizeof + ensure that the function called has the right argument format, as + for other <tgmath.h> macros for compilers before GCC 8, but may not + have exactly the argument type (among the types with that format) + specified in the standard logic. + + In the case of macros for _Float32x return type, when _Float64x + exists, _Float64 arguments should result in the *f64 function being + called while _Float32x arguments should result in the *f64x + function being called. These cases cannot be distinguished using + sizeof (or at all if the types are typedefs rather than different + types). However, for these functions it is OK (does not affect the + final result) to call a function with any argument format at least + as wide as all the floating-point arguments, unless that affects + rounding of integer arguments. Integer arguments are considered to + have type _Float64, so the *f64 functions are preferred for f32x* + macros when no argument has a wider floating-point type. */ +# if __HAVE_FLOAT64X_LONG_DOUBLE && __HAVE_DISTINCT_FLOAT128 +# define __TGMATH_2_NARROW_F32(F, X, Y) \ + (__extension__ (sizeof ((__tgmath_real_type (X)) 0 \ + + (__tgmath_real_type (Y)) 0) > sizeof (_Float64) \ + ? __TGMATH_F128 ((X) + (Y), F, (X, Y)) \ + F ## f64x (X, Y) \ + : F ## f64 (X, Y))) +# define __TGMATH_2_NARROW_F64(F, X, Y) \ + (__extension__ (sizeof ((__tgmath_real_type (X)) 0 \ + + (__tgmath_real_type (Y)) 0) > sizeof (_Float64) \ + ? __TGMATH_F128 ((X) + (Y), F, (X, Y)) \ + F ## f64x (X, Y) \ + : F ## f128 (X, Y))) +# define __TGMATH_2_NARROW_F32X(F, X, Y) \ + (__extension__ (sizeof ((__tgmath_real_type (X)) 0 \ + + (__tgmath_real_type (Y)) 0) > sizeof (_Float64) \ + ? __TGMATH_F128 ((X) + (Y), F, (X, Y)) \ + F ## f64x (X, Y) \ + : F ## f64 (X, Y))) +# elif __HAVE_FLOAT128 +# define __TGMATH_2_NARROW_F32(F, X, Y) \ + (__extension__ (sizeof ((__tgmath_real_type (X)) 0 \ + + (__tgmath_real_type (Y)) 0) > sizeof (_Float64) \ + ? F ## f128 (X, Y) \ + : F ## f64 (X, Y))) +# define __TGMATH_2_NARROW_F64(F, X, Y) \ + (F ## f128 (X, Y)) +# define __TGMATH_2_NARROW_F32X(F, X, Y) \ + (__extension__ (sizeof ((__tgmath_real_type (X)) 0 \ + + (__tgmath_real_type (Y)) 0) > sizeof (_Float32x) \ + ? F ## f64x (X, Y) \ + : F ## f64 (X, Y))) +# else +# define __TGMATH_2_NARROW_F32(F, X, Y) \ + (F ## f64 (X, Y)) +# endif # endif /* !__HAVE_BUILTIN_TGMATH. */ #else # error "Unsupported compiler; you cannot use <tgmath.h>" @@ -739,4 +844,66 @@ /* Real part of Z. */ #define creal(Val) __TGMATH_UNARY_REAL_IMAG_RET_REAL_SAME (Val, creal) + +/* Narrowing functions. */ + +#if __GLIBC_USE (IEC_60559_BFP_EXT_C2X) + +/* Add. */ +# define fadd(Val1, Val2) __TGMATH_2_NARROW_F (fadd, Val1, Val2) +# define dadd(Val1, Val2) __TGMATH_2_NARROW_D (dadd, Val1, Val2) + +/* Divide. */ +# define fdiv(Val1, Val2) __TGMATH_2_NARROW_F (fdiv, Val1, Val2) +# define ddiv(Val1, Val2) __TGMATH_2_NARROW_D (ddiv, Val1, Val2) + +/* Multiply. */ +# define fmul(Val1, Val2) __TGMATH_2_NARROW_F (fmul, Val1, Val2) +# define dmul(Val1, Val2) __TGMATH_2_NARROW_D (dmul, Val1, Val2) + +/* Subtract. */ +# define fsub(Val1, Val2) __TGMATH_2_NARROW_F (fsub, Val1, Val2) +# define dsub(Val1, Val2) __TGMATH_2_NARROW_D (dsub, Val1, Val2) + +#endif + +#if __GLIBC_USE (IEC_60559_TYPES_EXT) + +# if __HAVE_FLOAT16 +# define f16add(Val1, Val2) __TGMATH_2_NARROW_F16 (f16add, Val1, Val2) +# define f16div(Val1, Val2) __TGMATH_2_NARROW_F16 (f16div, Val1, Val2) +# define f16mul(Val1, Val2) __TGMATH_2_NARROW_F16 (f16mul, Val1, Val2) +# define f16sub(Val1, Val2) __TGMATH_2_NARROW_F16 (f16sub, Val1, Val2) +# endif + +# if __HAVE_FLOAT32 +# define f32add(Val1, Val2) __TGMATH_2_NARROW_F32 (f32add, Val1, Val2) +# define f32div(Val1, Val2) __TGMATH_2_NARROW_F32 (f32div, Val1, Val2) +# define f32mul(Val1, Val2) __TGMATH_2_NARROW_F32 (f32mul, Val1, Val2) +# define f32sub(Val1, Val2) __TGMATH_2_NARROW_F32 (f32sub, Val1, Val2) +# endif + +# if __HAVE_FLOAT64 && (__HAVE_FLOAT64X || __HAVE_FLOAT128) +# define f64add(Val1, Val2) __TGMATH_2_NARROW_F64 (f64add, Val1, Val2) +# define f64div(Val1, Val2) __TGMATH_2_NARROW_F64 (f64div, Val1, Val2) +# define f64mul(Val1, Val2) __TGMATH_2_NARROW_F64 (f64mul, Val1, Val2) +# define f64sub(Val1, Val2) __TGMATH_2_NARROW_F64 (f64sub, Val1, Val2) +# endif + +# if __HAVE_FLOAT32X +# define f32xadd(Val1, Val2) __TGMATH_2_NARROW_F32X (f32xadd, Val1, Val2) +# define f32xdiv(Val1, Val2) __TGMATH_2_NARROW_F32X (f32xdiv, Val1, Val2) +# define f32xmul(Val1, Val2) __TGMATH_2_NARROW_F32X (f32xmul, Val1, Val2) +# define f32xsub(Val1, Val2) __TGMATH_2_NARROW_F32X (f32xsub, Val1, Val2) +# endif + +# if __HAVE_FLOAT64X && (__HAVE_FLOAT128X || __HAVE_FLOAT128) +# define f64xadd(Val1, Val2) __TGMATH_2_NARROW_F64X (f64xadd, Val1, Val2) +# define f64xdiv(Val1, Val2) __TGMATH_2_NARROW_F64X (f64xdiv, Val1, Val2) +# define f64xmul(Val1, Val2) __TGMATH_2_NARROW_F64X (f64xmul, Val1, Val2) +# define f64xsub(Val1, Val2) __TGMATH_2_NARROW_F64X (f64xsub, Val1, Val2) +# endif + +#endif + #endif /* tgmath.h */ |