]>
Commit | Line | Data |
---|---|---|
4f88b380 | 1 | #!/usr/bin/python3 |
2dd0aec5 | 2 | # Generate tests for <tgmath.h> macros. |
2b778ceb | 3 | # Copyright (C) 2017-2021 Free Software Foundation, Inc. |
2dd0aec5 JM |
4 | # This file is part of the GNU C Library. |
5 | # | |
6 | # The GNU C Library is free software; you can redistribute it and/or | |
7 | # modify it under the terms of the GNU Lesser General Public | |
8 | # License as published by the Free Software Foundation; either | |
9 | # version 2.1 of the License, or (at your option) any later version. | |
10 | # | |
11 | # The GNU C Library is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | # Lesser General Public License for more details. | |
15 | # | |
16 | # You should have received a copy of the GNU Lesser General Public | |
17 | # License along with the GNU C Library; if not, see | |
5a82c748 | 18 | # <https://www.gnu.org/licenses/>. |
2dd0aec5 JM |
19 | |
20 | # As glibc does not support decimal floating point, the types to | |
21 | # consider for generic parameters are standard and binary | |
22 | # floating-point types, and integer types which are treated as double. | |
23 | # The corresponding complex types may also be used (including complex | |
24 | # integer types, which are a GNU extension, but are currently disabled | |
25 | # here because they do not work properly with tgmath.h). | |
26 | ||
27 | # The proposed resolution to TS 18661-1 DR#9 | |
28 | # <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2149.htm#dr_9> | |
29 | # makes the <tgmath.h> rules for selecting a function to call | |
30 | # correspond to the usual arithmetic conversions (applied successively | |
31 | # to the arguments for generic parameters in order), which choose the | |
32 | # type whose set of values contains that of the other type (undefined | |
33 | # behavior if neither type's set of values is a superset of the | |
34 | # other), with interchange types being preferred to standard types | |
35 | # (long double, double, float), being preferred to extended types | |
36 | # (_Float128x, _Float64x, _Float32x). | |
37 | ||
38 | # For the standard and binary floating-point types supported by GCC 7 | |
39 | # on any platform, this means the resulting type is the last of the | |
40 | # given types in one of the following orders, or undefined behavior if | |
41 | # types with both ibm128 and binary128 representation are specified. | |
42 | ||
43 | # If double = long double: _Float16, float, _Float32, _Float32x, | |
44 | # double, long double, _Float64, _Float64x, _Float128. | |
45 | ||
46 | # Otherwise: _Float16, float, _Float32, _Float32x, double, _Float64, | |
47 | # _Float64x, long double, _Float128. | |
48 | ||
49 | # We generate tests to verify the return type is exactly as expected. | |
50 | # We also verify that the function called is real or complex as | |
51 | # expected, and that it is called for the right floating-point format | |
52 | # (but it is OK to call a double function instead of a long double one | |
53 | # if they have the same format, for example). For all the formats | |
54 | # supported on any given configuration of glibc, the MANT_DIG value | |
55 | # uniquely determines the format. | |
56 | ||
57 | import string | |
7c67e6e8 | 58 | import sys |
2dd0aec5 JM |
59 | |
60 | class Type(object): | |
61 | """A type that may be used as an argument for generic parameters.""" | |
62 | ||
63 | # All possible argument or result types. | |
64 | all_types_list = [] | |
65 | # All argument types. | |
66 | argument_types_list = [] | |
67 | # All real argument types. | |
68 | real_argument_types_list = [] | |
69 | # Real argument types that correspond to a standard floating type | |
70 | # (float, double or long double; not _FloatN or _FloatNx). | |
71 | standard_real_argument_types_list = [] | |
f9fabc1b JM |
72 | # Real argument types other than float, double and long double |
73 | # (i.e., those that are valid as arguments to narrowing macros | |
74 | # returning _FloatN or _FloatNx). | |
75 | non_standard_real_argument_types_list = [] | |
2dd0aec5 JM |
76 | # The real floating types by their order properties (which are |
77 | # tuples giving the positions in both the possible orders above). | |
78 | real_types_order = {} | |
79 | # The type double. | |
80 | double_type = None | |
f9fabc1b JM |
81 | # The type long double. |
82 | long_double_type = None | |
2dd0aec5 JM |
83 | # The type _Complex double. |
84 | complex_double_type = None | |
85 | # The type _Float64. | |
86 | float64_type = None | |
f9fabc1b JM |
87 | # The type _Complex _Float64. |
88 | complex_float64_type = None | |
2dd0aec5 JM |
89 | # The type _Float64x. |
90 | float64x_type = None | |
f9fabc1b JM |
91 | # The type _Float64x if available, otherwise _Float64. |
92 | float32x_ext_type = None | |
2dd0aec5 JM |
93 | |
94 | def __init__(self, name, suffix=None, mant_dig=None, condition='1', | |
95 | order=None, integer=False, complex=False, real_type=None): | |
96 | """Initialize a Type object, creating any corresponding complex type | |
97 | in the process.""" | |
98 | self.name = name | |
99 | self.suffix = suffix | |
100 | self.mant_dig = mant_dig | |
101 | self.condition = condition | |
102 | self.order = order | |
103 | self.integer = integer | |
104 | self.complex = complex | |
105 | if complex: | |
106 | self.complex_type = self | |
107 | self.real_type = real_type | |
108 | else: | |
109 | # complex_type filled in by the caller once created. | |
110 | self.complex_type = None | |
111 | self.real_type = self | |
112 | ||
113 | def register_type(self, internal): | |
114 | """Record a type in the lists of all types.""" | |
115 | Type.all_types_list.append(self) | |
116 | if not internal: | |
117 | Type.argument_types_list.append(self) | |
118 | if not self.complex: | |
119 | Type.real_argument_types_list.append(self) | |
120 | if not self.name.startswith('_Float'): | |
121 | Type.standard_real_argument_types_list.append(self) | |
f9fabc1b JM |
122 | if self.name not in ('float', 'double', 'long double'): |
123 | Type.non_standard_real_argument_types_list.append(self) | |
2dd0aec5 JM |
124 | if self.order is not None: |
125 | Type.real_types_order[self.order] = self | |
126 | if self.name == 'double': | |
127 | Type.double_type = self | |
f9fabc1b JM |
128 | if self.name == 'long double': |
129 | Type.long_double_type = self | |
2dd0aec5 JM |
130 | if self.name == '_Complex double': |
131 | Type.complex_double_type = self | |
132 | if self.name == '_Float64': | |
133 | Type.float64_type = self | |
f9fabc1b JM |
134 | if self.name == '_Complex _Float64': |
135 | Type.complex_float64_type = self | |
2dd0aec5 JM |
136 | if self.name == '_Float64x': |
137 | Type.float64x_type = self | |
f9fabc1b JM |
138 | if self.name == 'Float32x_ext': |
139 | Type.float32x_ext_type = self | |
2dd0aec5 JM |
140 | |
141 | @staticmethod | |
142 | def create_type(name, suffix=None, mant_dig=None, condition='1', order=None, | |
143 | integer=False, complex_name=None, complex_ok=True, | |
144 | internal=False): | |
145 | """Create and register a Type object for a real type, creating any | |
146 | corresponding complex type in the process.""" | |
147 | real_type = Type(name, suffix=suffix, mant_dig=mant_dig, | |
148 | condition=condition, order=order, integer=integer, | |
149 | complex=False) | |
d9bef9c0 | 150 | if complex_ok: |
2dd0aec5 JM |
151 | if complex_name is None: |
152 | complex_name = '_Complex %s' % name | |
153 | complex_type = Type(complex_name, condition=condition, | |
154 | integer=integer, complex=True, | |
155 | real_type=real_type) | |
156 | else: | |
157 | complex_type = None | |
158 | real_type.complex_type = complex_type | |
159 | real_type.register_type(internal) | |
160 | if complex_type is not None: | |
161 | complex_type.register_type(internal) | |
162 | ||
f9fabc1b | 163 | def floating_type(self, floatn): |
2dd0aec5 JM |
164 | """Return the corresponding floating type.""" |
165 | if self.integer: | |
f9fabc1b JM |
166 | if floatn: |
167 | return (Type.complex_float64_type | |
168 | if self.complex | |
169 | else Type.float64_type) | |
170 | else: | |
171 | return (Type.complex_double_type | |
172 | if self.complex | |
173 | else Type.double_type) | |
2dd0aec5 JM |
174 | else: |
175 | return self | |
176 | ||
f9fabc1b | 177 | def real_floating_type(self, floatn): |
2dd0aec5 | 178 | """Return the corresponding real floating type.""" |
f9fabc1b | 179 | return self.real_type.floating_type(floatn) |
2dd0aec5 JM |
180 | |
181 | def __str__(self): | |
182 | """Return string representation of a type.""" | |
183 | return self.name | |
184 | ||
185 | @staticmethod | |
186 | def init_types(): | |
187 | """Initialize all the known types.""" | |
188 | Type.create_type('_Float16', 'f16', 'FLT16_MANT_DIG', | |
189 | complex_name='__CFLOAT16', | |
614d15f9 | 190 | condition='defined HUGE_VAL_F16', order=(0, 0)) |
2dd0aec5 JM |
191 | Type.create_type('float', 'f', 'FLT_MANT_DIG', order=(1, 1)) |
192 | Type.create_type('_Float32', 'f32', 'FLT32_MANT_DIG', | |
193 | complex_name='__CFLOAT32', | |
614d15f9 | 194 | condition='defined HUGE_VAL_F32', order=(2, 2)) |
2dd0aec5 JM |
195 | Type.create_type('_Float32x', 'f32x', 'FLT32X_MANT_DIG', |
196 | complex_name='__CFLOAT32X', | |
614d15f9 | 197 | condition='defined HUGE_VAL_F32X', order=(3, 3)) |
2dd0aec5 JM |
198 | Type.create_type('double', '', 'DBL_MANT_DIG', order=(4, 4)) |
199 | Type.create_type('long double', 'l', 'LDBL_MANT_DIG', order=(5, 7)) | |
200 | Type.create_type('_Float64', 'f64', 'FLT64_MANT_DIG', | |
201 | complex_name='__CFLOAT64', | |
614d15f9 | 202 | condition='defined HUGE_VAL_F64', order=(6, 5)) |
2dd0aec5 JM |
203 | Type.create_type('_Float64x', 'f64x', 'FLT64X_MANT_DIG', |
204 | complex_name='__CFLOAT64X', | |
614d15f9 | 205 | condition='defined HUGE_VAL_F64X', order=(7, 6)) |
2dd0aec5 JM |
206 | Type.create_type('_Float128', 'f128', 'FLT128_MANT_DIG', |
207 | complex_name='__CFLOAT128', | |
614d15f9 | 208 | condition='defined HUGE_VAL_F128', order=(8, 8)) |
2dd0aec5 JM |
209 | Type.create_type('char', integer=True) |
210 | Type.create_type('signed char', integer=True) | |
211 | Type.create_type('unsigned char', integer=True) | |
212 | Type.create_type('short int', integer=True) | |
213 | Type.create_type('unsigned short int', integer=True) | |
214 | Type.create_type('int', integer=True) | |
215 | Type.create_type('unsigned int', integer=True) | |
216 | Type.create_type('long int', integer=True) | |
217 | Type.create_type('unsigned long int', integer=True) | |
218 | Type.create_type('long long int', integer=True) | |
219 | Type.create_type('unsigned long long int', integer=True) | |
42df8d59 JM |
220 | Type.create_type('__int128', integer=True, |
221 | condition='defined __SIZEOF_INT128__') | |
222 | Type.create_type('unsigned __int128', integer=True, | |
223 | condition='defined __SIZEOF_INT128__') | |
2dd0aec5 JM |
224 | Type.create_type('enum e', integer=True, complex_ok=False) |
225 | Type.create_type('_Bool', integer=True, complex_ok=False) | |
2fee621d | 226 | Type.create_type('bit_field', integer=True, complex_ok=False) |
2dd0aec5 JM |
227 | # Internal types represent the combination of long double with |
228 | # _Float64 or _Float64x, for which the ordering depends on | |
229 | # whether long double has the same format as double. | |
01e659e7 | 230 | Type.create_type('long_double_Float64', None, 'LDBL_MANT_DIG', |
2dd0aec5 | 231 | complex_name='complex_long_double_Float64', |
614d15f9 JM |
232 | condition='defined HUGE_VAL_F64', order=(6, 7), |
233 | internal=True) | |
01e659e7 | 234 | Type.create_type('long_double_Float64x', None, 'FLT64X_MANT_DIG', |
2dd0aec5 | 235 | complex_name='complex_long_double_Float64x', |
614d15f9 JM |
236 | condition='defined HUGE_VAL_F64X', order=(7, 7), |
237 | internal=True) | |
f9fabc1b JM |
238 | # An internal type for the argument type used by f32x* |
239 | # narrowing macros (_Float64x if available, otherwise | |
240 | # _Float64). | |
241 | Type.create_type('Float32x_ext', None, 'FLT32X_EXT_MANT_DIG', | |
242 | complex_name='complex_Float32x_ext', | |
243 | condition='1', internal=True) | |
2dd0aec5 JM |
244 | |
245 | @staticmethod | |
f9fabc1b | 246 | def can_combine_types(types, floatn): |
2dd0aec5 JM |
247 | """Return a C preprocessor conditional for whether the given list of |
248 | types can be used together as type-generic macro arguments.""" | |
249 | have_long_double = False | |
250 | have_float128 = False | |
251 | for t in types: | |
f9fabc1b | 252 | t = t.real_floating_type(floatn) |
2dd0aec5 JM |
253 | if t.name == 'long double': |
254 | have_long_double = True | |
255 | if t.name == '_Float128' or t.name == '_Float64x': | |
256 | have_float128 = True | |
257 | if have_long_double and have_float128: | |
258 | # If ibm128 format is in use for long double, both | |
259 | # _Float64x and _Float128 are binary128 and the types | |
260 | # cannot be combined. | |
261 | return '(LDBL_MANT_DIG != 106)' | |
262 | return '1' | |
263 | ||
264 | @staticmethod | |
f9fabc1b | 265 | def combine_types(types, floatn): |
2dd0aec5 JM |
266 | """Return the result of combining a set of types.""" |
267 | have_complex = False | |
268 | combined = None | |
269 | for t in types: | |
270 | if t.complex: | |
271 | have_complex = True | |
f9fabc1b | 272 | t = t.real_floating_type(floatn) |
2dd0aec5 JM |
273 | if combined is None: |
274 | combined = t | |
275 | else: | |
276 | order = (max(combined.order[0], t.order[0]), | |
277 | max(combined.order[1], t.order[1])) | |
278 | combined = Type.real_types_order[order] | |
279 | return combined.complex_type if have_complex else combined | |
280 | ||
281 | def list_product_initial(initial, lists): | |
282 | """Return a list of lists, with an initial sequence from the first | |
283 | argument (a list of lists) followed by each sequence of one | |
284 | element from each successive element of the second argument.""" | |
285 | if not lists: | |
286 | return initial | |
287 | return list_product_initial([a + [b] for a in initial for b in lists[0]], | |
288 | lists[1:]) | |
289 | ||
290 | def list_product(lists): | |
291 | """Return a list of lists, with each sequence of one element from each | |
292 | successive element of the argument.""" | |
293 | return list_product_initial([[]], lists) | |
294 | ||
295 | try: | |
296 | trans_id = str.maketrans(' *', '_p') | |
297 | except AttributeError: | |
298 | trans_id = string.maketrans(' *', '_p') | |
299 | def var_for_type(name): | |
300 | """Return the name of a variable with a given type (name).""" | |
301 | return 'var_%s' % name.translate(trans_id) | |
302 | ||
303 | def vol_var_for_type(name): | |
304 | """Return the name of a variable with a given volatile type (name).""" | |
305 | return 'vol_var_%s' % name.translate(trans_id) | |
306 | ||
307 | def define_vars_for_type(name): | |
308 | """Return the definitions of variables with a given type (name).""" | |
2fee621d JM |
309 | if name == 'bit_field': |
310 | struct_vars = define_vars_for_type('struct s'); | |
311 | return '%s#define %s %s.bf\n' % (struct_vars, | |
312 | vol_var_for_type(name), | |
313 | vol_var_for_type('struct s')) | |
2dd0aec5 JM |
314 | return ('%s %s __attribute__ ((unused));\n' |
315 | '%s volatile %s __attribute__ ((unused));\n' | |
316 | % (name, var_for_type(name), name, vol_var_for_type(name))) | |
317 | ||
318 | def if_cond_text(conds, text): | |
319 | """Return the result of making some text conditional under #if. The | |
320 | text ends with a newline, as does the return value if not empty.""" | |
321 | if '0' in conds: | |
322 | return '' | |
323 | conds = [c for c in conds if c != '1'] | |
324 | conds = sorted(set(conds)) | |
325 | if not conds: | |
326 | return text | |
327 | return '#if %s\n%s#endif\n' % (' && '.join(conds), text) | |
328 | ||
329 | class Tests(object): | |
330 | """The state associated with testcase generation.""" | |
331 | ||
332 | def __init__(self): | |
333 | """Initialize a Tests object.""" | |
fa562680 JM |
334 | self.header_list = ['#define __STDC_WANT_IEC_60559_TYPES_EXT__\n' |
335 | '#include <float.h>\n' | |
2dd0aec5 JM |
336 | '#include <stdbool.h>\n' |
337 | '#include <stdint.h>\n' | |
338 | '#include <stdio.h>\n' | |
339 | '#include <string.h>\n' | |
340 | '#include <tgmath.h>\n' | |
341 | '\n' | |
342 | 'struct test\n' | |
343 | ' {\n' | |
344 | ' void (*func) (void);\n' | |
345 | ' const char *func_name;\n' | |
346 | ' const char *test_name;\n' | |
347 | ' int mant_dig;\n' | |
f9fabc1b | 348 | ' int narrow_mant_dig;\n' |
2dd0aec5 JM |
349 | ' };\n' |
350 | 'int num_pass, num_fail;\n' | |
351 | 'volatile int called_mant_dig;\n' | |
352 | 'const char *volatile called_func_name;\n' | |
2fee621d JM |
353 | 'enum e { E, F };\n' |
354 | 'struct s\n' | |
355 | ' {\n' | |
356 | ' int bf:2;\n' | |
357 | ' };\n'] | |
2dd0aec5 JM |
358 | float64_text = ('# if LDBL_MANT_DIG == DBL_MANT_DIG\n' |
359 | 'typedef _Float64 long_double_Float64;\n' | |
360 | 'typedef __CFLOAT64 complex_long_double_Float64;\n' | |
361 | '# else\n' | |
362 | 'typedef long double long_double_Float64;\n' | |
363 | 'typedef _Complex long double ' | |
364 | 'complex_long_double_Float64;\n' | |
365 | '# endif\n') | |
366 | float64_text = if_cond_text([Type.float64_type.condition], | |
367 | float64_text) | |
368 | float64x_text = ('# if LDBL_MANT_DIG == DBL_MANT_DIG\n' | |
369 | 'typedef _Float64x long_double_Float64x;\n' | |
370 | 'typedef __CFLOAT64X complex_long_double_Float64x;\n' | |
371 | '# else\n' | |
372 | 'typedef long double long_double_Float64x;\n' | |
373 | 'typedef _Complex long double ' | |
374 | 'complex_long_double_Float64x;\n' | |
375 | '# endif\n') | |
376 | float64x_text = if_cond_text([Type.float64x_type.condition], | |
377 | float64x_text) | |
f9fabc1b JM |
378 | float32x_ext_text = ('#ifdef HUGE_VAL_F64X\n' |
379 | 'typedef _Float64x Float32x_ext;\n' | |
380 | 'typedef __CFLOAT64X complex_Float32x_ext;\n' | |
381 | '# define FLT32X_EXT_MANT_DIG FLT64X_MANT_DIG\n' | |
382 | '#else\n' | |
383 | 'typedef _Float64 Float32x_ext;\n' | |
384 | 'typedef __CFLOAT64 complex_Float32x_ext;\n' | |
385 | '# define FLT32X_EXT_MANT_DIG FLT64_MANT_DIG\n' | |
386 | '#endif\n') | |
2dd0aec5 JM |
387 | self.header_list.append(float64_text) |
388 | self.header_list.append(float64x_text) | |
f9fabc1b | 389 | self.header_list.append(float32x_ext_text) |
2dd0aec5 JM |
390 | self.types_seen = set() |
391 | for t in Type.all_types_list: | |
392 | self.add_type_var(t.name, t.condition) | |
393 | self.test_text_list = [] | |
394 | self.test_array_list = [] | |
7c67e6e8 | 395 | self.macros_seen = set() |
2dd0aec5 JM |
396 | |
397 | def add_type_var(self, name, cond): | |
398 | """Add declarations of variables for a type.""" | |
399 | if name in self.types_seen: | |
400 | return | |
401 | t_vars = define_vars_for_type(name) | |
402 | self.header_list.append(if_cond_text([cond], t_vars)) | |
403 | self.types_seen.add(name) | |
404 | ||
405 | def add_tests(self, macro, ret, args, complex_func=None): | |
7c67e6e8 JM |
406 | """Add tests for a given tgmath.h macro, if that is the macro for |
407 | which tests are to be generated; otherwise just add it to the | |
408 | list of macros for which test generation is supported.""" | |
2dd0aec5 JM |
409 | # 'c' means the function argument or return type is |
410 | # type-generic and complex only (a complex function argument | |
411 | # may still have a real macro argument). 'g' means it is | |
412 | # type-generic and may be real or complex; 'r' means it is | |
413 | # type-generic and may only be real; 's' means the same as | |
414 | # 'r', but restricted to float, double and long double. | |
7c67e6e8 JM |
415 | self.macros_seen.add(macro) |
416 | if macro != self.macro: | |
417 | return | |
2dd0aec5 JM |
418 | have_complex = False |
419 | func = macro | |
f9fabc1b JM |
420 | narrowing = False |
421 | narrowing_std = False | |
2dd0aec5 JM |
422 | if ret == 'c' or 'c' in args: |
423 | # Complex-only. | |
424 | have_complex = True | |
425 | complex_func = func | |
426 | func = None | |
427 | elif ret == 'g' or 'g' in args: | |
428 | # Real and complex. | |
429 | have_complex = True | |
430 | if complex_func == None: | |
431 | complex_func = 'c%s' % func | |
f9fabc1b JM |
432 | # For narrowing macros, compute narrow_args, the list of |
433 | # argument types for which there is an actual corresponding | |
434 | # function. If none of those types exist, or the return type | |
435 | # does not exist, then the macro is not defined and no tests | |
436 | # of it can be run. | |
437 | if ret == 'float': | |
438 | narrowing = True | |
439 | narrowing_std = True | |
440 | narrow_cond = '1' | |
441 | narrow_args = [Type.double_type, Type.long_double_type] | |
442 | narrow_fallback = Type.double_type | |
443 | elif ret == 'double': | |
444 | narrowing = True | |
445 | narrowing_std = True | |
446 | narrow_cond = '1' | |
447 | narrow_args = [Type.long_double_type] | |
448 | narrow_fallback = Type.long_double_type | |
449 | elif ret.startswith('_Float'): | |
450 | narrowing = True | |
451 | narrow_args = [] | |
452 | nret_type = None | |
453 | narrow_fallback = None | |
454 | for order, real_type in sorted(Type.real_types_order.items()): | |
455 | if real_type.name == ret: | |
456 | nret_type = real_type | |
457 | elif nret_type and real_type.name.startswith('_Float'): | |
458 | narrow_args.append(real_type) | |
459 | if (narrow_fallback is None | |
460 | and ret.endswith('x') == real_type.name.endswith('x')): | |
461 | narrow_fallback = real_type | |
462 | if narrow_args: | |
463 | narrow_cond = ('(%s && (%s))' | |
464 | % (nret_type.condition, | |
465 | ' || '.join(t.condition | |
466 | for t in narrow_args))) | |
467 | if narrow_fallback is None: | |
468 | narrow_fallback = narrow_args[0] | |
469 | if ret == '_Float32x': | |
470 | narrow_fallback = Type.float32x_ext_type | |
471 | else: | |
472 | # No possible argument types, even conditionally. | |
473 | narrow_cond = '0' | |
474 | narrowing_nonstd = narrowing and not narrowing_std | |
2dd0aec5 JM |
475 | types = [ret] + args |
476 | for t in types: | |
477 | if t != 'c' and t != 'g' and t != 'r' and t != 's': | |
478 | self.add_type_var(t, '1') | |
479 | for t in Type.argument_types_list: | |
480 | if t.integer: | |
481 | continue | |
482 | if t.complex and not have_complex: | |
483 | continue | |
484 | if func == None and not t.complex: | |
485 | continue | |
486 | if ret == 's' and t.name.startswith('_Float'): | |
487 | continue | |
f9fabc1b JM |
488 | if narrowing and t not in narrow_args: |
489 | continue | |
2dd0aec5 JM |
490 | if ret == 'c': |
491 | ret_name = t.complex_type.name | |
492 | elif ret == 'g': | |
493 | ret_name = t.name | |
494 | elif ret == 'r' or ret == 's': | |
495 | ret_name = t.real_type.name | |
496 | else: | |
497 | ret_name = ret | |
498 | dummy_func_name = complex_func if t.complex else func | |
499 | arg_list = [] | |
500 | arg_num = 0 | |
501 | for a in args: | |
502 | if a == 'c': | |
503 | arg_name = t.complex_type.name | |
504 | elif a == 'g': | |
505 | arg_name = t.name | |
506 | elif a == 'r' or a == 's': | |
507 | arg_name = t.real_type.name | |
508 | else: | |
509 | arg_name = a | |
510 | arg_list.append('%s arg%d __attribute__ ((unused))' | |
511 | % (arg_name, arg_num)) | |
512 | arg_num += 1 | |
513 | dummy_func = ('%s\n' | |
514 | '(%s%s) (%s)\n' | |
515 | '{\n' | |
516 | ' called_mant_dig = %s;\n' | |
517 | ' called_func_name = "%s";\n' | |
518 | ' return 0;\n' | |
519 | '}\n' % (ret_name, dummy_func_name, | |
520 | t.real_type.suffix, ', '.join(arg_list), | |
521 | t.real_type.mant_dig, dummy_func_name)) | |
f9fabc1b JM |
522 | if narrowing: |
523 | dummy_cond = [narrow_cond, t.condition] | |
524 | else: | |
525 | dummy_cond = [t.condition] | |
526 | dummy_func = if_cond_text(dummy_cond, dummy_func) | |
2dd0aec5 JM |
527 | self.test_text_list.append(dummy_func) |
528 | arg_types = [] | |
529 | for t in args: | |
530 | if t == 'g' or t == 'c': | |
531 | arg_types.append(Type.argument_types_list) | |
532 | elif t == 'r': | |
f9fabc1b JM |
533 | if narrowing_std: |
534 | arg_types.append(Type.standard_real_argument_types_list) | |
535 | elif narrowing: | |
536 | arg_types.append( | |
537 | Type.non_standard_real_argument_types_list) | |
538 | else: | |
539 | arg_types.append(Type.real_argument_types_list) | |
2dd0aec5 JM |
540 | elif t == 's': |
541 | arg_types.append(Type.standard_real_argument_types_list) | |
542 | arg_types_product = list_product(arg_types) | |
543 | test_num = 0 | |
544 | for this_args in arg_types_product: | |
f9fabc1b JM |
545 | comb_type = Type.combine_types(this_args, narrowing_nonstd) |
546 | if narrowing: | |
547 | # As long as there are no integer arguments, and as | |
548 | # long as the chosen argument type is as wide as all | |
549 | # the floating-point arguments passed, the semantics | |
550 | # of the macro call do not depend on the exact | |
551 | # function chosen. In particular, for f32x functions | |
552 | # when _Float64x exists, the chosen type should differ | |
553 | # for _Float32x and _Float64 arguments, but it is not | |
554 | # always possible to distinguish those types before | |
555 | # GCC 7 and the implementation does not attempt to do | |
556 | # so before GCC 8. | |
557 | narrow_mant_dig = comb_type.real_type.mant_dig | |
558 | for arg_type in this_args: | |
559 | if arg_type.integer: | |
560 | narrow_mant_dig = 0 | |
561 | else: | |
562 | narrow_mant_dig = 0 | |
563 | if (narrowing | |
564 | and comb_type not in narrow_args | |
565 | and narrow_fallback is not None): | |
566 | comb_type = narrow_fallback | |
567 | can_comb = Type.can_combine_types(this_args, narrowing_nonstd) | |
2dd0aec5 JM |
568 | all_conds = [t.condition for t in this_args] |
569 | all_conds.append(can_comb) | |
f9fabc1b JM |
570 | if narrowing: |
571 | all_conds.append(narrow_cond) | |
2dd0aec5 JM |
572 | any_complex = func == None |
573 | for t in this_args: | |
574 | if t.complex: | |
575 | any_complex = True | |
576 | func_name = complex_func if any_complex else func | |
577 | test_name = '%s (%s)' % (macro, | |
578 | ', '.join([t.name for t in this_args])) | |
579 | test_func_name = 'test_%s_%d' % (macro, test_num) | |
580 | test_num += 1 | |
581 | mant_dig = comb_type.real_type.mant_dig | |
f9fabc1b JM |
582 | test_text = '%s, "%s", "%s", %s, %s' % (test_func_name, func_name, |
583 | test_name, mant_dig, | |
584 | narrow_mant_dig) | |
2dd0aec5 JM |
585 | test_text = ' { %s },\n' % test_text |
586 | test_text = if_cond_text(all_conds, test_text) | |
587 | self.test_array_list.append(test_text) | |
588 | call_args = [] | |
589 | call_arg_pos = 0 | |
590 | for t in args: | |
591 | if t == 'g' or t == 'c' or t == 'r' or t == 's': | |
592 | type = this_args[call_arg_pos].name | |
593 | call_arg_pos += 1 | |
594 | else: | |
595 | type = t | |
596 | call_args.append(vol_var_for_type(type)) | |
597 | call_args_text = ', '.join(call_args) | |
598 | if ret == 'g': | |
599 | ret_type = comb_type.name | |
600 | elif ret == 'r' or ret == 's': | |
601 | ret_type = comb_type.real_type.name | |
602 | elif ret == 'c': | |
603 | ret_type = comb_type.complex_type.name | |
604 | else: | |
605 | ret_type = ret | |
606 | call_text = '%s (%s)' % (macro, call_args_text) | |
607 | test_func_text = ('static void\n' | |
608 | '%s (void)\n' | |
609 | '{\n' | |
610 | ' extern typeof (%s) %s ' | |
611 | '__attribute__ ((unused));\n' | |
612 | ' %s = %s;\n' | |
613 | '}\n' % (test_func_name, call_text, | |
614 | var_for_type(ret_type), | |
615 | vol_var_for_type(ret_type), call_text)) | |
616 | test_func_text = if_cond_text(all_conds, test_func_text) | |
617 | self.test_text_list.append(test_func_text) | |
618 | ||
7c67e6e8 JM |
619 | def add_all_tests(self, macro): |
620 | """Add tests for the given tgmath.h macro, if any, and generate the | |
621 | list of all supported macros.""" | |
622 | self.macro = macro | |
2dd0aec5 JM |
623 | # C99/C11 real-only functions. |
624 | self.add_tests('atan2', 'r', ['r', 'r']) | |
625 | self.add_tests('cbrt', 'r', ['r']) | |
626 | self.add_tests('ceil', 'r', ['r']) | |
627 | self.add_tests('copysign', 'r', ['r', 'r']) | |
628 | self.add_tests('erf', 'r', ['r']) | |
629 | self.add_tests('erfc', 'r', ['r']) | |
630 | self.add_tests('exp2', 'r', ['r']) | |
631 | self.add_tests('expm1', 'r', ['r']) | |
632 | self.add_tests('fdim', 'r', ['r', 'r']) | |
633 | self.add_tests('floor', 'r', ['r']) | |
634 | self.add_tests('fma', 'r', ['r', 'r', 'r']) | |
635 | self.add_tests('fmax', 'r', ['r', 'r']) | |
636 | self.add_tests('fmin', 'r', ['r', 'r']) | |
637 | self.add_tests('fmod', 'r', ['r', 'r']) | |
638 | self.add_tests('frexp', 'r', ['r', 'int *']) | |
639 | self.add_tests('hypot', 'r', ['r', 'r']) | |
640 | self.add_tests('ilogb', 'int', ['r']) | |
641 | self.add_tests('ldexp', 'r', ['r', 'int']) | |
642 | self.add_tests('lgamma', 'r', ['r']) | |
643 | self.add_tests('llrint', 'long long int', ['r']) | |
644 | self.add_tests('llround', 'long long int', ['r']) | |
0908a38a JM |
645 | # log10 is real-only in ISO C, but supports complex arguments |
646 | # as a GNU extension. | |
647 | self.add_tests('log10', 'g', ['g']) | |
2dd0aec5 JM |
648 | self.add_tests('log1p', 'r', ['r']) |
649 | self.add_tests('log2', 'r', ['r']) | |
650 | self.add_tests('logb', 'r', ['r']) | |
651 | self.add_tests('lrint', 'long int', ['r']) | |
652 | self.add_tests('lround', 'long int', ['r']) | |
653 | self.add_tests('nearbyint', 'r', ['r']) | |
654 | self.add_tests('nextafter', 'r', ['r', 'r']) | |
655 | self.add_tests('nexttoward', 's', ['s', 'long double']) | |
656 | self.add_tests('remainder', 'r', ['r', 'r']) | |
657 | self.add_tests('remquo', 'r', ['r', 'r', 'int *']) | |
658 | self.add_tests('rint', 'r', ['r']) | |
659 | self.add_tests('round', 'r', ['r']) | |
660 | self.add_tests('scalbn', 'r', ['r', 'int']) | |
661 | self.add_tests('scalbln', 'r', ['r', 'long int']) | |
662 | self.add_tests('tgamma', 'r', ['r']) | |
663 | self.add_tests('trunc', 'r', ['r']) | |
664 | # C99/C11 real-and-complex functions. | |
665 | self.add_tests('acos', 'g', ['g']) | |
666 | self.add_tests('asin', 'g', ['g']) | |
667 | self.add_tests('atan', 'g', ['g']) | |
668 | self.add_tests('acosh', 'g', ['g']) | |
669 | self.add_tests('asinh', 'g', ['g']) | |
670 | self.add_tests('atanh', 'g', ['g']) | |
671 | self.add_tests('cos', 'g', ['g']) | |
672 | self.add_tests('sin', 'g', ['g']) | |
673 | self.add_tests('tan', 'g', ['g']) | |
674 | self.add_tests('cosh', 'g', ['g']) | |
675 | self.add_tests('sinh', 'g', ['g']) | |
676 | self.add_tests('tanh', 'g', ['g']) | |
677 | self.add_tests('exp', 'g', ['g']) | |
678 | self.add_tests('log', 'g', ['g']) | |
679 | self.add_tests('pow', 'g', ['g', 'g']) | |
680 | self.add_tests('sqrt', 'g', ['g']) | |
681 | self.add_tests('fabs', 'r', ['g'], 'cabs') | |
682 | # C99/C11 complex-only functions. | |
683 | self.add_tests('carg', 'r', ['c']) | |
684 | self.add_tests('cimag', 'r', ['c']) | |
685 | self.add_tests('conj', 'c', ['c']) | |
686 | self.add_tests('cproj', 'c', ['c']) | |
687 | self.add_tests('creal', 'r', ['c']) | |
688 | # TS 18661-1 functions. | |
689 | self.add_tests('roundeven', 'r', ['r']) | |
690 | self.add_tests('nextup', 'r', ['r']) | |
691 | self.add_tests('nextdown', 'r', ['r']) | |
692 | self.add_tests('fminmag', 'r', ['r', 'r']) | |
693 | self.add_tests('fmaxmag', 'r', ['r', 'r']) | |
694 | self.add_tests('llogb', 'long int', ['r']) | |
695 | self.add_tests('fromfp', 'intmax_t', ['r', 'int', 'unsigned int']) | |
696 | self.add_tests('fromfpx', 'intmax_t', ['r', 'int', 'unsigned int']) | |
697 | self.add_tests('ufromfp', 'uintmax_t', ['r', 'int', 'unsigned int']) | |
698 | self.add_tests('ufromfpx', 'uintmax_t', ['r', 'int', 'unsigned int']) | |
abd38358 JM |
699 | for fn, args in (('add', 2), ('div', 2), ('mul', 2), ('sqrt', 1), |
700 | ('sub', 2)): | |
f9fabc1b JM |
701 | for ret, prefix in (('float', 'f'), |
702 | ('double', 'd'), | |
703 | ('_Float16', 'f16'), | |
704 | ('_Float32', 'f32'), | |
705 | ('_Float64', 'f64'), | |
706 | ('_Float128', 'f128'), | |
707 | ('_Float32x', 'f32x'), | |
708 | ('_Float64x', 'f64x')): | |
abd38358 | 709 | self.add_tests(prefix + fn, ret, ['r'] * args) |
2dd0aec5 JM |
710 | # Miscellaneous functions. |
711 | self.add_tests('scalb', 's', ['s', 's']) | |
712 | ||
713 | def tests_text(self): | |
714 | """Return the text of the generated testcase.""" | |
715 | test_list = [''.join(self.test_text_list), | |
716 | 'static const struct test tests[] =\n' | |
717 | ' {\n', | |
718 | ''.join(self.test_array_list), | |
719 | ' };\n'] | |
720 | footer_list = ['static int\n' | |
721 | 'do_test (void)\n' | |
722 | '{\n' | |
723 | ' for (size_t i = 0;\n' | |
724 | ' i < sizeof (tests) / sizeof (tests[0]);\n' | |
725 | ' i++)\n' | |
726 | ' {\n' | |
727 | ' called_mant_dig = 0;\n' | |
728 | ' called_func_name = "";\n' | |
729 | ' tests[i].func ();\n' | |
730 | ' if (called_mant_dig == tests[i].mant_dig\n' | |
731 | ' && strcmp (called_func_name,\n' | |
732 | ' tests[i].func_name) == 0)\n' | |
733 | ' num_pass++;\n' | |
f9fabc1b JM |
734 | '#if !__GNUC_PREREQ (8, 0)\n' |
735 | ' else if (tests[i].narrow_mant_dig > 0\n' | |
736 | ' && (called_mant_dig\n' | |
737 | ' >= tests[i].narrow_mant_dig)\n' | |
738 | ' && strcmp (called_func_name,\n' | |
739 | ' tests[i].func_name) == 0)\n' | |
740 | ' {\n' | |
741 | ' num_pass++;\n' | |
742 | ' printf ("Test %zu (%s):\\n"\n' | |
743 | ' " Expected: %s precision %d\\n"\n' | |
744 | ' " Actual: %s precision %d\\n"\n' | |
745 | ' " (OK with old GCC)\\n\\n",\n' | |
746 | ' i, tests[i].test_name,\n' | |
747 | ' tests[i].func_name,\n' | |
748 | ' tests[i].mant_dig,\n' | |
749 | ' called_func_name, called_mant_dig);\n' | |
750 | ' }\n' | |
751 | '#endif\n' | |
2dd0aec5 JM |
752 | ' else\n' |
753 | ' {\n' | |
754 | ' num_fail++;\n' | |
755 | ' printf ("Test %zu (%s):\\n"\n' | |
756 | ' " Expected: %s precision %d\\n"\n' | |
757 | ' " Actual: %s precision %d\\n\\n",\n' | |
758 | ' i, tests[i].test_name,\n' | |
759 | ' tests[i].func_name,\n' | |
760 | ' tests[i].mant_dig,\n' | |
761 | ' called_func_name, called_mant_dig);\n' | |
762 | ' }\n' | |
763 | ' }\n' | |
764 | ' printf ("%d pass, %d fail\\n", num_pass, num_fail);\n' | |
765 | ' return num_fail != 0;\n' | |
766 | '}\n' | |
767 | '\n' | |
768 | '#include <support/test-driver.c>'] | |
769 | return ''.join(self.header_list + test_list + footer_list) | |
770 | ||
7c67e6e8 JM |
771 | def check_macro_list(self, macro_list): |
772 | """Check the list of macros that can be tested.""" | |
773 | if self.macros_seen != set(macro_list): | |
774 | print('error: macro list mismatch') | |
775 | sys.exit(1) | |
776 | ||
2dd0aec5 JM |
777 | def main(): |
778 | """The main entry point.""" | |
779 | Type.init_types() | |
780 | t = Tests() | |
7c67e6e8 JM |
781 | if sys.argv[1] == 'check-list': |
782 | macro = None | |
783 | macro_list = sys.argv[2:] | |
784 | else: | |
785 | macro = sys.argv[1] | |
786 | macro_list = [] | |
787 | t.add_all_tests(macro) | |
788 | if macro: | |
789 | print(t.tests_text()) | |
790 | else: | |
791 | t.check_macro_list(macro_list) | |
2dd0aec5 JM |
792 | |
793 | if __name__ == '__main__': | |
794 | main() |