From patchwork Thu Nov 16 19:00:08 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ulrich Weigand X-Patchwork-Id: 24297 Received: (qmail 34526 invoked by alias); 16 Nov 2017 19:00:45 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 29675 invoked by uid 89); 16 Nov 2017 19:00:39 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-9.7 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_2, GIT_PATCH_3, KAM_ASCII_DIVIDERS, KAM_LINEPADDING, KB_WAM_FROM_NAME_SINGLEWORD, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.3.2 spammy=*la, xl, x*, XL X-HELO: mx0a-001b2d01.pphosted.com Received: from mx0b-001b2d01.pphosted.com (HELO mx0a-001b2d01.pphosted.com) (148.163.158.5) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 16 Nov 2017 19:00:24 +0000 Received: from pps.filterd (m0098419.ppops.net [127.0.0.1]) by mx0b-001b2d01.pphosted.com (8.16.0.21/8.16.0.21) with SMTP id vAGIxj0K056708 for ; Thu, 16 Nov 2017 14:00:13 -0500 Received: from e06smtp14.uk.ibm.com (e06smtp14.uk.ibm.com [195.75.94.110]) by mx0b-001b2d01.pphosted.com with ESMTP id 2e9etu596y-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Thu, 16 Nov 2017 14:00:12 -0500 Received: from localhost by e06smtp14.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 16 Nov 2017 19:00:10 -0000 Received: from b06cxnps4074.portsmouth.uk.ibm.com (9.149.109.196) by e06smtp14.uk.ibm.com (192.168.101.144) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Thu, 16 Nov 2017 19:00:09 -0000 Received: from d06av26.portsmouth.uk.ibm.com (d06av26.portsmouth.uk.ibm.com [9.149.105.62]) by b06cxnps4074.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id vAGJ09hS44826666 for ; Thu, 16 Nov 2017 19:00:09 GMT Received: from d06av26.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id DD322AE05F for ; Thu, 16 Nov 2017 18:53:31 +0000 (GMT) Received: from d06av26.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id B8D11AE058 for ; Thu, 16 Nov 2017 18:53:31 +0000 (GMT) Received: from oc3748833570.ibm.com (unknown [9.152.213.29]) by d06av26.portsmouth.uk.ibm.com (Postfix) with ESMTP for ; Thu, 16 Nov 2017 18:53:31 +0000 (GMT) Received: by oc3748833570.ibm.com (Postfix, from userid 1000) id CD22CD80106; Thu, 16 Nov 2017 20:00:08 +0100 (CET) Subject: [RFC v2][2/2] Target FP: Make use of MPFR if available To: gdb-patches@sourceware.org Date: Thu, 16 Nov 2017 20:00:08 +0100 (CET) From: "Ulrich Weigand" MIME-Version: 1.0 X-TM-AS-GCONF: 00 x-cbid: 17111619-0016-0000-0000-000005022F2E X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17111619-0017-0000-0000-0000283DEF6C Message-Id: <20171116190008.CD22CD80106@oc3748833570.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-11-16_06:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 suspectscore=15 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 impostorscore=0 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1709140000 definitions=main-1711160253 [RFC v2][2/2] 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. Bye, Ulrich gdb/ChangeLog: * 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. * Makefile.in (LIBMPFR): Add define. (CLIBS): Add $(LIBMPFR). * configure.ac: Add --with-mpfr configure option. * configure: Regenerate. * config.in: Regenerate. gdb/testsuite/ChangeLog: * gdb.base/float128.c (large128): New variable. * gdb.base/float128.exp: Add test to print largest __float128 value. Index: binutils-gdb/gdb/target-float.c =================================================================== --- binutils-gdb.orig/gdb/target-float.c +++ binutils-gdb/gdb/target-float.c @@ -1142,6 +1142,569 @@ host_float_ops::compare (const gdb_by } +/* 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_ } /* 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; } Index: binutils-gdb/gdb/testsuite/gdb.base/float128.c =================================================================== --- binutils-gdb.orig/gdb/testsuite/gdb.base/float128.c +++ binutils-gdb/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; Index: binutils-gdb/gdb/testsuite/gdb.base/float128.exp =================================================================== --- binutils-gdb.orig/gdb/testsuite/gdb.base/float128.exp +++ binutils-gdb/gdb/testsuite/gdb.base/float128.exp @@ -74,3 +74,6 @@ gdb_test_multiple "set var f128=20.375l" 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" + Index: binutils-gdb/gdb/Makefile.in =================================================================== --- binutils-gdb.orig/gdb/Makefile.in +++ binutils-gdb/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) $( $(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) Index: binutils-gdb/gdb/config.in =================================================================== --- binutils-gdb.orig/gdb/config.in +++ binutils-gdb/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 Index: binutils-gdb/gdb/configure.ac =================================================================== --- binutils-gdb.orig/gdb/configure.ac +++ binutils-gdb/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. # # --------------------- # Index: binutils-gdb/gdb/configure =================================================================== --- binutils-gdb.orig/gdb/configure +++ binutils-gdb/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. # # --------------------- #