From e006972e45723d8d10eee6b66bae5392e2b2e608 Mon Sep 17 00:00:00 2001 From: Dana Robinson <43805+derobins@users.noreply.github.com> Date: Mon, 15 Jul 2024 05:38:13 -0700 Subject: [PATCH] Clean up Fortran __float128 configure-time checks (#4649) * Always use DECIMAL_DIG instead of LDBL_DIG. This was controlled by an ifdef that is always true in C99 or greater It's confusing to use float.h C constants as variable names in configure.ac and the PAC_FC_LDBL_DIG macro. * Directly compare MY_FLT128_DIG and MY_LDBL_DIG * Make uniform across CMake and Autotools * Don't export quadmath.h variables to H5pubconf.h --- config/cmake/ConfigureChecks.cmake | 102 +++++++++++++++++------------ config/cmake/H5pubconf.h.in | 11 +--- config/cmake/HDF5UseFortran.cmake | 23 ++++--- config/cmake/HDFTests.c | 12 ++++ configure.ac | 86 ++++++++++++++++++------ fortran/test/tH5T_F03.F90 | 4 -- m4/aclocal_fc.m4 | 38 ++++++----- 7 files changed, 170 insertions(+), 106 deletions(-) diff --git a/config/cmake/ConfigureChecks.cmake b/config/cmake/ConfigureChecks.cmake index fe421e4e4c4..bac45179051 100644 --- a/config/cmake/ConfigureChecks.cmake +++ b/config/cmake/ConfigureChecks.cmake @@ -133,11 +133,15 @@ endif () ## Check for non-standard extension quadmath.h -CHECK_INCLUDE_FILES(quadmath.h C_HAVE_QUADMATH) -if (C_HAVE_QUADMATH) - set(${HDF_PREFIX}_HAVE_QUADMATH_H 1) +# gcc puts symbols like FLT128_DIG in quadmath.h instead of float.h, so +# check for that. This is only used by the build system and doesn't need +# to be exported to H5pubconf.h. +CHECK_INCLUDE_FILES("quadmath.h" INCLUDE_QUADMATH_H) +# Convert TRUE/FALSE to 0/1 for preprocessor values in test code, below +if (${INCLUDE_QUADMATH_H}) + set(C_INCLUDE_QUADMATH_H 1) else () - set(${HDF_PREFIX}_HAVE_QUADMATH_H 0) + set(C_INCLUDE_QUADMATH_H 0) endif () if (CYGWIN) @@ -641,22 +645,38 @@ endif() #----------------------------------------------------------------------------- if (HDF5_BUILD_FORTRAN) - HDF_CHECK_TYPE_SIZE(__float128 _SIZEOF___FLOAT128) - if (_SIZEOF___FLOAT128) - set (${HDF_PREFIX}_HAVE_FLOAT128 1) - set (${HDF_PREFIX}_SIZEOF___FLOAT128 ${_SIZEOF___FLOAT128}) - else () - set (${HDF_PREFIX}_HAVE_FLOAT128 0) - set (${HDF_PREFIX}_SIZEOF___FLOAT128 0) - endif () + # ---------------------------------------------------------------------- + # __float128 checks + # + # If __float128 exists and we can determine its precision, we will use + # it in the Fortran interface. The checks for this require that the + # precision be specified via a symbol named FLT128_DIG, which might be + # found in quadmath.h. + # + # The checks here are based on the GNU __float128 extension type from + # libquadmath, which is now part of gcc. Other compilers (clang, Intel) + # also expose __float128 and/or __float128 may be an alias for some + # other 128-bit floating point type. + # + # 128-bit floating-point math is usually handled in software and is thus + # orders of magnitude slower than hardware-supported floating-point math. + # - HDF_CHECK_TYPE_SIZE(_Quad _SIZEOF__QUAD) - if (NOT _SIZEOF__QUAD) - set (${HDF_PREFIX}_SIZEOF__QUAD 0) + #----------------------------------------------------------------------------- + # Is the __float128 type available? + #----------------------------------------------------------------------------- + HDF_FUNCTION_TEST (HAVE___FLOAT128) + # Convert TRUE/FALSE to 0/1 for preprocessor values in test code, below + if (${HAVE___FLOAT128}) + set(C_HAVE_FLOAT128 1) else () - set (${HDF_PREFIX}_SIZEOF__QUAD ${_SIZEOF__QUAD}) + set(C_HAVE_FLOAT128 0) endif () + #----------------------------------------------------------------------------- + # Get the max decimal precision in C, checking both long double and + # __float128 (if available) + #----------------------------------------------------------------------------- if (NOT CMAKE_CROSSCOMPILING) #----------------------------------------------------------------------------- # The provided CMake C macros don't provide a general compile/run function @@ -677,7 +697,6 @@ if (HDF5_BUILD_FORTRAN) TRY_RUN (RUN_RESULT_VAR COMPILE_RESULT_VAR ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/testCCompiler1.c - COMPILE_DEFINITIONS "-D_SIZEOF___FLOAT128=${H5_SIZEOF___FLOAT128};-D_HAVE_QUADMATH_H=${H5_HAVE_QUADMATH_H}" COMPILE_OUTPUT_VARIABLE COMPILEOUT ${_RUN_OUTPUT_VARIABLE} OUTPUT_VAR ) @@ -714,43 +733,42 @@ if (HDF5_BUILD_FORTRAN) " #include \n\ #include \n\ -#define CHECK_FLOAT128 _SIZEOF___FLOAT128\n\ -#if CHECK_FLOAT128!=0\n\ -#if _HAVE_QUADMATH_H!=0\n\ -#include \n\ -#endif\n\ -#ifdef FLT128_DIG\n\ -#define C_FLT128_DIG FLT128_DIG\n\ -#else\n\ -#define C_FLT128_DIG 0\n\ -#endif\n\ +#if ${C_HAVE_FLOAT128}\n\ +# if ${C_INCLUDE_QUADMATH_H}\n\ +# include \n\ +# endif\n\ +# ifdef FLT128_DIG\n\ +# define C_FLT128_DIG FLT128_DIG\n\ +# else\n\ +# define C_FLT128_DIG 0\n\ +# endif\n\ #else\n\ -#define C_FLT128_DIG 0\n\ +# define C_FLT128_DIG 0\n\ #endif\n\ -#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L\n\ #define C_LDBL_DIG DECIMAL_DIG\n\ -#else\n\ -#define C_LDBL_DIG LDBL_DIG\n\ -#endif\n\nint main(void) {\nprintf(\"\\%d\\\;\\%d\\\;\", C_LDBL_DIG, C_FLT128_DIG)\\\;\n\nreturn 0\\\;\n}\n - " +\n\ +int main(void) {\nprintf(\"\\%d\\\;\\%d\\\;\", C_LDBL_DIG, C_FLT128_DIG)\\\;\n\nreturn 0\\\;\n}\n + " ) C_RUN ("maximum decimal precision for C" ${PROG_SRC} PROG_RES PROG_OUTPUT4) message (STATUS "Testing maximum decimal precision for C - ${PROG_OUTPUT4}") - # dnl The output from the above program will be: - # dnl -- long double decimal precision -- __float128 decimal precision + # The output from the above program will be: + # -- long double decimal precision -- __float128 decimal precision - list (GET PROG_OUTPUT4 0 H5_LDBL_DIG) - list (GET PROG_OUTPUT4 1 H5_FLT128_DIG) + list (GET PROG_OUTPUT4 0 MY_LDBL_DIG) + list (GET PROG_OUTPUT4 1 MY_FLT128_DIG) - if (${HDF_PREFIX}_SIZEOF___FLOAT128 EQUAL "0" OR FLT128_DIG EQUAL "0") - set (${HDF_PREFIX}_HAVE_FLOAT128 0) - set (${HDF_PREFIX}_SIZEOF___FLOAT128 0) - set (_PAC_C_MAX_REAL_PRECISION ${H5_LDBL_DIG}) + # Set configure output and behavior + if (${HAVE___FLOAT128} AND (${MY_FLT128_DIG} GREATER ${MY_LDBL_DIG})) + set (${HDF_PREFIX}_HAVE_FLOAT128 1) + set (_PAC_C_MAX_REAL_PRECISION ${MY_FLT128_DIG}) else () - set (_PAC_C_MAX_REAL_PRECISION ${H5_FLT128_DIG}) + # No __float128 or the precision of __float128 <= that of long double + set (_PAC_C_MAX_REAL_PRECISION ${MY_LDBL_DIG}) endif () + if (NOT ${_PAC_C_MAX_REAL_PRECISION}) set (${HDF_PREFIX}_PAC_C_MAX_REAL_PRECISION 0) else () diff --git a/config/cmake/H5pubconf.h.in b/config/cmake/H5pubconf.h.in index e2265ad949f..22ea093cc7c 100644 --- a/config/cmake/H5pubconf.h.in +++ b/config/cmake/H5pubconf.h.in @@ -144,7 +144,7 @@ /* Determine if _Float16 is available */ #cmakedefine H5_HAVE__FLOAT16 @H5_HAVE__FLOAT16@ -/* Determine if __float128 is available */ +/* Determine if __float128 will be used in the Fortran wrappers */ #cmakedefine H5_HAVE_FLOAT128 @H5_HAVE_FLOAT128@ /* Define to 1 if you have the `flock' function. */ @@ -265,9 +265,6 @@ /* Define to 1 if you have the header file. */ #cmakedefine H5_HAVE_PWD_H @H5_HAVE_PWD_H@ -/* Define to 1 if you have the header file. */ -#cmakedefine H5_HAVE_QUADMATH_H @H5_HAVE_QUADMATH_H@ - /* Define whether the Read-Only S3 virtual file driver (VFD) should be compiled */ #cmakedefine H5_HAVE_ROS3_VFD @H5_HAVE_ROS3_VFD@ @@ -595,15 +592,9 @@ /* The size of `unsigned', as computed by sizeof. */ #cmakedefine H5_SIZEOF_UNSIGNED @H5_SIZEOF_UNSIGNED@ -/* The size of `_Quad', as computed by sizeof. */ -#define H5_SIZEOF__QUAD @H5_SIZEOF__QUAD@ - /* The size of `_Float16', as computed by sizeof. */ #define H5_SIZEOF__FLOAT16 @H5_SIZEOF__FLOAT16@ -/* The size of `__float128', as computed by sizeof. */ -#define H5_SIZEOF___FLOAT128 @H5_SIZEOF___FLOAT128@ - /* Define if strict file format checks are enabled */ #cmakedefine H5_STRICT_FORMAT_CHECKS @H5_STRICT_FORMAT_CHECKS@ diff --git a/config/cmake/HDF5UseFortran.cmake b/config/cmake/HDF5UseFortran.cmake index 98fa75ecfc9..ce33c7036e3 100644 --- a/config/cmake/HDF5UseFortran.cmake +++ b/config/cmake/HDF5UseFortran.cmake @@ -363,13 +363,13 @@ set (PROG_SRC3 " ) FORTRAN_RUN ("SIZEOF NATIVE KINDs" ${PROG_SRC3} XX YY PAC_SIZEOF_NATIVE_KINDS_RESULT PROG_OUTPUT3) -# dnl The output from the above program will be: -# dnl -- LINE 1 -- sizeof INTEGER -# dnl -- LINE 2 -- kind of INTEGER -# dnl -- LINE 3 -- sizeof REAL -# dnl -- LINE 4 -- kind of REAL -# dnl -- LINE 5 -- sizeof DOUBLE PRECISION -# dnl -- LINE 6 -- kind of DOUBLE PRECISION +# The output from the above program will be: +# -- LINE 1 -- sizeof INTEGER +# -- LINE 2 -- kind of INTEGER +# -- LINE 3 -- sizeof REAL +# -- LINE 4 -- kind of REAL +# -- LINE 5 -- sizeof DOUBLE PRECISION +# -- LINE 6 -- kind of DOUBLE PRECISION # # Convert the string to a list of strings by replacing the carriage return with a semicolon string (REGEX REPLACE "[\r\n]+" ";" PROG_OUTPUT3 "${PROG_OUTPUT3}") @@ -402,11 +402,10 @@ endif () set (${HDF_PREFIX}_FORTRAN_SIZEOF_LONG_DOUBLE ${${HDF_PREFIX}_SIZEOF_LONG_DOUBLE}) -# remove the invalid kind from the list -if (NOT(${${HDF_PREFIX}_SIZEOF___FLOAT128} EQUAL 0)) - if (NOT(${${HDF_PREFIX}_SIZEOF___FLOAT128} EQUAL ${max_real_fortran_sizeof}) - AND NOT(${${HDF_PREFIX}_FORTRAN_SIZEOF_LONG_DOUBLE} EQUAL ${max_real_fortran_sizeof}) - # account for the fact that the C compiler can have 16-byte __float128 and the fortran compiler only has 8-byte doubles, +# Remove the invalid kind from the list +if (${${HDF_PREFIX}_HAVE_FLOAT128}) + if (NOT(16 EQUAL ${max_real_fortran_sizeof}) AND NOT(${${HDF_PREFIX}_FORTRAN_SIZEOF_LONG_DOUBLE} EQUAL ${max_real_fortran_sizeof}) + # Account for the fact that the C compiler can have 16-byte __float128 and the fortran compiler only has 8-byte doubles, # so we don't want to remove the 8-byte fortran doubles. AND NOT(${PAC_FORTRAN_NATIVE_DOUBLE_SIZEOF} EQUAL ${max_real_fortran_sizeof})) message (WARNING " diff --git a/config/cmake/HDFTests.c b/config/cmake/HDFTests.c index 72f69828315..766d6211b3c 100644 --- a/config/cmake/HDFTests.c +++ b/config/cmake/HDFTests.c @@ -15,6 +15,18 @@ /* A simple test program to see if a function "works" */ #define SIMPLE_TEST(x) int main(void){ x; return 0; } +#ifdef HAVE___FLOAT128 + +/* Check if __float128 works (only used in the Fortran interface) */ +int +main () +{ + __float128 x; + + return 0; +} + +#endif /* HAVE___FLOAT128 */ #ifdef HAVE_BUILTIN_EXPECT diff --git a/configure.ac b/configure.ac index 1324b7b7703..b23c3c6c762 100644 --- a/configure.ac +++ b/configure.ac @@ -794,30 +794,76 @@ AC_MSG_RESULT([$HDF_FORTRAN]) if test "X$HDF_FORTRAN" = "Xyes"; then -## ---------------------------------------------------------------------- -## Check for non-standard extension __FLOAT128 -## - HAVE_FLOAT128=0 - HAVE_QUADMATH=0 - FLT128_DIG=0 - LDBL_DIG=0 + ## ---------------------------------------------------------------------- + ## __float128 checks + ## + ## If __float128 exists and we can determine its precision, we will use + ## it in the Fortran interface. The checks for this require that the + ## precision be specified via a symbol named FLT128_DIG, which might be + ## found in quadmath.h. + ## + ## The checks here are based on the GNU __float128 extension type from + ## libquadmath, which is now part of gcc. Other compilers (clang, Intel) + ## also expose __float128 and/or __float128 may be an alias for some + ## other 128-bit floating point type. + ## + ## 128-bit floating-point math is usually handled in software and is thus + ## orders of magnitude slower than hardware-supported floating-point math. + ## + AC_MSG_CHECKING([if __float128 exists]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],[[__float128 x; return 0;]])], + [HAVE___FLOAT128=1; AC_MSG_RESULT([yes])], + [HAVE___FLOAT128=0; AC_MSG_RESULT([no])] + ) + + ## gcc puts symbols like FLT128_DIG in quadmath.h instead of float.h, + ## so check for that before running the precision macro. Note that + ## using AC_CHECK_HEADER instead of AC_CHECK_HEADERS keeps the + ## H5_HAVE_QUADMATH_H symbol out of the public config header. + ## + AC_CHECK_HEADER([quadmath.h], [INCLUDE_QUADMATH_H=1], [INCLUDE_QUADMATH_H=0]) + + ## ---------------------------------------------------------------------- + ## Get the max decimal precision in C, checking both long double and + ## __float128 (if available) + ## + AC_MSG_CHECKING([maximum decimal precision for C]) - AC_CHECK_SIZEOF([__float128]) - AC_CHECK_SIZEOF([_Quad]) - AC_CHECK_HEADERS([quadmath.h], [HAVE_QUADMATH=1], []) + MY_FLT128_DIG=0 + MY_LDBL_DIG=0 + + ## Macro to compare long double and __float128 to see which has higher precision PAC_FC_LDBL_DIG + ## Set results + if test "$MY_FLT128_DIG" -gt "$MY_LDBL_DIG" ; then + PAC_C_MAX_REAL_PRECISION=$MY_FLT128_DIG + PRECISION_TYPE="(__float128)" + else + PAC_C_MAX_REAL_PRECISION=$MY_LDBL_DIG + PRECISION_TYPE="(long double)" + fi + AC_MSG_RESULT([$PAC_C_MAX_REAL_PRECISION $PRECISION_TYPE]) + + ## Store results in config file AC_SUBST([PAC_C_MAX_REAL_PRECISION]) + AC_DEFINE_UNQUOTED([PAC_C_MAX_REAL_PRECISION], $PAC_C_MAX_REAL_PRECISION, [Determine the maximum decimal precision in C]) + + ## Are we going to use __float128? + AC_MSG_CHECKING([if __float128 will be used in the Fortran wrappers]) - if test "$ac_cv_sizeof___float128" != 0 && test "$FLT128_DIG" != 0 ; then - AC_DEFINE([HAVE_FLOAT128], [1], [Determine if __float128 is available]) - PAC_C_MAX_REAL_PRECISION=$FLT128_DIG + if test "$MY_FLT128_DIG" -gt "$MY_LDBL_DIG" ; then + AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_FLOAT128], [1], [Determine if __float128 will be used in the Fortran wrappers]) else - PAC_C_MAX_REAL_PRECISION=$LDBL_DIG + ## Can't use __float128, but write an undef line anyway + AC_MSG_RESULT([no]) + AH_TEMPLATE([HAVE_FLOAT128], [Determine if __float128 will be used in the Fortran wrappers]) fi - AC_DEFINE_UNQUOTED([PAC_C_MAX_REAL_PRECISION], $PAC_C_MAX_REAL_PRECISION, [Determine the maximum decimal precision in C]) - AC_MSG_RESULT([$PAC_C_MAX_REAL_PRECISION]) + ## ---------------------------------------------------------------------- + ## Define interface version + ## VERS_MAJOR=`cat $srcdir/src/H5public.h | sed -n 's/^#define H5_VERS_MAJOR //p'` VERS_MINOR=`cat $srcdir/src/H5public.h | sed -n 's/^#define H5_VERS_MINOR //p'` VERS_RELEASE=`cat $srcdir/src/H5public.h | sed -n 's/^#define H5_VERS_RELEASE //p'` @@ -825,10 +871,10 @@ if test "X$HDF_FORTRAN" = "Xyes"; then AC_DEFINE_UNQUOTED([VERS_MINOR_TMP], $VERS_MINOR, [Define minor library version]) AC_DEFINE_UNQUOTED([VERS_RELEASE_TMP], $VERS_RELEASE, [Define release library version]) -## We will output an include file for Fortran, H5config_f.inc which -## contains various configure definitions used by the Fortran Library. -## Prepend H5_ to all macro names. This avoids name conflict between HDF5 macro -## names and those generated by another software package that uses the HDF5 library. + ## We will output an include file for Fortran, H5config_f.inc which + ## contains various configure definitions used by the Fortran Library. + ## Prepend H5_ to all macro names. This avoids name conflict between HDF5 macro + ## names and those generated by another software package that uses the HDF5 library. AC_CONFIG_HEADERS([fortran/src/H5config_f.inc], [cat fortran/src/H5config_f.inc | sed '1d;s%^/\* \(.*\) \*/%\1%;s/#define /#define H5_/;s/#undef /#undef H5_/' >fortran/src/H5config_f.inc.tmp; sed -i 's\_TMP\\g' fortran/src/H5config_f.inc.tmp; mv -f fortran/src/H5config_f.inc.tmp fortran/src/H5config_f.inc]) diff --git a/fortran/test/tH5T_F03.F90 b/fortran/test/tH5T_F03.F90 index a59cd73f751..e7a3797e37e 100644 --- a/fortran/test/tH5T_F03.F90 +++ b/fortran/test/tH5T_F03.F90 @@ -1090,10 +1090,8 @@ SUBROUTINE test_h5kind_to_type(total_error) CALL check("H5Dcreate_f",error, total_error) CALL H5Dcreate_f(file_id, dsetnamer8, h5kind_to_type(real_kind_15,H5_REAL_KIND), dspace_id, dset_idr8, error) CALL check("H5Dcreate_f",error, total_error) -!#ifdef H5_HAVE_FLOAT128 CALL H5Dcreate_f(file_id, dsetnamer16, h5kind_to_type(real_kind_31,H5_REAL_KIND), dspace_id, dset_idr16, error) CALL check("H5Dcreate_f",error, total_error) -!#endif ! ! Write the dataset. ! @@ -1123,11 +1121,9 @@ SUBROUTINE test_h5kind_to_type(total_error) f_ptr = C_LOC(dset_data_r15(1)) CALL h5dwrite_f(dset_idr8, h5kind_to_type(real_kind_15,H5_REAL_KIND), f_ptr, error) CALL check("H5Dwrite_f",error, total_error) -!#ifdef H5_HAVE_FLOAT128 f_ptr = C_LOC(dset_data_r31(1)) CALL h5dwrite_f(dset_idr16, h5kind_to_type(real_kind_31,H5_REAL_KIND), f_ptr, error) CALL check("H5Dwrite_f",error, total_error) -!#endif ! ! Close the file ! diff --git a/m4/aclocal_fc.m4 b/m4/aclocal_fc.m4 index cfcfbcf7ca2..610ee300611 100644 --- a/m4/aclocal_fc.m4 +++ b/m4/aclocal_fc.m4 @@ -528,37 +528,39 @@ FCFLAGS=$saved_FCFLAGS AC_LANG_POP([Fortran]) ]) +dnl Check for the maximum decimal precision for C +dnl +dnl Depends on if __float128 and/or quadmath.h exist. We only support 128-bit +dnl floats that work like GNU's quadmath.h __float128 type, which have the +dnl precision stored in a symbol named FLT128_DIG. +dnl +dnl The MY_(LDBL|FLT128)_DIG variables are from configure.ac +dnl AC_DEFUN([PAC_FC_LDBL_DIG],[ -AC_MSG_CHECKING([maximum decimal precision for C]) AC_LANG_CONFTEST([ AC_LANG_PROGRAM([ #include #include - #define CHECK_FLOAT128 $ac_cv_sizeof___float128 - #if CHECK_FLOAT128!=0 - # if $HAVE_QUADMATH!=0 - #include - # endif - # ifdef FLT128_DIG - #define C_FLT128_DIG FLT128_DIG - # else - #define C_FLT128_DIG 0 - # endif + #if $HAVE___FLOAT128 != 0 + # if $INCLUDE_QUADMATH_H != 0 + # include + # endif + # ifdef FLT128_DIG + # define C_FLT128_DIG FLT128_DIG + # else + # define C_FLT128_DIG 0 + # endif #else - #define C_FLT128_DIG 0 + # define C_FLT128_DIG 0 #endif - #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #define C_LDBL_DIG DECIMAL_DIG - #else - #define C_LDBL_DIG LDBL_DIG - #endif ],[[ fprintf(stderr, "%d\n%d\n", C_LDBL_DIG, C_FLT128_DIG); ]]) ]) AC_RUN_IFELSE([],[ - LDBL_DIG=$(./conftest$EXEEXT 2>&1 | sed -n '1p') - FLT128_DIG=$(./conftest$EXEEXT 2>&1 | sed -n '2p') + MY_LDBL_DIG=$(./conftest$EXEEXT 2>&1 | sed -n '1p') + MY_FLT128_DIG=$(./conftest$EXEEXT 2>&1 | sed -n '2p') ],[ AC_MSG_ERROR([C program fails to build or run!]) ],[])