diff options
Diffstat (limited to 'math/gen-tgmath-tests.py')
-rwxr-xr-x | math/gen-tgmath-tests.py | 213 |
1 files changed, 131 insertions, 82 deletions
diff --git a/math/gen-tgmath-tests.py b/math/gen-tgmath-tests.py index c841db6..be10c47 100755 --- a/math/gen-tgmath-tests.py +++ b/math/gen-tgmath-tests.py @@ -19,14 +19,13 @@ # As glibc does not support decimal floating point, the types to # consider for generic parameters are standard and binary -# floating-point types, and integer types which are treated as double. -# The corresponding complex types may also be used (including complex -# integer types, which are a GNU extension, but are currently disabled -# here because they do not work properly with tgmath.h). - -# The proposed resolution to TS 18661-1 DR#9 -# <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2149.htm#dr_9> -# makes the <tgmath.h> rules for selecting a function to call +# floating-point types, and integer types which are treated as +# _Float32x if any argument has a _FloatNx type and otherwise as +# double. The corresponding complex types may also be used (including +# complex integer types, which are a GNU extension, but are currently +# disabled here because they do not work properly with tgmath.h). + +# C2x makes the <tgmath.h> rules for selecting a function to call # correspond to the usual arithmetic conversions (applied successively # to the arguments for generic parameters in order), which choose the # type whose set of values contains that of the other type (undefined @@ -69,10 +68,6 @@ 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 = {} @@ -86,13 +81,16 @@ class Type(object): float64_type = None # The type _Complex _Float64. complex_float64_type = None + # The type _Float32x. + float32x_type = None + # The type _Complex _Float32x. + complex_float32x_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): + order=None, integer=False, complex=False, real_type=None, + floatnx=False): """Initialize a Type object, creating any corresponding complex type in the process.""" self.name = name @@ -102,6 +100,7 @@ class Type(object): self.order = order self.integer = integer self.complex = complex + self.floatnx = floatnx if complex: self.complex_type = self self.real_type = real_type @@ -119,8 +118,6 @@ 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': @@ -133,26 +130,28 @@ class Type(object): Type.float64_type = self if self.name == '_Complex _Float64': Type.complex_float64_type = self + if self.name == '_Float32x': + Type.float32x_type = self + if self.name == '_Complex _Float32x': + Type.complex_float32x_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, integer=False, complex_name=None, complex_ok=True, - internal=False): + floatnx=False, internal=False): """Create and register a Type object for a real type, creating any corresponding complex type in the process.""" real_type = Type(name, suffix=suffix, mant_dig=mant_dig, condition=condition, order=order, integer=integer, - complex=False) + complex=False, floatnx=floatnx) if complex_ok: if complex_name is None: complex_name = '_Complex %s' % name complex_type = Type(complex_name, condition=condition, integer=integer, complex=True, - real_type=real_type) + real_type=real_type, floatnx=floatnx) else: complex_type = None real_type.complex_type = complex_type @@ -160,13 +159,13 @@ class Type(object): if complex_type is not None: complex_type.register_type(internal) - def floating_type(self, floatn): + def floating_type(self, integer_float32x): """Return the corresponding floating type.""" if self.integer: - if floatn: - return (Type.complex_float64_type + if integer_float32x: + return (Type.complex_float32x_type if self.complex - else Type.float64_type) + else Type.float32x_type) else: return (Type.complex_double_type if self.complex @@ -174,9 +173,9 @@ class Type(object): else: return self - def real_floating_type(self, floatn): + def real_floating_type(self, integer_float32x): """Return the corresponding real floating type.""" - return self.real_type.floating_type(floatn) + return self.real_type.floating_type(integer_float32x) def __str__(self): """Return string representation of a type.""" @@ -194,7 +193,8 @@ class Type(object): condition='defined HUGE_VAL_F32', order=(2, 2)) Type.create_type('_Float32x', 'f32x', 'FLT32X_MANT_DIG', complex_name='__CFLOAT32X', - condition='defined HUGE_VAL_F32X', order=(3, 3)) + condition='defined HUGE_VAL_F32X', order=(3, 3), + floatnx=True) Type.create_type('double', '', 'DBL_MANT_DIG', order=(4, 4)) Type.create_type('long double', 'l', 'LDBL_MANT_DIG', order=(5, 7)) Type.create_type('_Float64', 'f64', 'FLT64_MANT_DIG', @@ -202,7 +202,8 @@ class Type(object): condition='defined HUGE_VAL_F64', order=(6, 5)) Type.create_type('_Float64x', 'f64x', 'FLT64X_MANT_DIG', complex_name='__CFLOAT64X', - condition='defined HUGE_VAL_F64X', order=(7, 6)) + condition='defined HUGE_VAL_F64X', order=(7, 6), + floatnx=True) Type.create_type('_Float128', 'f128', 'FLT128_MANT_DIG', complex_name='__CFLOAT128', condition='defined HUGE_VAL_F128', order=(8, 8)) @@ -235,21 +236,16 @@ 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, floatn): + def can_combine_types(types): """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 + integer_float32x = any(t.floatnx for t in types) for t in types: - t = t.real_floating_type(floatn) + t = t.real_floating_type(integer_float32x) if t.name == 'long double': have_long_double = True if t.name == '_Float128' or t.name == '_Float64x': @@ -262,14 +258,15 @@ class Type(object): return '1' @staticmethod - def combine_types(types, floatn): + def combine_types(types): """Return the result of combining a set of types.""" have_complex = False combined = None + integer_float32x = any(t.floatnx for t in types) for t in types: if t.complex: have_complex = True - t = t.real_floating_type(floatn) + t = t.real_floating_type(integer_float32x) if combined is None: combined = t else: @@ -375,18 +372,8 @@ 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) @@ -439,39 +426,33 @@ class Tests(object): 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 = [] + narrow_args_1 = [] + narrow_args_2 = [] 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 ret.endswith('x') == real_type.name.endswith('x'): + narrow_args_1.append(real_type) + else: + narrow_args_2.append(real_type) + narrow_args = narrow_args_1 + narrow_args_2 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': @@ -530,19 +511,13 @@ class Tests(object): if t == 'g' or t == 'c': arg_types.append(Type.argument_types_list) elif t == 'r': - 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) + 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, narrowing_nonstd) + comb_type = Type.combine_types(this_args) if narrowing: # As long as there are no integer arguments, and as # long as the chosen argument type is as wide as all @@ -550,22 +525,22 @@ class Tests(object): # 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. + # for double / _Float32x and _Float64 arguments, but + # it is not always possible to distinguish those types + # before GCC 7 (resulting in some cases - only real + # arguments - where a wider argument type is used, + # which is semantically OK, and others - integer + # arguments present - where it may not be OK, but is + # unavoidable). 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) + can_comb = Type.can_combine_types(this_args) all_conds = [t.condition for t in this_args] + narrow_args_cond = '(%s)' % ' && '.join(sorted(set(all_conds))) all_conds.append(can_comb) if narrowing: all_conds.append(narrow_cond) @@ -579,10 +554,69 @@ class Tests(object): test_func_name = 'test_%s_%d' % (macro, test_num) test_num += 1 mant_dig = comb_type.real_type.mant_dig + test_mant_dig_comp = '' + if (narrowing + and comb_type not in narrow_args): + # The expected argument type is the first in + # narrow_args that can represent all the values of + # comb_type (which, for the supported cases, means the + # first with mant_dig at least as large as that for + # comb_type, provided this isn't the case of an IBM + # long double argument with binary128 type from + # narrow_args). + narrow_extra_conds = [] + test_mant_dig_list = ['#undef NARROW_MANT_DIG\n#if 0\n'] + for t in narrow_args: + t_cond = '(%s && %s && %s <= %s && %s)' % ( + narrow_args_cond, t.condition, mant_dig, t.mant_dig, + Type.can_combine_types(this_args + [t])) + narrow_extra_conds.append(t_cond) + test_mant_dig_list.append('#elif %s\n' + '#define NARROW_MANT_DIG %s\n' + % (t_cond, t.mant_dig)) + test_mant_dig_list.append('#endif\n') + test_mant_dig_comp = ''.join(test_mant_dig_list) + all_conds.append('(%s)' % ' || '.join(narrow_extra_conds)) + # A special case where this logic isn't correct is + # where comb_type is the internal long_double_Float64 + # or long_double_Float64x, which will be detected as + # not in narrow_args even if the actual type chosen in + # a particular configuration would have been in + # narrow_args, so check for that case and handle it + # appropriately. In particular, if long double has + # the same format as double and there are long double + # and _Float64 arguments, and the macro returns + # _Float32x, the function called should be one for + # _Float64 arguments, not one for _Float64x arguments + # that would arise from this logic. + if comb_type.real_type.name == 'long_double_Float64': + comb_type_1 = Type.long_double_type + comb_type_2 = Type.float64_type + comb_type_is_2_cond = 'LDBL_MANT_DIG <= FLT64_MANT_DIG' + elif comb_type.real_type.name == 'long_double_Float64x': + comb_type_1 = Type.long_double_type + comb_type_2 = Type.float64x_type + comb_type_is_2_cond = 'LDBL_MANT_DIG < FLT64X_MANT_DIG' + else: + comb_type_1 = None + comb_type_2 = None + if comb_type_1 is None: + mant_dig = 'NARROW_MANT_DIG' + else: + mant_dig = '' + if comb_type_1 in narrow_args: + mant_dig += '!(%s) ? %s : ' % (comb_type_is_2_cond, + comb_type_1.mant_dig) + if comb_type_2 in narrow_args: + mant_dig += '%s ? %s : ' % (comb_type_is_2_cond, + comb_type_2.mant_dig) + mant_dig += 'NARROW_MANT_DIG' + if narrow_mant_dig != 0: + narrow_mant_dig = 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 = '%s { %s },\n' % (test_mant_dig_comp, test_text) test_text = if_cond_text(all_conds, test_text) self.test_array_list.append(test_text) call_args = [] @@ -742,7 +776,7 @@ class Tests(object): ' && strcmp (called_func_name,\n' ' tests[i].func_name) == 0)\n' ' num_pass++;\n' - '#if !__GNUC_PREREQ (8, 0)\n' + '#if !__GNUC_PREREQ (7, 0)\n' ' else if (tests[i].narrow_mant_dig > 0\n' ' && (called_mant_dig\n' ' >= tests[i].narrow_mant_dig)\n' @@ -759,6 +793,21 @@ class Tests(object): ' tests[i].mant_dig,\n' ' called_func_name, called_mant_dig);\n' ' }\n' + ' else if (tests[i].narrow_mant_dig == 0\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' + ' " (unavoidable 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' |