From 2400729ecfd2c7be8b18aeaa822fef5a4b503f8a Mon Sep 17 00:00:00 2001 From: Ulrich Weigand Date: Wed, 22 Nov 2017 13:53:43 +0100 Subject: [PATCH] Target FP: Make use of MPFR if available This second patch introduces mfpr_float_ops, an new implementation of target_float_ops. This implements precise emulation of target floating-point formats using the MPFR library. This is then used to perform operations on types that do not match any host type. Note that use of MPFR is still not required. The patch adds a configure option --with-mpfr similar to --with-expat. If use of MPFR is disabled via the option or MPFR is not available, code will fall back to current behavior. This means that operations on types that do not match any host type will be implemented on the host long double type instead. A new test case verifies that we can correctly print the largest __float128 value now. gdb/ChangeLog: 2017-11-22 Ulrich Weigand * NEWS: Document use of GNU MPFR. * README: Likewise. * Makefile.in (LIBMPFR): Add define. (CLIBS): Add $(LIBMPFR). * configure.ac: Add --with-mpfr configure option. * configure: Regenerate. * config.in: Regenerate. * target-float.c [HAVE_LIBMPFR]: Include . (class mpfr_float_ops): New type. (mpfr_float_ops::from_target): Two new overloaded functions. (mpfr_float_ops::to_target): Likewise. (mpfr_float_ops::to_string): New function. (mpfr_float_ops::from_string): Likewise. (mpfr_float_ops::to_longest): Likewise. (mpfr_float_ops::from_longest): Likewise. (mpfr_float_ops::from_ulongest): Likewise. (mpfr_float_ops::to_host_double): Likewise. (mpfr_float_ops::from_host_double): Likewise. (mpfr_float_ops::convert): Likewise. (mpfr_float_ops::binop): Likewise. (mpfr_float_ops::compare): Likewise. (get_target_float_ops): Use mpfr_float_ops if available. gdb/doc/ChangeLog: 2017-11-22 Ulrich Weigand * gdb.texinfo (Requirements): Document use of GNU MPFR. gdb/testsuite/ChangeLog: 2017-11-22 Ulrich Weigand * gdb.base/float128.c (large128): New variable. * gdb.base/float128.exp: Add test to print largest __float128 value. --- gdb/ChangeLog | 27 ++ gdb/Makefile.in | 5 +- gdb/NEWS | 5 + gdb/README | 17 + gdb/config.in | 3 + gdb/configure | 499 ++++++++++++++++++++++++ gdb/configure.ac | 22 ++ gdb/doc/ChangeLog | 5 + gdb/doc/gdb.texinfo | 15 + gdb/target-float.c | 569 ++++++++++++++++++++++++++++ gdb/testsuite/ChangeLog | 5 + gdb/testsuite/gdb.base/float128.c | 4 + gdb/testsuite/gdb.base/float128.exp | 3 + 13 files changed, 1178 insertions(+), 1 deletion(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index b330c7b1cf5..56233006cab 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,30 @@ +2017-11-22 Ulrich Weigand + + * NEWS: Document use of GNU MPFR. + * README: Likewise. + + * Makefile.in (LIBMPFR): Add define. + (CLIBS): Add $(LIBMPFR). + * configure.ac: Add --with-mpfr configure option. + * configure: Regenerate. + * config.in: Regenerate. + + * target-float.c [HAVE_LIBMPFR]: Include . + (class mpfr_float_ops): New type. + (mpfr_float_ops::from_target): Two new overloaded functions. + (mpfr_float_ops::to_target): Likewise. + (mpfr_float_ops::to_string): New function. + (mpfr_float_ops::from_string): Likewise. + (mpfr_float_ops::to_longest): Likewise. + (mpfr_float_ops::from_longest): Likewise. + (mpfr_float_ops::from_ulongest): Likewise. + (mpfr_float_ops::to_host_double): Likewise. + (mpfr_float_ops::from_host_double): Likewise. + (mpfr_float_ops::convert): Likewise. + (mpfr_float_ops::binop): Likewise. + (mpfr_float_ops::compare): Likewise. + (get_target_float_ops): Use mpfr_float_ops if available. + 2017-11-22 Ulrich Weigand * target-float.c: Do not include . diff --git a/gdb/Makefile.in b/gdb/Makefile.in index a3bfbf93e1f..78a43c52d25 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -194,6 +194,9 @@ LIBBABELTRACE = @LIBBABELTRACE@ # Where is libipt? This will be empty if libipt was not available. LIBIPT = @LIBIPT@ +# Where is libmpfr? This will be empty if libmpfr was not available. +LIBMPFR = @LIBMPFR@ + WARN_CFLAGS = @WARN_CFLAGS@ WERROR_CFLAGS = @WERROR_CFLAGS@ GDB_WARN_CFLAGS = $(WARN_CFLAGS) @@ -703,7 +706,7 @@ CLIBS = $(SIM) $(READLINE) $(OPCODES) $(BFD) $(ZLIB) $(INTL) $(LIBIBERTY) $(LIBD $(XM_CLIBS) $(NAT_CLIBS) $(GDBTKLIBS) \ @LIBS@ @GUILE_LIBS@ @PYTHON_LIBS@ \ $(LIBEXPAT) $(LIBLZMA) $(LIBBABELTRACE) $(LIBIPT) \ - $(LIBIBERTY) $(WIN32LIBS) $(LIBGNU) $(LIBICONV) + $(LIBIBERTY) $(WIN32LIBS) $(LIBGNU) $(LIBICONV) $(LIBMPFR) CDEPS = $(XM_CDEPS) $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE_DEPS) \ $(OPCODES) $(INTL_DEPS) $(LIBIBERTY) $(CONFIG_DEPS) $(LIBGNU) diff --git a/gdb/NEWS b/gdb/NEWS index dc070facb86..a239624b827 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -3,6 +3,11 @@ *** Changes since GDB 8.0 +* GDB now uses the GNU MPFR library, if available, to emulate target + floating-point arithmetic during expression evaluation when the target + uses different floating-point formats than the host. At least version + 3.1 of GNU MPFR is required. + * GDB now supports access to the guarded-storage-control registers and the software-based guarded-storage broadcast control registers on IBM z14. diff --git a/gdb/README b/gdb/README index d485536ecfe..e43887ffcdf 100644 --- a/gdb/README +++ b/gdb/README @@ -89,6 +89,14 @@ features. Expat will be linked in if it is available at build time, or those features will be disabled. The latest version of Expat should be available from `http://expat.sourceforge.net'. + GDB uses GNU MPFR, a library for multiple-precision floating-point +computation with correct rounding, to emulate target floating-point +arithmetic during expression evaluation when the target uses different +floating-point formats than the host. MPFR will be linked in if it is +available at build time. If GNU MPFR it is not available, GDB will fall +back to using host floating-point arithmetic. The latest version of +GNU MPFR should be available from `http://www.mpfr.org'. + GDB can be used as a cross-debugger, running on a machine of one type while debugging a program running on a machine of another type. See below. @@ -475,6 +483,15 @@ prefer; but you may abbreviate option names if you use `--'. have libexpat installed, you can get the latest version from http://expat.sourceforge.net. +`--with-mpfr' + Build GDB with the GNU MPFR library. (Done by default if + GNU MPFR is installed and found at configure time.) This library + is used to emulate target floating-point arithmetic during expression + evaluation when the target uses different floating-point formats than + the host. If GNU MPFR is not available, GDB will fall back to using + host floating-point arithmetic. If your host does not have GNU MPFR + installed, you can get the latest version from http://www.mpfr.org. + `--with-python[=PATH]' Build GDB with Python scripting support. (Done by default if libpython is present and found at configure time.) Python makes diff --git a/gdb/config.in b/gdb/config.in index 904c604cc2e..60cdc77cceb 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -249,6 +249,9 @@ /* Define to 1 if you have the `mcheck' library (-lmcheck). */ #undef HAVE_LIBMCHECK +/* Define if you have the mpfr library. */ +#undef HAVE_LIBMPFR + /* Define if Python 2.4 is being used. */ #undef HAVE_LIBPYTHON2_4 diff --git a/gdb/configure b/gdb/configure index def194dceea..55544fb4bf8 100755 --- a/gdb/configure +++ b/gdb/configure @@ -682,6 +682,9 @@ PYTHON_LIBS PYTHON_CPPFLAGS PYTHON_CFLAGS python_prog_path +LTLIBMPFR +LIBMPFR +HAVE_LIBMPFR LTLIBEXPAT LIBEXPAT HAVE_LIBEXPAT @@ -838,6 +841,8 @@ with_system_readline with_jit_reader_dir with_expat with_libexpat_prefix +with_mpfr +with_libmpfr_prefix with_python with_guile enable_libmcheck @@ -1560,6 +1565,9 @@ Optional Packages: --with-expat include expat support (auto/yes/no) --with-libexpat-prefix[=DIR] search for libexpat in DIR/include and DIR/lib --without-libexpat-prefix don't search for libexpat in includedir and libdir + --with-mpfr include MPFR support (auto/yes/no) + --with-libmpfr-prefix[=DIR] search for libmpfr in DIR/include and DIR/lib + --without-libmpfr-prefix don't search for libmpfr in includedir and libdir --with-python[=PYTHON] include python support (auto/yes/no/) --with-guile[=GUILE] include guile support @@ -9643,6 +9651,497 @@ done fi fi + +# Check whether --with-mpfr was given. +if test "${with_mpfr+set}" = set; then : + withval=$with_mpfr; +else + with_mpfr=auto +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use MPFR" >&5 +$as_echo_n "checking whether to use MPFR... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_mpfr" >&5 +$as_echo "$with_mpfr" >&6; } + +if test "${with_mpfr}" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: MPFR support disabled; some features may be unavailable." >&5 +$as_echo "$as_me: WARNING: MPFR support disabled; some features may be unavailable." >&2;} + HAVE_LIBMPFR=no +else + + + + + + + + + use_additional=yes + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + +# Check whether --with-libmpfr-prefix was given. +if test "${with_libmpfr_prefix+set}" = set; then : + withval=$with_libmpfr_prefix; + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + else + additional_includedir="$withval/include" + additional_libdir="$withval/lib" + fi + fi + +fi + + LIBMPFR= + LTLIBMPFR= + INCMPFR= + rpathdirs= + ltrpathdirs= + names_already_handled= + names_next_round='mpfr ' + while test -n "$names_next_round"; do + names_this_round="$names_next_round" + names_next_round= + for name in $names_this_round; do + already_handled= + for n in $names_already_handled; do + if test "$n" = "$name"; then + already_handled=yes + break + fi + done + if test -z "$already_handled"; then + names_already_handled="$names_already_handled $name" + uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'` + eval value=\"\$HAVE_LIB$uppername\" + if test -n "$value"; then + if test "$value" = yes; then + eval value=\"\$LIB$uppername\" + test -z "$value" || LIBMPFR="${LIBMPFR}${LIBMPFR:+ }$value" + eval value=\"\$LTLIB$uppername\" + test -z "$value" || LTLIBMPFR="${LTLIBMPFR}${LTLIBMPFR:+ }$value" + else + : + fi + else + found_dir= + found_la= + found_so= + found_a= + if test $use_additional = yes; then + if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext"; then + found_dir="$additional_libdir" + found_so="$additional_libdir/lib$name.$shlibext" + if test -f "$additional_libdir/lib$name.la"; then + found_la="$additional_libdir/lib$name.la" + fi + else + if test -f "$additional_libdir/lib$name.$libext"; then + found_dir="$additional_libdir" + found_a="$additional_libdir/lib$name.$libext" + if test -f "$additional_libdir/lib$name.la"; then + found_la="$additional_libdir/lib$name.la" + fi + fi + fi + fi + if test "X$found_dir" = "X"; then + for x in $LDFLAGS $LTLIBMPFR; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + case "$x" in + -L*) + dir=`echo "X$x" | sed -e 's/^X-L//'` + if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext"; then + found_dir="$dir" + found_so="$dir/lib$name.$shlibext" + if test -f "$dir/lib$name.la"; then + found_la="$dir/lib$name.la" + fi + else + if test -f "$dir/lib$name.$libext"; then + found_dir="$dir" + found_a="$dir/lib$name.$libext" + if test -f "$dir/lib$name.la"; then + found_la="$dir/lib$name.la" + fi + fi + fi + ;; + esac + if test "X$found_dir" != "X"; then + break + fi + done + fi + if test "X$found_dir" != "X"; then + LTLIBMPFR="${LTLIBMPFR}${LTLIBMPFR:+ }-L$found_dir -l$name" + if test "X$found_so" != "X"; then + if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then + LIBMPFR="${LIBMPFR}${LIBMPFR:+ }$found_so" + else + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $found_dir" + fi + if test "$hardcode_direct" = yes; then + LIBMPFR="${LIBMPFR}${LIBMPFR:+ }$found_so" + else + if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then + LIBMPFR="${LIBMPFR}${LIBMPFR:+ }$found_so" + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $found_dir" + fi + else + haveit= + for x in $LDFLAGS $LIBMPFR; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + LIBMPFR="${LIBMPFR}${LIBMPFR:+ }-L$found_dir" + fi + if test "$hardcode_minus_L" != no; then + LIBMPFR="${LIBMPFR}${LIBMPFR:+ }$found_so" + else + LIBMPFR="${LIBMPFR}${LIBMPFR:+ }-l$name" + fi + fi + fi + fi + else + if test "X$found_a" != "X"; then + LIBMPFR="${LIBMPFR}${LIBMPFR:+ }$found_a" + else + LIBMPFR="${LIBMPFR}${LIBMPFR:+ }-L$found_dir -l$name" + fi + fi + additional_includedir= + case "$found_dir" in + */lib | */lib/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'` + additional_includedir="$basedir/include" + ;; + esac + if test "X$additional_includedir" != "X"; then + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + for x in $CPPFLAGS $INCMPFR; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + INCMPFR="${INCMPFR}${INCMPFR:+ }-I$additional_includedir" + fi + fi + fi + fi + fi + if test -n "$found_la"; then + save_libdir="$libdir" + case "$found_la" in + */* | *\\*) . "$found_la" ;; + *) . "./$found_la" ;; + esac + libdir="$save_libdir" + for dep in $dependency_libs; do + case "$dep" in + -L*) + additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` + if test "X$additional_libdir" != "X/usr/lib"; then + haveit= + if test "X$additional_libdir" = "X/usr/local/lib"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + haveit= + for x in $LDFLAGS $LIBMPFR; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + LIBMPFR="${LIBMPFR}${LIBMPFR:+ }-L$additional_libdir" + fi + fi + haveit= + for x in $LDFLAGS $LTLIBMPFR; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + LTLIBMPFR="${LTLIBMPFR}${LTLIBMPFR:+ }-L$additional_libdir" + fi + fi + fi + fi + ;; + -R*) + dir=`echo "X$dep" | sed -e 's/^X-R//'` + if test "$enable_rpath" != no; then + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $dir" + fi + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $dir" + fi + fi + ;; + -l*) + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` + ;; + *.la) + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` + ;; + *) + LIBMPFR="${LIBMPFR}${LIBMPFR:+ }$dep" + LTLIBMPFR="${LTLIBMPFR}${LTLIBMPFR:+ }$dep" + ;; + esac + done + fi + else + LIBMPFR="${LIBMPFR}${LIBMPFR:+ }-l$name" + LTLIBMPFR="${LTLIBMPFR}${LTLIBMPFR:+ }-l$name" + fi + fi + fi + done + done + if test "X$rpathdirs" != "X"; then + if test -n "$hardcode_libdir_separator"; then + alldirs= + for found_dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir" + done + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIBMPFR="${LIBMPFR}${LIBMPFR:+ }$flag" + else + for found_dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$found_dir" + eval flag=\"$hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIBMPFR="${LIBMPFR}${LIBMPFR:+ }$flag" + done + fi + fi + if test "X$ltrpathdirs" != "X"; then + for found_dir in $ltrpathdirs; do + LTLIBMPFR="${LTLIBMPFR}${LTLIBMPFR:+ }-R$found_dir" + done + fi + + + ac_save_CPPFLAGS="$CPPFLAGS" + + for element in $INCMPFR; do + haveit= + for x in $CPPFLAGS; do + + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + eval x=\"$x\" + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" + + if test "X$x" = "X$element"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element" + fi + done + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libmpfr" >&5 +$as_echo_n "checking for libmpfr... " >&6; } +if test "${ac_cv_libmpfr+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + + ac_save_LIBS="$LIBS" + LIBS="$LIBS $LIBMPFR" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +mpfr_exp_t exp; mpfr_t x; + mpfr_frexp (&exp, x, x, MPFR_RNDN); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_libmpfr=yes +else + ac_cv_libmpfr=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS="$ac_save_LIBS" + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libmpfr" >&5 +$as_echo "$ac_cv_libmpfr" >&6; } + if test "$ac_cv_libmpfr" = yes; then + HAVE_LIBMPFR=yes + +$as_echo "#define HAVE_LIBMPFR 1" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libmpfr" >&5 +$as_echo_n "checking how to link with libmpfr... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBMPFR" >&5 +$as_echo "$LIBMPFR" >&6; } + else + HAVE_LIBMPFR=no + CPPFLAGS="$ac_save_CPPFLAGS" + LIBMPFR= + LTLIBMPFR= + fi + + + + + + + if test "$HAVE_LIBMPFR" != yes; then + if test "$with_mpfr" = yes; then + as_fn_error "MPFR is missing or unusable" "$LINENO" 5 + else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: MPFR is missing or unusable; some features may be unavailable." >&5 +$as_echo "$as_me: WARNING: MPFR is missing or unusable; some features may be unavailable." >&2;} + fi + fi +fi + # --------------------- # # Check for libpython. # # --------------------- # diff --git a/gdb/configure.ac b/gdb/configure.ac index b909217c77d..d43899c4146 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -695,6 +695,28 @@ else fi fi +AC_ARG_WITH(mpfr, + AS_HELP_STRING([--with-mpfr], [include MPFR support (auto/yes/no)]), + [], [with_mpfr=auto]) +AC_MSG_CHECKING([whether to use MPFR]) +AC_MSG_RESULT([$with_mpfr]) + +if test "${with_mpfr}" = no; then + AC_MSG_WARN([MPFR support disabled; some features may be unavailable.]) + HAVE_LIBMPFR=no +else + AC_LIB_HAVE_LINKFLAGS([mpfr], [], [#include ], + [mpfr_exp_t exp; mpfr_t x; + mpfr_frexp (&exp, x, x, MPFR_RNDN);]) + if test "$HAVE_LIBMPFR" != yes; then + if test "$with_mpfr" = yes; then + AC_MSG_ERROR([MPFR is missing or unusable]) + else + AC_MSG_WARN([MPFR is missing or unusable; some features may be unavailable.]) + fi + fi +fi + # --------------------- # # Check for libpython. # # --------------------- # diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 2d8f5afb3d8..81af3aa632f 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2017-11-22 Ulrich Weigand + + * gdb.texinfo (Requirements): Document use of GNU MPFR. + + 2017-11-16 Phil Muldoon * python.texi (Basic Python): Add rbreak documentation. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 29d47892fc7..ab05a3718d6 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -34270,6 +34270,21 @@ Branch trace (@pxref{Branch Trace Format}, @pxref{Branch Trace Configuration Format}) @end itemize +@item MPFR +@anchor{MPFR} +@value{GDBN} can use the GNU MPFR multiple-precision floating-point +library. This library may be included with your operating system +distribution; if it is not, you can get the latest version from +@url{http://www.mpfr.org}. The @file{configure} script will search +for this library in several standard locations; if it is installed +in an unusual path, you can use the @option{--with-libmpfr-prefix} +option to specify its location. + +GNU MPFR is used to emulate target floating-point arithmetic during +expression evaluation when the target uses different floating-point +formats than the host. If GNU MPFR it is not available, @value{GDBN} +will fall back to using host floating-point arithmetic. + @item zlib @cindex compressed debug sections @value{GDBN} will use the @samp{zlib} library, if available, to read diff --git a/gdb/target-float.c b/gdb/target-float.c index 8f1b9f64432..32237ec9d93 100644 --- a/gdb/target-float.c +++ b/gdb/target-float.c @@ -1142,6 +1142,569 @@ host_float_ops::compare (const gdb_byte *x, const struct type *type_x, } +/* Implementation of target_float_ops using the MPFR library + mpfr_t as intermediate type. */ + +#ifdef HAVE_LIBMPFR + +#include + +class mpfr_float_ops : public target_float_ops +{ +public: + std::string to_string (const gdb_byte *addr, const struct type *type, + const char *format) const override; + bool from_string (gdb_byte *addr, const struct type *type, + const std::string &string) const override; + + LONGEST to_longest (const gdb_byte *addr, + const struct type *type) const override; + void from_longest (gdb_byte *addr, const struct type *type, + LONGEST val) const override; + void from_ulongest (gdb_byte *addr, const struct type *type, + ULONGEST val) const override; + double to_host_double (const gdb_byte *addr, + const struct type *type) const override; + void from_host_double (gdb_byte *addr, const struct type *type, + double val) const override; + void convert (const gdb_byte *from, const struct type *from_type, + gdb_byte *to, const struct type *to_type) const override; + + void binop (enum exp_opcode opcode, + const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y, + gdb_byte *res, const struct type *type_res) const override; + int compare (const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y) const override; + +private: + /* Local wrapper class to handle mpfr_t initalization and cleanup. */ + class gdb_mpfr + { + public: + mpfr_t val; + + gdb_mpfr (const struct type *type) + { + const struct floatformat *fmt = floatformat_from_type (type); + mpfr_init2 (val, floatformat_precision (fmt)); + } + + gdb_mpfr (const gdb_mpfr &source) + { + mpfr_init2 (val, mpfr_get_prec (source.val)); + } + + ~gdb_mpfr () + { + mpfr_clear (val); + } + }; + + void from_target (const struct floatformat *fmt, + const gdb_byte *from, gdb_mpfr &to) const; + void from_target (const struct type *type, + const gdb_byte *from, gdb_mpfr &to) const; + + void to_target (const struct type *type, + const gdb_mpfr &from, gdb_byte *to) const; + void to_target (const struct floatformat *fmt, + const gdb_mpfr &from, gdb_byte *to) const; +}; + + +/* Convert TO/FROM target floating-point format to mpfr_t. */ + +void +mpfr_float_ops::from_target (const struct floatformat *fmt, + const gdb_byte *orig_from, gdb_mpfr &to) const +{ + const gdb_byte *from = orig_from; + mpfr_exp_t exponent; + unsigned long mant; + unsigned int mant_bits, mant_off; + int mant_bits_left; + int special_exponent; /* It's a NaN, denorm or zero. */ + enum floatformat_byteorders order; + unsigned char newfrom[FLOATFORMAT_LARGEST_BYTES]; + enum float_kind kind; + + gdb_assert (fmt->totalsize + <= FLOATFORMAT_LARGEST_BYTES * FLOATFORMAT_CHAR_BIT); + + /* Handle non-numbers. */ + kind = floatformat_classify (fmt, from); + if (kind == float_infinite) + { + mpfr_set_inf (to.val, floatformat_is_negative (fmt, from) ? -1 : 1); + return; + } + if (kind == float_nan) + { + mpfr_set_nan (to.val); + return; + } + + order = floatformat_normalize_byteorder (fmt, from, newfrom); + + if (order != fmt->byteorder) + from = newfrom; + + if (fmt->split_half) + { + gdb_mpfr top (to), bot (to); + + from_target (fmt->split_half, from, top); + /* Preserve the sign of 0, which is the sign of the top half. */ + if (mpfr_zero_p (top.val)) + { + mpfr_set (to.val, top.val, MPFR_RNDN); + return; + } + from_target (fmt->split_half, + from + fmt->totalsize / FLOATFORMAT_CHAR_BIT / 2, bot); + mpfr_add (to.val, top.val, bot.val, MPFR_RNDN); + return; + } + + exponent = get_field (from, order, fmt->totalsize, fmt->exp_start, + fmt->exp_len); + /* Note that if exponent indicates a NaN, we can't really do anything useful + (not knowing if the host has NaN's, or how to build one). So it will + end up as an infinity or something close; that is OK. */ + + mant_bits_left = fmt->man_len; + mant_off = fmt->man_start; + mpfr_set_zero (to.val, 0); + + special_exponent = exponent == 0 || exponent == fmt->exp_nan; + + /* Don't bias NaNs. Use minimum exponent for denorms. For + simplicity, we don't check for zero as the exponent doesn't matter. + Note the cast to int; exp_bias is unsigned, so it's important to + make sure the operation is done in signed arithmetic. */ + if (!special_exponent) + exponent -= fmt->exp_bias; + else if (exponent == 0) + exponent = 1 - fmt->exp_bias; + + /* Build the result algebraically. Might go infinite, underflow, etc; + who cares. */ + + /* If this format uses a hidden bit, explicitly add it in now. Otherwise, + increment the exponent by one to account for the integer bit. */ + + if (!special_exponent) + { + if (fmt->intbit == floatformat_intbit_no) + mpfr_set_ui_2exp (to.val, 1, exponent, MPFR_RNDN); + else + exponent++; + } + + gdb_mpfr tmp (to); + + while (mant_bits_left > 0) + { + mant_bits = std::min (mant_bits_left, 32); + + mant = get_field (from, order, fmt->totalsize, mant_off, mant_bits); + + mpfr_set_si (tmp.val, mant, MPFR_RNDN); + mpfr_mul_2si (tmp.val, tmp.val, exponent - mant_bits, MPFR_RNDN); + mpfr_add (to.val, to.val, tmp.val, MPFR_RNDN); + exponent -= mant_bits; + mant_off += mant_bits; + mant_bits_left -= mant_bits; + } + + /* Negate it if negative. */ + if (get_field (from, order, fmt->totalsize, fmt->sign_start, 1)) + mpfr_neg (to.val, to.val, MPFR_RNDN); +} + +void +mpfr_float_ops::from_target (const struct type *type, + const gdb_byte *from, gdb_mpfr &to) const +{ + from_target (floatformat_from_type (type), from, to); +} + +void +mpfr_float_ops::to_target (const struct floatformat *fmt, + const gdb_mpfr &from, gdb_byte *orig_to) const +{ + unsigned char *to = orig_to; + mpfr_exp_t exponent; + unsigned int mant_bits, mant_off; + int mant_bits_left; + enum floatformat_byteorders order = fmt->byteorder; + unsigned char newto[FLOATFORMAT_LARGEST_BYTES]; + + if (order != floatformat_little) + order = floatformat_big; + + if (order != fmt->byteorder) + to = newto; + + memset (to, 0, floatformat_totalsize_bytes (fmt)); + + if (fmt->split_half) + { + gdb_mpfr top (from), bot (from); + + mpfr_set (top.val, from.val, MPFR_RNDN); + /* If the rounded top half is Inf, the bottom must be 0 not NaN + or Inf. */ + if (mpfr_inf_p (top.val)) + mpfr_set_zero (bot.val, 0); + else + mpfr_sub (bot.val, from.val, top.val, MPFR_RNDN); + + to_target (fmt->split_half, top, to); + to_target (fmt->split_half, bot, + to + fmt->totalsize / FLOATFORMAT_CHAR_BIT / 2); + return; + } + + gdb_mpfr tmp (from); + + if (mpfr_zero_p (from.val)) + goto finalize_byteorder; /* Result is zero */ + + mpfr_set (tmp.val, from.val, MPFR_RNDN); + + if (mpfr_nan_p (tmp.val)) /* Result is NaN */ + { + /* From is NaN */ + put_field (to, order, fmt->totalsize, fmt->exp_start, + fmt->exp_len, fmt->exp_nan); + /* Be sure it's not infinity, but NaN value is irrel. */ + put_field (to, order, fmt->totalsize, fmt->man_start, + fmt->man_len, 1); + goto finalize_byteorder; + } + + /* If negative, set the sign bit. */ + if (mpfr_sgn (tmp.val) < 0) + { + put_field (to, order, fmt->totalsize, fmt->sign_start, 1, 1); + mpfr_neg (tmp.val, tmp.val, MPFR_RNDN); + } + + if (mpfr_inf_p (tmp.val)) /* Result is Infinity. */ + { + /* Infinity exponent is same as NaN's. */ + put_field (to, order, fmt->totalsize, fmt->exp_start, + fmt->exp_len, fmt->exp_nan); + /* Infinity mantissa is all zeroes. */ + put_field (to, order, fmt->totalsize, fmt->man_start, + fmt->man_len, 0); + goto finalize_byteorder; + } + + mpfr_frexp (&exponent, tmp.val, tmp.val, MPFR_RNDN); + + if (exponent + fmt->exp_bias <= 0) + { + /* The value is too small to be expressed in the destination + type (not enough bits in the exponent. Treat as 0. */ + put_field (to, order, fmt->totalsize, fmt->exp_start, + fmt->exp_len, 0); + put_field (to, order, fmt->totalsize, fmt->man_start, + fmt->man_len, 0); + goto finalize_byteorder; + } + + if (exponent + fmt->exp_bias >= (1 << fmt->exp_len)) + { + /* The value is too large to fit into the destination. + Treat as infinity. */ + put_field (to, order, fmt->totalsize, fmt->exp_start, + fmt->exp_len, fmt->exp_nan); + put_field (to, order, fmt->totalsize, fmt->man_start, + fmt->man_len, 0); + goto finalize_byteorder; + } + + put_field (to, order, fmt->totalsize, fmt->exp_start, fmt->exp_len, + exponent + fmt->exp_bias - 1); + + mant_bits_left = fmt->man_len; + mant_off = fmt->man_start; + while (mant_bits_left > 0) + { + unsigned long mant_long; + + mant_bits = mant_bits_left < 32 ? mant_bits_left : 32; + + mpfr_mul_2ui (tmp.val, tmp.val, 32, MPFR_RNDN); + mant_long = mpfr_get_ui (tmp.val, MPFR_RNDZ) & 0xffffffffL; + mpfr_sub_ui (tmp.val, tmp.val, mant_long, MPFR_RNDZ); + + /* If the integer bit is implicit, then we need to discard it. + If we are discarding a zero, we should be (but are not) creating + a denormalized number which means adjusting the exponent + (I think). */ + if (mant_bits_left == fmt->man_len + && fmt->intbit == floatformat_intbit_no) + { + mant_long <<= 1; + mant_long &= 0xffffffffL; + /* If we are processing the top 32 mantissa bits of a doublest + so as to convert to a float value with implied integer bit, + we will only be putting 31 of those 32 bits into the + final value due to the discarding of the top bit. In the + case of a small float value where the number of mantissa + bits is less than 32, discarding the top bit does not alter + the number of bits we will be adding to the result. */ + if (mant_bits == 32) + mant_bits -= 1; + } + + if (mant_bits < 32) + { + /* The bits we want are in the most significant MANT_BITS bits of + mant_long. Move them to the least significant. */ + mant_long >>= 32 - mant_bits; + } + + put_field (to, order, fmt->totalsize, + mant_off, mant_bits, mant_long); + mant_off += mant_bits; + mant_bits_left -= mant_bits; + } + + finalize_byteorder: + /* Do we need to byte-swap the words in the result? */ + if (order != fmt->byteorder) + floatformat_normalize_byteorder (fmt, newto, orig_to); +} + +void +mpfr_float_ops::to_target (const struct type *type, + const gdb_mpfr &from, gdb_byte *to) const +{ + /* Ensure possible padding bytes in the target buffer are zeroed out. */ + memset (to, 0, TYPE_LENGTH (type)); + + to_target (floatformat_from_type (type), from, to); +} + +/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, + to a string, optionally using the print format FORMAT. */ +std::string +mpfr_float_ops::to_string (const gdb_byte *addr, + const struct type *type, + const char *format) const +{ + const struct floatformat *fmt = floatformat_from_type (type); + + /* Unless we need to adhere to a specific format, provide special + output for certain cases. */ + if (format == nullptr) + { + /* Detect invalid representations. */ + if (!floatformat_is_valid (fmt, addr)) + return ""; + + /* Handle NaN and Inf. */ + enum float_kind kind = floatformat_classify (fmt, addr); + if (kind == float_nan) + { + const char *sign = floatformat_is_negative (fmt, addr)? "-" : ""; + const char *mantissa = floatformat_mantissa (fmt, addr); + return string_printf ("%snan(0x%s)", sign, mantissa); + } + else if (kind == float_infinite) + { + const char *sign = floatformat_is_negative (fmt, addr)? "-" : ""; + return string_printf ("%sinf", sign); + } + } + + /* Determine the format string to use on the host side. */ + std::string host_format = floatformat_printf_format (fmt, format, 'R'); + + gdb_mpfr tmp (type); + from_target (type, addr, tmp); + + int size = mpfr_snprintf (NULL, 0, host_format.c_str (), tmp.val); + std::string str (size, '\0'); + mpfr_sprintf (&str[0], host_format.c_str (), tmp.val); + + return str; +} + +/* Parse string STRING into a target floating-number of type TYPE and + store it as byte-stream ADDR. Return whether parsing succeeded. */ +bool +mpfr_float_ops::from_string (gdb_byte *addr, + const struct type *type, + const std::string &in) const +{ + gdb_mpfr tmp (type); + + char *endptr; + mpfr_strtofr (tmp.val, in.c_str (), &endptr, 0, MPFR_RNDN); + + /* We only accept the whole string. */ + if (*endptr) + return false; + + to_target (type, tmp, addr); + return true; +} + +/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, + to an integer value (rounding towards zero). */ +LONGEST +mpfr_float_ops::to_longest (const gdb_byte *addr, + const struct type *type) const +{ + gdb_mpfr tmp (type); + from_target (type, addr, tmp); + return mpfr_get_sj (tmp.val, MPFR_RNDZ); +} + +/* Convert signed integer VAL to a target floating-number of type TYPE + and store it as byte-stream ADDR. */ +void +mpfr_float_ops::from_longest (gdb_byte *addr, + const struct type *type, + LONGEST val) const +{ + gdb_mpfr tmp (type); + mpfr_set_sj (tmp.val, val, MPFR_RNDN); + to_target (type, tmp, addr); +} + +/* Convert unsigned integer VAL to a target floating-number of type TYPE + and store it as byte-stream ADDR. */ +void +mpfr_float_ops::from_ulongest (gdb_byte *addr, + const struct type *type, + ULONGEST val) const +{ + gdb_mpfr tmp (type); + mpfr_set_uj (tmp.val, val, MPFR_RNDN); + to_target (type, tmp, addr); +} + +/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, + to a floating-point value in the host "double" format. */ +double +mpfr_float_ops::to_host_double (const gdb_byte *addr, + const struct type *type) const +{ + gdb_mpfr tmp (type); + from_target (type, addr, tmp); + return mpfr_get_d (tmp.val, MPFR_RNDN); +} + +/* Convert floating-point value VAL in the host "double" format to a target + floating-number of type TYPE and store it as byte-stream ADDR. */ +void +mpfr_float_ops::from_host_double (gdb_byte *addr, + const struct type *type, + double val) const +{ + gdb_mpfr tmp (type); + mpfr_set_d (tmp.val, val, MPFR_RNDN); + to_target (type, tmp, addr); +} + +/* Convert a floating-point number of type FROM_TYPE from the target + byte-stream FROM to a floating-point number of type TO_TYPE, and + store it to the target byte-stream TO. */ +void +mpfr_float_ops::convert (const gdb_byte *from, + const struct type *from_type, + gdb_byte *to, + const struct type *to_type) const +{ + gdb_mpfr from_tmp (from_type), to_tmp (to_type); + from_target (from_type, from, from_tmp); + mpfr_set (to_tmp.val, from_tmp.val, MPFR_RNDN); + to_target (to_type, to_tmp, to); +} + +/* Perform the binary operation indicated by OPCODE, using as operands the + target byte streams X and Y, interpreted as floating-point numbers of + types TYPE_X and TYPE_Y, respectively. Convert the result to type + TYPE_RES and store it into the byte-stream RES. */ +void +mpfr_float_ops::binop (enum exp_opcode op, + const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y, + gdb_byte *res, const struct type *type_res) const +{ + gdb_mpfr x_tmp (type_x), y_tmp (type_y), tmp (type_res); + + from_target (type_x, x, x_tmp); + from_target (type_y, y, y_tmp); + + switch (op) + { + case BINOP_ADD: + mpfr_add (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); + break; + + case BINOP_SUB: + mpfr_sub (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); + break; + + case BINOP_MUL: + mpfr_mul (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); + break; + + case BINOP_DIV: + mpfr_div (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); + break; + + case BINOP_EXP: + mpfr_pow (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); + break; + + case BINOP_MIN: + mpfr_min (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); + break; + + case BINOP_MAX: + mpfr_max (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); + break; + + default: + error (_("Integer-only operation on floating point number.")); + break; + } + + to_target (type_res, tmp, res); +} + +/* Compare the two target byte streams X and Y, interpreted as floating-point + numbers of types TYPE_X and TYPE_Y, respectively. Return zero if X and Y + are equal, -1 if X is less than Y, and 1 otherwise. */ +int +mpfr_float_ops::compare (const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y) const +{ + gdb_mpfr x_tmp (type_x), y_tmp (type_y); + + from_target (type_x, x, x_tmp); + from_target (type_y, y, y_tmp); + + if (mpfr_equal_p (x_tmp.val, y_tmp.val)) + return 0; + else if (mpfr_less_p (x_tmp.val, y_tmp.val)) + return -1; + else + return 1; +} + +#endif + + /* Helper routines operating on decimal floating-point data. */ /* Decimal floating point is one of the extension to IEEE 754, which is @@ -1685,10 +2248,16 @@ get_target_float_ops (enum target_float_ops_kind kind) } /* For binary floating-point formats that do not match any host format, + use mpfr_t as intermediate format to provide precise target-floating + point emulation. However, if the MPFR library is not availabe, use the largest host floating-point type as intermediate format. */ case target_float_ops_kind::binary: { +#ifdef HAVE_LIBMPFR + static mpfr_float_ops binary_float_ops; +#else static host_float_ops binary_float_ops; +#endif return &binary_float_ops; } diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index b916e93a92c..00279828a0b 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2017-11-22 Ulrich Weigand + + * gdb.base/float128.c (large128): New variable. + * gdb.base/float128.exp: Add test to print largest __float128 value. + 2017-11-22 Ulrich Weigand * gdb.arch/vsx-regs.exp: Update register content checks. diff --git a/gdb/testsuite/gdb.base/float128.c b/gdb/testsuite/gdb.base/float128.c index 4ded2f4cc8c..8cd437473b5 100644 --- a/gdb/testsuite/gdb.base/float128.c +++ b/gdb/testsuite/gdb.base/float128.c @@ -21,6 +21,10 @@ long double ld; __float128 f128; +// Test largest IEEE-128 value. This has to be supported since the +// __float128 data type by definition is encoded as IEEE-128. +__float128 large128 = 1.18973149535723176508575932662800702e+4932q; + int main() { ld = 1.375l; diff --git a/gdb/testsuite/gdb.base/float128.exp b/gdb/testsuite/gdb.base/float128.exp index 9a46282168a..803bb596114 100644 --- a/gdb/testsuite/gdb.base/float128.exp +++ b/gdb/testsuite/gdb.base/float128.exp @@ -74,3 +74,6 @@ gdb_test_multiple "set var f128=20.375l" "$test" { gdb_test "print ld" ".* = 10\\.375.*" "the value of ld is changed to 10.375" gdb_test "print f128" ".* = 20\\.375.*" "the value of f128 is changed to 20.375" +# Test that we can correctly handle the largest IEEE-128 value +gdb_test "print large128" ".* = 1\\.18973149535723176508575932662800702e\\+4932" "print large128" + -- 2.30.2