diff options
Diffstat (limited to 'math/gen-tgmath-tests.py')
-rwxr-xr-x | math/gen-tgmath-tests.py | 184 |
1 files changed, 165 insertions, 19 deletions
diff --git a/math/gen-tgmath-tests.py b/math/gen-tgmath-tests.py index 2a7d94e..1c09844 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' |