Skip to content

Commit

Permalink
Add support for _Float16 16-bit floating point type (HDFGroup#4065)
Browse files Browse the repository at this point in the history
Fixed some conversion issues with Clang due to problematic undefined
behavior when casting a negative floating-point value to an integer

Fixed a bug in the library's software integer to floating-point
conversion function where a user's conversion exception function
returning H5T_CONV_UNHANDLED in the case of overflows would result in
incorrect data after conversion

Added configure checks for functions and macros related to _Float16
usage since some compilers expose the datatype but not the functions or
macros

Fixed a dt_arith test failure when H5_WANT_DCONV_EXCEPTION isn't defined

Fixed a few warnings from not explicitly casting some _Float16 variables
upwards
  • Loading branch information
jhendersonHDF authored Mar 19, 2024
1 parent 4e222bf commit 330b80a
Show file tree
Hide file tree
Showing 61 changed files with 5,765 additions and 1,022 deletions.
83 changes: 80 additions & 3 deletions config/cmake/ConfigureChecks.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,8 @@ macro (H5ConversionTests TEST def msg)
${CMAKE_BINARY_DIR}
${HDF_RESOURCES_DIR}/ConversionTests.c
CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=-D${TEST}_TEST
OUTPUT_VARIABLE OUTPUT
COMPILE_OUTPUT_VARIABLE ${TEST}_COMPILE_OUTPUT
RUN_OUTPUT_VARIABLE ${TEST}_RUN_OUTPUT
)
if (${TEST}_COMPILE)
if (${TEST}_RUN EQUAL "0")
Expand All @@ -818,14 +819,17 @@ macro (H5ConversionTests TEST def msg)
set (${TEST} "" CACHE INTERNAL ${msg})
message (VERBOSE "${msg}... no")
file (APPEND ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeError.log
"Test ${TEST} Run failed with the following output and exit code:\n ${OUTPUT}\n"
"Test ${TEST} Compile succeeded with the following output:\n ${${TEST}_COMPILE_OUTPUT}\n"
)
file (APPEND ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeError.log
"Test ${TEST} Run failed with exit code ${${TEST}_RUN} and with the following output:\n ${${TEST}_RUN_OUTPUT}\n"
)
endif ()
else ()
set (${TEST} "" CACHE INTERNAL ${msg})
message (VERBOSE "${msg}... no")
file (APPEND ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeError.log
"Test ${TEST} Compile failed with the following output:\n ${OUTPUT}\n"
"Test ${TEST} Compile failed with the following output:\n ${${TEST}_COMPILE_OUTPUT}\n"
)
endif ()
else ()
Expand Down Expand Up @@ -887,3 +891,76 @@ H5ConversionTests (${HDF_PREFIX}_LLONG_TO_LDOUBLE_CORRECT TRUE "Checking IF corr
# some long double values
#-----------------------------------------------------------------------------
H5ConversionTests (${HDF_PREFIX}_DISABLE_SOME_LDOUBLE_CONV FALSE "Checking IF the cpu is power9 and cannot correctly converting long double values")

#-----------------------------------------------------------------------------
# Check if _Float16 type is available
#-----------------------------------------------------------------------------
message (STATUS "Checking if _Float16 support is available")
set (${HDF_PREFIX}_HAVE__FLOAT16 0)
HDF_CHECK_TYPE_SIZE (_Float16 ${HDF_PREFIX}_SIZEOF__FLOAT16)
if (${HDF_PREFIX}_SIZEOF__FLOAT16)
# Request _Float16 support
set (CMAKE_REQUIRED_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} "-D__STDC_WANT_IEC_60559_TYPES_EXT__")

# Some compilers expose the _Float16 datatype, but not the macros and
# functions used with the datatype. We need the macros for proper
# datatype conversion support. Check for these here.
CHECK_SYMBOL_EXISTS (FLT16_EPSILON "float.h" h5_have_flt16_epsilon)
CHECK_SYMBOL_EXISTS (FLT16_MIN "float.h" h5_have_flt16_min)
CHECK_SYMBOL_EXISTS (FLT16_MAX "float.h" h5_have_flt16_max)
CHECK_SYMBOL_EXISTS (FLT16_MIN_10_EXP "float.h" h5_have_flt16_min_10_exp)
CHECK_SYMBOL_EXISTS (FLT16_MAX_10_EXP "float.h" h5_have_flt16_max_10_exp)
CHECK_SYMBOL_EXISTS (FLT16_MANT_DIG "float.h" h5_have_flt16_mant_dig)

if (h5_have_flt16_epsilon AND h5_have_flt16_min AND
h5_have_flt16_max AND h5_have_flt16_min_10_exp AND
h5_have_flt16_max_10_exp AND h5_have_flt16_mant_dig)
# Some compilers like OneAPI on Windows appear to detect _Float16 support
# properly up to this point, and, in the absence of any architecture-specific
# tuning compiler flags, will generate code for H5Tconv.c that performs
# software conversions on _Float16 variables with compiler-internal functions
# such as __extendhfsf2, __truncsfhf2, or __truncdfhf2. However, these
# compilers will fail to link these functions into the build for currently
# unknown reasons and cause the build to fail. Since these are compiler-internal
# functions that we don't appear to have much control over, let's try to
# compile a program that will generate these functions to check for _Float16
# support. If we fail to compile this program, we will simply disable
# _Float16 support for the time being.
H5ConversionTests (
${HDF_PREFIX}_FLOAT16_CONVERSION_FUNCS_LINK
FALSE
"Checking if compiler can convert _Float16 type with casts"
)

if (${${HDF_PREFIX}_FLOAT16_CONVERSION_FUNCS_LINK})
# Finally, MacOS 13 appears to have a bug specifically when converting
# long double values to _Float16. Release builds of the dt_arith test
# would cause any assignments to a _Float16 variable to be elided,
# whereas Debug builds would perform incorrect hardware conversions by
# simply chopping off all the bytes of the value except for the first 2.
# These tests pass on MacOS 14, so let's perform a quick test to check
# if the hardware conversion is done correctly.
H5ConversionTests (
${HDF_PREFIX}_LDOUBLE_TO_FLOAT16_CORRECT
TRUE
"Checking if correctly converting long double to _Float16 values"
)

if (NOT ${${HDF_PREFIX}_LDOUBLE_TO_FLOAT16_CORRECT})
message (VERBOSE "Conversions from long double to _Float16 appear to be incorrect. These will be emulated through a soft conversion function.")
endif ()

set (${HDF_PREFIX}_HAVE__FLOAT16 1)

# Check if we can use fabsf16
CHECK_FUNCTION_EXISTS (fabsf16 ${HDF_PREFIX}_HAVE_FABSF16)
else ()
message (STATUS "_Float16 support has been disabled because the compiler couldn't compile and run a test program for _Float16 conversions")
message (STATUS "Check ${CMAKE_BINARY_DIR}/CMakeFiles/CMakeError.log for information on why the test program couldn't be compiled/run")
endif ()
else ()
message (STATUS "_Float16 support has been disabled since the required macros (FLT16_MAX, FLT16_EPSILON, etc. were not found)")
endif ()
else ()
message (STATUS "_Float16 support has been disabled since the _Float16 type was not found")
endif ()
122 changes: 122 additions & 0 deletions config/cmake/ConversionTests.c
Original file line number Diff line number Diff line change
Expand Up @@ -285,3 +285,125 @@ int HDF_NO_UBSAN main(void)
}

#endif

#ifdef H5_FLOAT16_CONVERSION_FUNCS_LINK_TEST

#define __STDC_WANT_IEC_60559_TYPES_EXT__

#include <stdlib.h>
#include <float.h>

int HDF_NO_UBSAN main(void)
{
_Float16 fl16_var;
signed char sc;
unsigned char usc;
short s;
unsigned short us;
int i;
unsigned int ui;
long l;
unsigned long ul;
long long ll;
unsigned long long ull;
float f;
double d;
long double ld;
int ret = 0;

/*
* Cast the _Float16 type between all the different C datatypes
* we support conversions for in H5Tconv.c to check if the compiler
* properly links any software conversion functions it may generate
* for the casts, such as __extendhfsf2 or __truncdfhf2.
*/

fl16_var = 3.0f16;

sc = (signed char)fl16_var;
usc = (unsigned char)fl16_var;
s = (short)fl16_var;
us = (unsigned short)fl16_var;
i = (int)fl16_var;
ui = (unsigned int)fl16_var;
l = (long)fl16_var;
ul = (unsigned long)fl16_var;
ll = (long long)fl16_var;
ull = (unsigned long long)fl16_var;
f = (float)fl16_var;
d = (double)fl16_var;
ld = (long double)fl16_var;

sc = (signed char)3;
fl16_var = (_Float16)sc;

usc = (unsigned char)3;
fl16_var = (_Float16)usc;

s = (short)3;
fl16_var = (_Float16)s;

us = (unsigned short)3;
fl16_var = (_Float16)us;

i = (int)3;
fl16_var = (_Float16)i;

ui = (unsigned int)3;
fl16_var = (_Float16)ui;

l = (long)3;
fl16_var = (_Float16)l;

ul = (unsigned long)3;
fl16_var = (_Float16)ul;

ll = (long long)3;
fl16_var = (_Float16)ll;

ull = (unsigned long long)3;
fl16_var = (_Float16)ull;

f = (float)3.0f;
fl16_var = (_Float16)f;

d = (double)3.0;
fl16_var = (_Float16)d;

ld = (long double)3.0l;
fl16_var = (_Float16)ld;

done:
exit(ret);
}

#endif

#ifdef H5_LDOUBLE_TO_FLOAT16_CORRECT_TEST

#define __STDC_WANT_IEC_60559_TYPES_EXT__

#include <stdlib.h>
#include <float.h>
#include <math.h>
#include <limits.h>

int HDF_NO_UBSAN main(void)
{
long double ld;
_Float16 half;
int ret = 1;

ld = 32.0L;
half = 64.0f16;

half = (_Float16)ld;

if (fabsl(ld - (long double)half) < LDBL_EPSILON)
ret = 0;

done:
exit(ret);
}

#endif
12 changes: 12 additions & 0 deletions config/cmake/H5pubconf.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@
/* Define if library information should be embedded in the executables */
#cmakedefine H5_HAVE_EMBEDDED_LIBINFO @H5_HAVE_EMBEDDED_LIBINFO@

/* Define to 1 if you have the `fabsf16' function. */
#cmakedefine H5_HAVE_FABSF16 @H5_HAVE_FABSF16@

/* Define to 1 if you have the `fcntl' function. */
#cmakedefine H5_HAVE_FCNTL @H5_HAVE_FCNTL@

Expand All @@ -143,6 +146,9 @@
/* Define if support for szip filter is enabled */
#cmakedefine H5_HAVE_FILTER_SZIP @H5_HAVE_FILTER_SZIP@

/* Determine if _Float16 is available */
#cmakedefine H5_HAVE__FLOAT16 @H5_HAVE__FLOAT16@

/* Determine if __float128 is available */
#cmakedefine H5_HAVE_FLOAT128 @H5_HAVE_FLOAT128@

Expand Down Expand Up @@ -384,6 +390,9 @@
/* Define if new-style references should be used with dimension scales */
#cmakedefine H5_DIMENSION_SCALES_WITH_NEW_REF @H5_DIMENSION_SCALES_WITH_NEW_REF@

/* Define if your system can convert long double to _Float16 values correctly. */
#cmakedefine H5_LDOUBLE_TO_FLOAT16_CORRECT @H5_LDOUBLE_TO_FLOAT16_CORRECT@

/* Define if your system can convert long double to (unsigned) long long
values correctly. */
#cmakedefine H5_LDOUBLE_TO_LLONG_ACCURATE @H5_LDOUBLE_TO_LLONG_ACCURATE@
Expand Down Expand Up @@ -586,6 +595,9 @@
/* 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@

Expand Down
103 changes: 103 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,109 @@ AC_CHECK_SIZEOF([float])
AC_CHECK_SIZEOF([double])
AC_CHECK_SIZEOF([long double])

## ----------------------------------------------------------------------
## Check if _Float16 support is available
##
AC_MSG_NOTICE([checking if _Float16 support is available])
HAVE__FLOAT16="no"
AC_CHECK_SIZEOF([_Float16])
if test "$ac_cv_sizeof__Float16" != 0; then
# Some compilers expose the _Float16 datatype, but not the macros and
# functions used with the datatype. We need the macros for proper
# datatype conversion support. Check for these here.
AC_CHECK_DECL([FLT16_EPSILON], [], [], [[
#define __STDC_WANT_IEC_60559_TYPES_EXT__
#include <float.h>]])
AC_CHECK_DECL([FLT16_MIN], [], [], [[
#define __STDC_WANT_IEC_60559_TYPES_EXT__
#include <float.h>]])
AC_CHECK_DECL([FLT16_MAX], [], [], [[
#define __STDC_WANT_IEC_60559_TYPES_EXT__
#include <float.h>]])
AC_CHECK_DECL([FLT16_MIN_10_EXP], [], [], [[
#define __STDC_WANT_IEC_60559_TYPES_EXT__
#include <float.h>]])
AC_CHECK_DECL([FLT16_MAX_10_EXP], [], [], [[
#define __STDC_WANT_IEC_60559_TYPES_EXT__
#include <float.h>]])
AC_CHECK_DECL([FLT16_MANT_DIG], [], [], [[
#define __STDC_WANT_IEC_60559_TYPES_EXT__
#include <float.h>]])

if test "X$ac_cv_have_decl_FLT16_EPSILON" = "Xyes" &&
test "X$ac_cv_have_decl_FLT16_MIN" = "Xyes" &&
test "X$ac_cv_have_decl_FLT16_MAX" = "Xyes" &&
test "X$ac_cv_have_decl_FLT16_MIN_10_EXP" = "Xyes" &&
test "X$ac_cv_have_decl_FLT16_MAX_10_EXP" = "Xyes" &&
test "X$ac_cv_have_decl_FLT16_MANT_DIG" = "Xyes" ; then
# Some compilers like OneAPI on Windows appear to detect _Float16 support
# properly up to this point, and, in the absence of any architecture-specific
# tuning compiler flags, will generate code for H5Tconv.c that performs
# software conversions on _Float16 variables with compiler-internal functions
# such as __extendhfsf2, __truncsfhf2, or __truncdfhf2. However, these
# compilers will fail to link these functions into the build for currently
# unknown reasons and cause the build to fail. Since these are compiler-internal
# functions that we don't appear to have much control over, let's try to
# compile a program that will generate these functions to check for _Float16
# support. If we fail to compile this program, we will simply disable
# _Float16 support for the time being.
AC_MSG_CHECKING([if compiler can correctly compile and run a test program which converts _Float16 to other types with casts])
TEST_SRC="`(echo \"#define H5_FLOAT16_CONVERSION_FUNCS_LINK_TEST 1\"; cat $srcdir/config/cmake/ConversionTests.c)`"
AC_CACHE_VAL([hdf5_cv_float16_conversion_funcs_link],
[AC_RUN_IFELSE(
[AC_LANG_SOURCE([$TEST_SRC])],
[hdf5_cv_float16_conversion_funcs_link=yes], [hdf5_cv_float16_conversion_funcs_link=no], [hdf5_cv_float16_conversion_funcs_link=no])])

if test ${hdf5_cv_float16_conversion_funcs_link} = "yes"; then
AC_MSG_RESULT([yes])

# Finally, MacOS 13 appears to have a bug specifically when converting
# long double values to _Float16. Release builds of the dt_arith test
# would cause any assignments to a _Float16 variable to be elided,
# whereas Debug builds would perform incorrect hardware conversions by
# simply chopping off all the bytes of the value except for the first 2.
# These tests pass on MacOS 14, so let's perform a quick test to check
# if the hardware conversion is done correctly.
AC_MSG_CHECKING([if compiler can correctly convert long double values to _Float16])
TEST_SRC="`(echo \"#define H5_LDOUBLE_TO_FLOAT16_CORRECT_TEST 1\"; cat $srcdir/config/cmake/ConversionTests.c)`"
if test ${ac_cv_sizeof_long_double} = 0; then
hdf5_cv_ldouble_to_float16_correct=${hdf5_cv_ldouble_to_float16_correct=no}
else
AC_CACHE_VAL([hdf5_cv_ldouble_to_float16_correct],
[AC_RUN_IFELSE(
[AC_LANG_SOURCE([$TEST_SRC])],
[hdf5_cv_ldouble_to_float16_correct=yes], [hdf5_cv_ldouble_to_float16_correct=no], [hdf5_cv_ldouble_to_float16_correct=yes])])
fi

if test ${hdf5_cv_ldouble_to_float16_correct} = "yes"; then
AC_DEFINE([LDOUBLE_TO_FLOAT16_CORRECT], [1],
[Define if your system can convert long double to _Float16 values correctly.])
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
AC_MSG_NOTICE([Conversions from long double to _Float16 appear to be incorrect. These will be emulated through a soft conversion function.])
fi

HAVE__FLOAT16="yes"

# Check if we can use fabsf16
AC_CHECK_FUNC([fabsf16], [AC_DEFINE([HAVE_FABSF16], [1],
[Define if has fabsf16 function])], [])

# Define HAVE__FLOAT16 macro for H5pubconf.h if _Float16 is available.
AC_DEFINE([HAVE__FLOAT16], [1], [Determine if _Float16 is available])
else
AC_MSG_RESULT([no])
fi
fi

AC_MSG_CHECKING([if _Float16 support is enabled])
AC_MSG_RESULT([$HAVE__FLOAT16])
fi

# Define HAVE__FLOAT16 value to substitute into other files for conditional testing
AC_SUBST([HAVE__FLOAT16])

## ----------------------------------------------------------------------
## Check if the Fortran interface should be enabled
##
Expand Down
2 changes: 1 addition & 1 deletion doxygen/dox/DDLBNF112.dox
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/** \page DDLBNF112 DDL in BNF for HDF5 1.12 and above
/** \page DDLBNF112 DDL in BNF for HDF5 1.12 through HDF5 1.14.3

\todo Revise this & break it up!

Expand Down
Loading

0 comments on commit 330b80a

Please sign in to comment.