From: Bruno Haible Date: Tue, 31 Oct 2023 20:54:49 +0000 (+0100) Subject: fenv-exceptions-trapping: Add tests. X-Git-Tag: v1.0~661 X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=e60ee371bb935823d18c98ad9cefe440eb4a43c7;p=gnulib.git fenv-exceptions-trapping: Add tests. * tests/test-fenv-except-trapping-1.c: New file. * tests/test-fenv-except-trapping-2.sh: New file. * tests/test-fenv-except-trapping-2.c: New file. * modules/fenv-exceptions-trapping-tests: New file. --- diff --git a/ChangeLog b/ChangeLog index f7d1a2237e..3b327c4c3d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2023-10-31 Bruno Haible + fenv-exceptions-trapping: Add tests. + * tests/test-fenv-except-trapping-1.c: New file. + * tests/test-fenv-except-trapping-2.sh: New file. + * tests/test-fenv-except-trapping-2.c: New file. + * modules/fenv-exceptions-trapping-tests: New file. + fenv-exceptions-trapping: New module. * lib/fenv.in.h (feenableexcept, fedisableexcept, fegetexcept): New declarations. diff --git a/modules/fenv-exceptions-trapping-tests b/modules/fenv-exceptions-trapping-tests new file mode 100644 index 0000000000..21f178461f --- /dev/null +++ b/modules/fenv-exceptions-trapping-tests @@ -0,0 +1,25 @@ +Files: +tests/test-fenv-except-trapping-1.c +tests/test-fenv-except-trapping-2.sh +tests/test-fenv-except-trapping-2.c +tests/infinity.h +tests/macros.h +m4/musl.m4 + +Depends-on: +fenv-exceptions-tracking-c99 +nan +snan + +configure.ac: +gl_MUSL_LIBC + +Makefile.am: +TESTS += \ + test-fenv-except-trapping-1 \ + test-fenv-except-trapping-2.sh +check_PROGRAMS += \ + test-fenv-except-trapping-1 \ + test-fenv-except-trapping-2 +test_fenv_except_trapping_1_LDADD = $(LDADD) @FENV_EXCEPTIONS_TRAPPING_LIBM@ +test_fenv_except_trapping_2_LDADD = $(LDADD) @FENV_EXCEPTIONS_TRAPPING_LIBM@ @FENV_EXCEPTIONS_TRACKING_LIBM@ diff --git a/tests/test-fenv-except-trapping-1.c b/tests/test-fenv-except-trapping-1.c new file mode 100644 index 0000000000..7c67a06ec9 --- /dev/null +++ b/tests/test-fenv-except-trapping-1.c @@ -0,0 +1,69 @@ +/* Test of turning floating-point exceptions into traps (signals). + Copyright (C) 2023 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible , 2023. */ + +#include + +/* Specification. */ +#include + +#include + +#include "macros.h" + +/* Check of the Boole-an algebra. */ + +/* Converts 5 bits to an exceptions bit mask. */ +static int +uint_to_exceptions (unsigned int a) +{ + return (a & 0x01 ? FE_INVALID : 0) + | (a & 0x02 ? FE_DIVBYZERO : 0) + | (a & 0x04 ? FE_OVERFLOW : 0) + | (a & 0x08 ? FE_UNDERFLOW : 0) + | (a & 0x10 ? FE_INEXACT : 0); +} + +int +main () +{ + unsigned int a, b; + + /* Run through all possible valid arguments to feenableexcept and + fedisableexcept. */ + for (a = 0; a < 0x20; a++) + for (b = 0; b < 0x20; b++) + { + unsigned int c = a & ~b; + + if (fedisableexcept (FE_ALL_EXCEPT) == -1 + || feenableexcept (uint_to_exceptions (a)) == -1) + { + fputs ("Skipping test: trapping floating-point exceptions are not supported on this machine.\n", stderr); + return 77; + } + ASSERT (fedisableexcept (uint_to_exceptions (b)) + == uint_to_exceptions (a)); + /* Check fegetexcept. */ + ASSERT (fegetexcept () == uint_to_exceptions (c)); + /* Check the return value of feenableexcept. It should be consistent + with fegetexcept. */ + ASSERT (feenableexcept (0) == uint_to_exceptions (c)); + } + + return 0; +} diff --git a/tests/test-fenv-except-trapping-2.c b/tests/test-fenv-except-trapping-2.c new file mode 100644 index 0000000000..18f42b89c2 --- /dev/null +++ b/tests/test-fenv-except-trapping-2.c @@ -0,0 +1,488 @@ +/* Test of turning floating-point exceptions into traps (signals). + Copyright (C) 2023 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible , 2023. */ + +#include + +/* Specification. */ +#include + +#include + +#include "infinity.h" +#include "snan.h" +#include "macros.h" + +/* Operations that should raise various floating-point exceptions. */ + +/* For a list, see the glibc documentation + */ + +#define infinityf my_infinityf /* Avoid collision with Cygwin's */ +static float volatile infinityf, qnanf; +static double volatile infinityd, qnand; +static long double volatile infinityl, qnanl; + +static void +raise_invalid_addition (char type) +{ + switch (type) + { + case 'f': + { + float volatile a, b; + _GL_UNUSED float volatile c; + a = infinityf; b = - infinityf; + c = a + b; + } + break; + case 'd': + { + double volatile a, b; + _GL_UNUSED double volatile c; + a = infinityd; b = - infinityd; + c = a + b; + } + break; + case 'l': + { + long double volatile a, b; + _GL_UNUSED long double volatile c; + a = infinityl; b = - infinityl; + c = a + b; + } + break; + } +} + +static void +raise_invalid_multiplication (char type) +{ + switch (type) + { + case 'f': + { + float volatile a, b; + _GL_UNUSED float volatile c; + a = infinityf; b = 0.0f; + c = a * b; + } + break; + case 'd': + { + double volatile a, b; + _GL_UNUSED double volatile c; + a = infinityd; b = 0.0; + c = a * b; + } + break; + case 'l': + { + long double volatile a, b; + _GL_UNUSED long double volatile c; + a = infinityl; b = 0.0L; + c = a * b; + } + break; + } +} + +static void +raise_invalid_division (char type) +{ + switch (type) + { + case 'f': + { + float volatile a, b; + _GL_UNUSED float volatile c; + a = infinityf; b = - infinityf; + c = a / b; + } + break; + case 'd': + { + double volatile a, b; + _GL_UNUSED double volatile c; + a = infinityd; b = - infinityd; + c = a / b; + } + break; + case 'l': + { + long double volatile a, b; + _GL_UNUSED long double volatile c; + a = infinityl; b = - infinityl; + c = a / b; + } + break; + } +} + +static void +raise_invalid_comparison (char type) +{ + switch (type) + { + case 'f': + { + float volatile a, b; + _GL_UNUSED int volatile c; + a = qnanf; b = qnanf; + c = a > b; + } + break; + case 'd': + { + double volatile a, b; + _GL_UNUSED int volatile c; + a = qnand; b = qnand; + c = a > b; + } + break; + case 'l': + { + long double volatile a, b; + _GL_UNUSED int volatile c; + a = qnanl; b = qnanl; + c = a > b; + } + break; + } +} + +static void +raise_invalid_snan (char type) +{ + switch (type) + { + case 'f': + { + float volatile a, b; + _GL_UNUSED float volatile c; + a = SNaNf (); b = 1.0f; + c = a + b; + } + break; + case 'd': + { + double volatile a, b; + _GL_UNUSED double volatile c; + a = SNaNd (); b = 1.0; + c = a + b; + } + break; + case 'l': + { + long double volatile a, b; + _GL_UNUSED long double volatile c; + a = SNaNl (); b = 1.0L; + c = a + b; + } + break; + } +} + +static void +raise_divbyzero (char type) +{ + switch (type) + { + case 'f': + { + float volatile a, b; + _GL_UNUSED float volatile c; + a = 2.5f; b = - 0.0f; + c = a / b; + } + break; + case 'd': + { + double volatile a, b; + _GL_UNUSED double volatile c; + a = 2.5; b = - 0.0; + c = a / b; + } + break; + case 'l': + { + long double volatile a, b; + _GL_UNUSED long double volatile c; + a = 2.5L; b = - 0.0L; + c = a / b; + } + break; + } +} + +static void +raise_overflow (char type) +{ + switch (type) + { + case 'f': + { + float volatile a, b; + _GL_UNUSED float volatile c; + a = 1e20f; b = 1e30f; + c = a * b; + } + break; + case 'd': + { + double volatile a, b; + _GL_UNUSED double volatile c; + a = 1e160; b = 1e260; + c = a * b; + } + break; + case 'l': + { + long double volatile a, b; + _GL_UNUSED long double volatile c; + a = 1e200L; b = 1e300L; + c = a * b; + c = c * c; + c = c * c; + c = c * c; + c = c * c; + } + break; + } +} + +static void +raise_underflow (char type) +{ + switch (type) + { + case 'f': + { + float volatile a, b; + _GL_UNUSED float volatile c; + a = 1e-20f; b = 1e-30f; + c = a * b; + } + break; + case 'd': + { + double volatile a, b; + _GL_UNUSED double volatile c; + a = 1e-160; b = 1e-260; + c = a * b; + } + break; + case 'l': + { + long double volatile a, b; + _GL_UNUSED long double volatile c; + a = 1e-200L; b = 1e-300L; + c = a * b; + c = c * c; + c = c * c; + c = c * c; + c = c * c; + } + break; + } +} + +static void +raise_inexact (char type) +{ + switch (type) + { + case 'f': + { + float volatile a, b; + _GL_UNUSED float volatile c; + a = 2.5f; b = 3.0f; + c = a / b; + } + break; + case 'd': + { + double volatile a, b; + _GL_UNUSED double volatile c; + a = 2.5; b = 3.0; + c = a / b; + } + break; + case 'l': + { + long double volatile a, b; + _GL_UNUSED long double volatile c; + a = 2.5L; b = 3.0L; + c = a / b; + } + break; + } +} + +int +main (int argc, char *argv[]) +{ + if (argc > 3) + { + const char *operation_arg = argv[1]; + const char *procedure_arg = argv[2]; + const char *type_arg = argv[3]; + + void (*operation) (char) = NULL; + int expected_exceptions = 0; + int possible_exceptions = 0; + + /* Preparations (to be executed before we call feenableexcept). */ + infinityf = Infinityf (); + qnanf = NaNf (); + infinityd = Infinityd (); + qnand = NaNd (); + infinityl = Infinityl (); + qnanl = NaNl (); + feclearexcept (FE_ALL_EXCEPT); + + /* Analyze the operation argument. */ + switch (operation_arg[0]) + { + case '1': + operation = raise_invalid_addition; + expected_exceptions = FE_INVALID; + break; + case '2': + operation = raise_invalid_multiplication; + expected_exceptions = FE_INVALID; + break; + case '3': + operation = raise_invalid_division; + expected_exceptions = FE_INVALID; + break; + case '4': + operation = raise_invalid_comparison; + expected_exceptions = FE_INVALID; + break; + case '5': + operation = raise_invalid_snan; + expected_exceptions = FE_INVALID; + break; + case '6': + operation = raise_divbyzero; + expected_exceptions = FE_DIVBYZERO; + break; + case '7': + operation = raise_overflow; + expected_exceptions = FE_OVERFLOW; + possible_exceptions = FE_INEXACT; + break; + case '8': + operation = raise_underflow; + expected_exceptions = FE_UNDERFLOW; + possible_exceptions = FE_INEXACT; + break; + case '9': + operation = raise_inexact; + expected_exceptions = FE_INEXACT; + break; + } + + /* Analyze the procedure argument. */ + switch (procedure_arg[0]) + { + /* These three procedures should lead to a trap. */ + case 'p': + if (feenableexcept (expected_exceptions) == -1) + goto skip; + break; + case 'q': + if (feenableexcept (FE_ALL_EXCEPT) == -1) + goto skip; + break; + case 'r': + if (feenableexcept (FE_ALL_EXCEPT) == -1) + goto skip; + ASSERT (fedisableexcept (FE_ALL_EXCEPT & ~expected_exceptions) + == FE_ALL_EXCEPT); + break; + /* This procedure should *not* lead to a trap. */ + case 's': + if (feenableexcept (FE_ALL_EXCEPT & ~(expected_exceptions | possible_exceptions)) == -1) + goto skip; + break; + } + + /* Avoid known test failures. */ + int known_failure = 0; + /* The '4' tests do not work on Linux/powerpc* and Linux/s390*, as well as + on GNU/kFreeBSD/i386, GNU/kFreeBSD/x86_64, musl libc/i386, + musl libc/powerpc64le, macOS/i386, macOS/x86_64, macOS/arm64, + FreeBSD/i386, FreeBSD/x86_64, NetBSD/i386, NetBSD/x86_64, OpenBSD/i386, + OpenBSD/x86_64, AIX/powerpc, Solaris/i386, Solaris/x86_64, + Cygwin/x86_64, native Windows/i386, + native Windows/x86_64. */ + #if (__GLIBC__ >= 2 && (defined __powerpc__ || (defined __s390__ || defined __s390x__))) \ + || (__GLIBC__ >= 2 && __FreeBSD_kernel__ && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86))) \ + || (defined MUSL_LIBC && ((defined __i386 || defined _M_IX86) || defined __powerpc__)) \ + || ((defined __APPLE__ && defined __MACH__) && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86) || defined __aarch64__)) \ + || ((defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__) && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86))) \ + || (defined _AIX && defined __powerpc__) \ + || (defined __sun && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86))) \ + || (defined __CYGWIN__ && (defined __x86_64__ || defined _M_X64)) \ + || (defined _WIN32 && ((defined __x86_64__ || defined _M_X64) || (defined __i386 || defined _M_IX86))) + known_failure |= (operation_arg[0] == '4'); + #endif + /* The '7' and '8' tests, with types 'f' and 'd', do not work reliably + on Linux/i386. */ + #if defined __i386 || defined _M_IX86 + known_failure |= (operation_arg[0] == '7' || operation_arg[0] == '8'); + #endif + /* The '9' tests do not work on Linux/alpha, musl libc/powerpc64le, + AIX/powerpc. */ + #if (__GLIBC__ >= 2 && defined __alpha) \ + || ((defined MUSL_LIBC || defined _AIX) && defined __powerpc__) + known_failure |= (operation_arg[0] == '9'); + #endif + /* The 'l' tests do not work on Linux/loongarch64 with glibc 2.37. + Likewise on Linux/alpha with glibc 2.7 on Linux 2.6.26. + Likewise on FreeBSD 12.2/sparc. + Cause unknown. */ + #if (__GLIBC__ >= 2 && defined __loongarch__) \ + || ((__GLIBC__ == 2 && __GLIBC_MINOR__ < 36) && defined __alpha) \ + || (defined __FreeBSD__ && defined __sparc) + known_failure |= (type_arg[0] == 'l'); + #endif + if (known_failure) + { + fputs ("Skipping test: known failure on this platform\n", stderr); + return 77; + } + + /* Analyze the type argument. */ + switch (type_arg[0]) + { + case 'f': + case 'd': + case 'l': + operation (type_arg[0]); + break; + } + } + + return 0; + + skip: + fputs ("Skipping test: trapping floating-point exceptions are not supported on this machine.\n", stderr); + return 77; +} diff --git a/tests/test-fenv-except-trapping-2.sh b/tests/test-fenv-except-trapping-2.sh new file mode 100755 index 0000000000..d51197feb9 --- /dev/null +++ b/tests/test-fenv-except-trapping-2.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +# Test of turning floating-point exceptions into traps (signals). + +final_rc=0 + +for operation in 1 2 3 4 5 6 7 8 9; do + for procedure in p q r s; do + for type in f d l; do + ${CHECKER} ./test-fenv-except-trapping-2${EXEEXT} $operation $procedure $type + rc=$? + if test $rc = 77; then + if test $final_rc = 0; then + final_rc=77 + fi + else + if test $procedure = s; then + if test $rc != 0; then + echo "Failed (got a trap): ./test-fenv-except-trapping-2 $operation $procedure $type" 1>&2 + final_rc=1 + fi + else + if test $rc = 0; then + echo "Failed (got no trap): ./test-fenv-except-trapping-2 $operation $procedure $type" 1>&2 + final_rc=1 + fi + fi + fi + done + done +done + +exit $final_rc