summary refs log tree commit diff
path: root/math/gen-tgmath-tests.py
diff options
context:
space:
mode:
Diffstat (limited to 'math/gen-tgmath-tests.py')
-rwxr-xr-xmath/gen-tgmath-tests.py213
1 files changed, 131 insertions, 82 deletions
diff --git a/math/gen-tgmath-tests.py b/math/gen-tgmath-tests.py
index c841db6000..be10c47877 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'