This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[patch 2/2] Wrap-up expression support for DFP.
- From: Thiago Jung Bauermann <bauerman at br dot ibm dot com>
- To: gdb-patches at sourceware dot org
- Date: Thu, 20 Dec 2007 03:49:28 -0200
- Subject: [patch 2/2] Wrap-up expression support for DFP.
- References: <20071220054926.148275471@br.ibm.com>
This patch implements all the missing expression support for
decimal floating point that I could find:
- +, -, *, /, exp, <, = with operands of same or different sizes of DFP,
or a DFP and an integer type;
- negation (C's !), unary +;
- casting to and from decimal float.
If anything else is missing I'll be glad to add it.
Notes:
- doesn't support conversion of 64-bit integers to decimal float,
because of libdecnumber limitation;
- error checking in decimal float operations ignore underflow, overflow
and divide by zero to imitate binary float implementation;
- decimal_from_floating is not very nice because it uses sprintf, but
I couldn't think of a better way.
Tested with no regressions on Linux/ppc32 and Linux/ppc64.
Ok to commit?
--
[]'s
Thiago Jung Bauermann
Software Engineer
IBM Linux Technology Center
gdb/
2007-12-19 Thiago Jung Bauermann <bauerman@br.ibm.com>
* Makefile.in (dfp.o): Depend on expression.h, gdbtypes.h and value.h.
(valarith.o): Depend on dfp.h.
(valops.o): Likewise.
* dfp.c: Include expression.h, gdbtypes.h, value.h and dfp.h.
(set_decnumber_context): New function.
(decimal_check_errors): Likewise.
(decimal_from_number): Likewise.
(decimal_to_number): Likewise.
(decimal_from_string): Use set_decnumber_context and
decimal_check_errors.
(decimal_from_integral): New function.
(decimal_from_floating): Likewise.
(decimal_to_double): Likewise.
(promote_decimal): Likewise.
(decimal_binop): Likewise.
(decimal_is_zero): Likewise.
(decimal_compare): Likewise.
(decimal_convert): Likewise.
* dfp.h (decimal_from_integral): New prototype.
(decimal_from_floating): Likewise.
(decimal_to_double): Likewise.
(decimal_binop): Likewise.
(decimal_is_zero): Likewise.
(decimal_compare): Likewise.
(decimal_convert): Likewise.
* eval.c (evaluate_subexp_standard): Remove expect_type argument from
call to value_from_decfloat.
* valarith.c: Include dfp.h.
(value_args_as_decimal): New function.
(value_binop): Add if block to handle TYPE_CODE_DECFLOAT values.
(value_logical_not): Likewise.
(value_equal): Likewise.
(value_less): Likewise.
(value_pos): Likewise.
(value_neg): Formatting fix.
* valops.c: Include dfp.h.
(value_cast): Add if block to handle TYPE_CODE_DECFLOAT values.
* value.c (unpack_long): Add case to handle TYPE_CODE_DECFLOAT.
(unpack_double): Add if block to handle TYPE_CODE_DECFLOAT.
(value_from_decfloat): Remove expect_type argument.
* value.h (value_from_decfloat): Update prototype.
gdb/testsuite/
2007-12-19 Thiago Jung Bauermann <bauerman@br.ibm.com>
* gdb.base/dfp-exprs.exp (test_dfp_arithmetic_expressions): Add tests
for expressions with decimal float values.
(test_dfp_conversions): New function to test casts to and from
decimal float types.
Call test_dfp_conversions.
* gdb.base/dfp-test.c (struct decstruct): Add float4 and double8
elements.
(main): Initialize ds.float4 and ds.double8 elements.
* gdb.base/dfp-test.exp (d32_set_tests): Fix typo. Adjust expect
string to new error message.
(d64_set_tests): Likewise.
(d128_set_tests): Likewise.
Add tests for expressions with decimal float variables. Add tests for
conversions to and from decimal float types.
Index: src-git/gdb/Makefile.in
===================================================================
--- src-git.orig/gdb/Makefile.in 2007-12-19 16:34:35.000000000 -0200
+++ src-git/gdb/Makefile.in 2007-12-19 16:55:05.000000000 -0200
@@ -2046,7 +2046,8 @@ dsrec.o: dsrec.c $(defs_h) $(serial_h) $
dummy-frame.o: dummy-frame.c $(defs_h) $(dummy_frame_h) $(regcache_h) \
$(frame_h) $(inferior_h) $(gdb_assert_h) $(frame_unwind_h) \
$(command_h) $(gdbcmd_h) $(gdb_string_h)
-dfp.o: dfp.c $(defs_h) $(dfp_h) $(decimal128_h) $(decimal64_h) $(decimal32_h)
+dfp.o: dfp.c $(defs_h) $(expression_h) $(gdbtypes_h) $(value_h) $(dfp_h) \
+ $(decimal128_h) $(decimal64_h) $(decimal32_h)
dwarf2expr.o: dwarf2expr.c $(defs_h) $(symtab_h) $(gdbtypes_h) $(value_h) \
$(gdbcore_h) $(elf_dwarf2_h) $(dwarf2expr_h)
dwarf2-frame.o: dwarf2-frame.c $(defs_h) $(dwarf2expr_h) $(elf_dwarf2_h) \
@@ -2910,12 +2911,12 @@ v850-tdep.o: v850-tdep.c $(defs_h) $(fra
$(regcache_h) $(dis_asm_h) $(osabi_h)
valarith.o: valarith.c $(defs_h) $(value_h) $(symtab_h) $(gdbtypes_h) \
$(expression_h) $(target_h) $(language_h) $(gdb_string_h) \
- $(doublest_h) $(infcall_h)
+ $(doublest_h) $(dfp_h) $(infcall_h)
valops.o: valops.c $(defs_h) $(symtab_h) $(gdbtypes_h) $(value_h) $(frame_h) \
$(inferior_h) $(gdbcore_h) $(target_h) $(demangle_h) $(language_h) \
$(gdbcmd_h) $(regcache_h) $(cp_abi_h) $(block_h) $(infcall_h) \
$(dictionary_h) $(cp_support_h) $(gdb_string_h) $(gdb_assert_h) \
- $(cp_support_h) $(observer_h)
+ $(cp_support_h) $(observer_h) $(dfp_h)
valprint.o: valprint.c $(defs_h) $(gdb_string_h) $(symtab_h) $(gdbtypes_h) \
$(value_h) $(gdbcore_h) $(gdbcmd_h) $(target_h) $(language_h) \
$(annotate_h) $(valprint_h) $(floatformat_h) $(doublest_h) \
Index: src-git/gdb/dfp.c
===================================================================
--- src-git.orig/gdb/dfp.c 2007-12-19 16:34:35.000000000 -0200
+++ src-git/gdb/dfp.c 2007-12-19 19:55:11.000000000 -0200
@@ -18,6 +18,10 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
+#include "expression.h"
+#include "gdbtypes.h"
+#include "value.h"
+#include "dfp.h"
/* The order of the following headers is important for making sure
decNumber structure is large enough to hold decimal128 digits. */
@@ -50,6 +54,89 @@ match_endianness (const gdb_byte *from,
return;
}
+/* Helper function to get the appropriate libdecnumber context for each size
+ of decimal float. */
+static void
+set_decnumber_context (decContext *ctx, int len)
+{
+ switch (len)
+ {
+ case 4:
+ decContextDefault (ctx, DEC_INIT_DECIMAL32);
+ break;
+ case 8:
+ decContextDefault (ctx, DEC_INIT_DECIMAL64);
+ break;
+ case 16:
+ decContextDefault (ctx, DEC_INIT_DECIMAL128);
+ break;
+ }
+
+ ctx->traps = 0;
+}
+
+/* Check for errors signaled in the decimal context structure. */
+static void
+decimal_check_errors (decContext *ctx)
+{
+ /* An error here could be a division by zero, an overflow, an underflow or
+ an invalid operation (from the DEC_Errors constant in decContext.h).
+ Since GDB doesn't complain about division by zero, overflow or underflow
+ errors for binary floating, we won't complain about them for decimal
+ floating either. */
+ if (ctx->status & DEC_IEEE_854_Invalid_operation)
+ {
+ /* Leave only the error bits in the status flags. */
+ ctx->status &= DEC_IEEE_854_Invalid_operation;
+ error (_("Cannot perform operation: %s"), decContextStatusToString (ctx));
+ }
+}
+
+/* Helper function to convert from libdecnumber's appropriate representation
+ for computation to each size of decimal float. */
+static void
+decimal_from_number (const decNumber *from, gdb_byte *to, int len)
+{
+ decContext set;
+
+ set_decnumber_context (&set, len);
+
+ switch (len)
+ {
+ case 4:
+ decimal32FromNumber ((decimal32 *) to, from, &set);
+ break;
+ case 8:
+ decimal64FromNumber ((decimal64 *) to, from, &set);
+ break;
+ case 16:
+ decimal128FromNumber ((decimal128 *) to, from, &set);
+ break;
+ }
+}
+
+/* Helper function to convert each size of decimal float to libdecnumber's
+ appropriate representation for computation. */
+static void
+decimal_to_number (const gdb_byte *from, int len, decNumber *to)
+{
+ switch (len)
+ {
+ case 4:
+ decimal32ToNumber ((decimal32 *) from, to);
+ break;
+ case 8:
+ decimal64ToNumber ((decimal64 *) from, to);
+ break;
+ case 16:
+ decimal128ToNumber ((decimal128 *) from, to);
+ break;
+ default:
+ error (_("Unknown decimal floating point type.\n"));
+ break;
+ }
+}
+
/* Convert decimal type to its string representation. LEN is the length
of the decimal type, 4 bytes for decimal32, 8 bytes for decimal64 and
16 bytes for decimal128. */
@@ -59,6 +146,7 @@ decimal_to_string (const gdb_byte *decby
gdb_byte dec[16];
match_endianness (decbytes, len, dec);
+
switch (len)
{
case 4:
@@ -85,21 +173,17 @@ decimal_from_string (gdb_byte *decbytes,
decContext set;
gdb_byte dec[16];
+ set_decnumber_context (&set, len);
+
switch (len)
{
case 4:
- decContextDefault (&set, DEC_INIT_DECIMAL32);
- set.traps = 0;
decimal32FromString ((decimal32 *) dec, string, &set);
break;
case 8:
- decContextDefault (&set, DEC_INIT_DECIMAL64);
- set.traps = 0;
decimal64FromString ((decimal64 *) dec, string, &set);
break;
case 16:
- decContextDefault (&set, DEC_INIT_DECIMAL128);
- set.traps = 0;
decimal128FromString ((decimal128 *) dec, string, &set);
break;
default:
@@ -109,5 +193,206 @@ decimal_from_string (gdb_byte *decbytes,
match_endianness (dec, len, decbytes);
+ /* Check for errors in the DFP operation. */
+ decimal_check_errors (&set);
+
return 1;
}
+
+/* Converts a value of an integral type to a decimal float of
+ specified LEN bytes. */
+void
+decimal_from_integral (struct value *from, gdb_byte *to, int len)
+{
+ LONGEST l;
+ gdb_byte dec[16];
+ decNumber number;
+ struct type *type;
+
+ type = check_typedef (value_type (from));
+
+ if (TYPE_LENGTH (type) > 4)
+ /* libdecnumber can convert only 32-bit integers. */
+ error (_("Conversion of large integer to a decimal floating type is not supported."));
+
+ l = value_as_long (from);
+
+ if (TYPE_UNSIGNED (type))
+ decNumberFromUInt32 (&number, (unsigned int) l);
+ else
+ decNumberFromInt32 (&number, (int) l);
+
+ decimal_from_number (&number, dec, len);
+ match_endianness (dec, len, to);
+}
+
+/* Converts a value of a float type to a decimal float of
+ specified LEN bytes.
+
+ This is an ugly way to do the conversion, but libdecnumber does
+ not offer a direct way to do it. */
+void
+decimal_from_floating (struct value *from, gdb_byte *to, int len)
+{
+ /* The size of this buffer is a conservative guess: assumes an 128-bit
+ long double could have 40 decimal digits, plus 4 for exponent, plus
+ 3 for mantissa and exponent signs and 'E', plus '\0'. */
+ char buffer[48];
+
+ /* We cannot use snprintf here because it is defined only in C99.
+ We have to assume buffer size is sufficient. */
+ sprintf (buffer, "%Lf", value_as_double (from));
+ decimal_from_string (to, len, buffer);
+}
+
+/* Converts a decimal float of LEN bytes to a double value. */
+DOUBLEST
+decimal_to_double (const gdb_byte *from, int len)
+{
+ char buffer[MAX_DECIMAL_STRING];
+
+ /* This is an ugly way to do the conversion, but libdecnumber does
+ not offer a direct way to do it. */
+ decimal_to_string (from, len, buffer);
+ return atof (buffer);
+}
+
+/* Check if operands have the same size and convert them to the
+ biggest of the two if necessary. */
+static int
+promote_decimal (gdb_byte *x, int len_x, gdb_byte *y, int len_y)
+{
+ int len_result;
+ decNumber number;
+
+ if (len_x < len_y)
+ {
+ decimal_to_number (x, len_x, &number);
+ decimal_from_number (&number, x, len_y);
+ len_result = len_y;
+ }
+ else if (len_x > len_y)
+ {
+ decimal_to_number (y, len_y, &number);
+ decimal_from_number (&number, y, len_x);
+ len_result = len_x;
+ }
+ else
+ len_result = len_x;
+
+ return len_result;
+}
+
+/* Perform operation OP with operands X and Y and store value in RESULT.
+ If LEN_X and LEN_Y are not equal, RESULT will have the size of the biggest
+ of the two, and LEN_RESULT will be set accordingly. */
+void
+decimal_binop (enum exp_opcode op, const gdb_byte *x, int len_x,
+ const gdb_byte *y, int len_y, gdb_byte *result, int *len_result)
+{
+ decContext set;
+ decNumber number1, number2, number3;
+ gdb_byte dec1[16], dec2[16], dec3[16];
+
+ match_endianness (x, len_x, dec1);
+ match_endianness (y, len_y, dec2);
+
+ *len_result = promote_decimal (dec1, len_x, dec2, len_y);
+
+ /* Both operands are of size *len_result from now on. */
+
+ decimal_to_number (dec1, *len_result, &number1);
+ decimal_to_number (dec2, *len_result, &number2);
+
+ set_decnumber_context (&set, *len_result);
+
+ switch (op)
+ {
+ case BINOP_ADD:
+ decNumberAdd (&number3, &number1, &number2, &set);
+ break;
+ case BINOP_SUB:
+ decNumberSubtract (&number3, &number1, &number2, &set);
+ break;
+ case BINOP_MUL:
+ decNumberMultiply (&number3, &number1, &number2, &set);
+ break;
+ case BINOP_DIV:
+ decNumberDivide (&number3, &number1, &number2, &set);
+ break;
+ case BINOP_EXP:
+ decNumberPower (&number3, &number1, &number2, &set);
+ break;
+ default:
+ error (_("Unknown decimal floating point operation."));
+ break;
+ }
+
+ /* Check for errors in the DFP operation. */
+ decimal_check_errors (&set);
+
+ decimal_from_number (&number3, dec3, *len_result);
+
+ match_endianness (dec3, *len_result, result);
+}
+
+/* Returns true if X (which is LEN bytes wide) is the number zero. */
+int
+decimal_is_zero (const gdb_byte *x, int len)
+{
+ decNumber number;
+ gdb_byte dec[16];
+
+ match_endianness (x, len, dec);
+ decimal_to_number (dec, len, &number);
+
+ return decNumberIsZero (&number);
+}
+
+/* Compares two numbers numerically. If X is less than Y then the return value
+ will be -1. If they are equal, then the return value will be 0. If X is
+ greater than the Y then the return value will be 1. */
+int
+decimal_compare (const gdb_byte *x, int len_x, const gdb_byte *y, int len_y)
+{
+ decNumber number1, number2, result;
+ decContext set;
+ gdb_byte dec1[16], dec2[16];
+ int len_result;
+
+ match_endianness (x, len_x, dec1);
+ match_endianness (y, len_y, dec2);
+
+ len_result = promote_decimal (dec1, len_x, dec2, len_y);
+
+ decimal_to_number (dec1, len_result, &number1);
+ decimal_to_number (dec2, len_result, &number2);
+
+ set_decnumber_context (&set, len_result);
+
+ decNumberCompare (&result, &number1, &number2, &set);
+
+ /* Check for errors in the DFP operation. */
+ decimal_check_errors (&set);
+
+ if (decNumberIsNaN (&result))
+ error (_("Comparison with an invalid number (NaN)."));
+ else if (decNumberIsZero (&result))
+ return 0;
+ else if (decNumberIsNegative (&result))
+ return -1;
+ else
+ return 1;
+}
+
+/* Convert a decimal value from a decimal type with LEN_FROM bytes to a
+ decimal type with LEN_TO bytes. */
+void
+decimal_convert (const gdb_byte *from, int len_from, gdb_byte *to,
+ int len_to)
+{
+ decNumber number;
+
+ decimal_to_number (from, len_from, &number);
+ decimal_from_number (&number, to, len_to);
+}
Index: src-git/gdb/dfp.h
===================================================================
--- src-git.orig/gdb/dfp.h 2007-12-19 16:34:35.000000000 -0200
+++ src-git/gdb/dfp.h 2007-12-19 19:54:58.000000000 -0200
@@ -31,5 +31,14 @@
extern void decimal_to_string (const gdb_byte *, int, char *);
extern int decimal_from_string (gdb_byte *, int, const char *);
+extern void decimal_from_integral (struct value *from, gdb_byte *to, int len);
+extern void decimal_from_floating (struct value *from, gdb_byte *to, int len);
+extern DOUBLEST decimal_to_double (const gdb_byte *from, int len);
+extern void decimal_binop (enum exp_opcode, const gdb_byte *, int,
+ const gdb_byte *, int, gdb_byte *, int *);
+extern int decimal_is_zero (const gdb_byte *x, int len);
+extern int decimal_compare (const gdb_byte *x, int len_x, const gdb_byte *y, int len_y);
+extern void decimal_convert (const gdb_byte *from, int len_from, gdb_byte *to,
+ int len_to);
#endif
Index: src-git/gdb/eval.c
===================================================================
--- src-git.orig/gdb/eval.c 2007-12-19 16:34:35.000000000 -0200
+++ src-git/gdb/eval.c 2007-12-19 16:55:05.000000000 -0200
@@ -458,8 +458,8 @@ evaluate_subexp_standard (struct type *e
case OP_DECFLOAT:
(*pos) += 3;
- return value_from_decfloat (expect_type, exp->elts[pc + 1].type,
- exp->elts[pc + 2].decfloatconst);
+ return value_from_decfloat (exp->elts[pc + 1].type,
+ exp->elts[pc + 2].decfloatconst);
case OP_VAR_VALUE:
(*pos) += 3;
Index: src-git/gdb/testsuite/gdb.base/dfp-exprs.exp
===================================================================
--- src-git.orig/gdb/testsuite/gdb.base/dfp-exprs.exp 2007-12-19 16:34:35.000000000 -0200
+++ src-git/gdb/testsuite/gdb.base/dfp-exprs.exp 2007-12-19 16:55:05.000000000 -0200
@@ -74,14 +74,102 @@ proc test_dfp_literals_accepted {} {
proc test_dfp_arithmetic_expressions {} {
-# Arithmetic operations for DFP types are not yet implemented in GDB.
-# These tests are to verify that they will generate expected error messages.
+ # _Decimal32 operands.
+ gdb_test "p 1.4df + 1.2df" " = 2.6"
+ gdb_test "p 1.4df - 1.2df" " = 0.2"
+ gdb_test "p 1.4df * 1.2df" " = 1.68"
+ gdb_test "p 1.4df / 1.2df" " = 1.166667"
+
+ # _Decimal64 operands.
+ gdb_test "p 1.4dd + 1.2dd" " = 2.6"
+ gdb_test "p 1.4dd - 1.2dd" " = 0.2"
+ gdb_test "p 1.4dd * 1.2dd" " = 1.68"
+ gdb_test "p 1.4dd / 1.2dd" " = 1.166666666666667"
+
+ # _Decimal128 operands.
+ gdb_test "p 1.4dl + 1.2dl" " = 2.6"
+ gdb_test "p 1.4dl - 1.2dl" " = 0.2"
+ gdb_test "p 1.4dl * 1.2dl" " = 1.68"
+ gdb_test "p 1.4dl / 1.2dl" " = 1.166666666666666666666666666666667"
+
+ # Test type of operation result.
+ gdb_test "ptype 2.df + 2.df" "= _Decimal32"
+ gdb_test "ptype 2.dd + 2.dd" "= _Decimal64"
+ gdb_test "ptype 2.dl + 2.dl" "= _Decimal128"
+
+ # Mixture of different _Decimal sizes.
+ gdb_test "p 2.1df + 2.7dd" "= 4.8"
+ gdb_test "p 2.1dd + 2.7df" "= 4.8"
+ gdb_test "p 2.6df + 2.7dl" "= 5.3"
+ gdb_test "p 2.6dl + 2.7df" "= 5.3"
+ gdb_test "p 2.3dd + 2.2dl" "= 4.5"
+ gdb_test "p 2.3dl + 2.2dd" "= 4.5"
+ gdb_test "ptype 2.df + 2.dd" "= _Decimal64"
+ gdb_test "ptype 2.df + 2.dl" "= _Decimal128"
+ gdb_test "ptype 2.dd + 2.dl" "= _Decimal128"
+
+ # Mixture of Decimal and integral operands
+ gdb_test "p 1.2df + 1" " = 2.2"
+ gdb_test "p 2 + 1.7dd" " = 3.7"
+ gdb_test "p 3 + 2.1dl" " = 5.1"
+ gdb_test "ptype 1.2df + 1" " = _Decimal32"
+ gdb_test "ptype 2 + 1.7dd" " = _Decimal64"
+ gdb_test "ptype 3 + 2.1dl" " = _Decimal128"
+
+ # Reject operation with integral larger than 32-bits
+ gdb_test "p 1.2df + 2ll" "Conversion of large integer to a decimal floating type is not supported."
+
+ # Reject operation with DFP and Binary FP
+ gdb_test "p 1.2df + 1.2f" "Mixing decimal floating types with other floating types is not allowed."
+
+ # Test other operations with DFP operands
+ gdb_test "p !0.df" " = 1"
+ gdb_test "p !0.dd" " = 1"
+ gdb_test "p !0.dl" " = 1"
+ gdb_test "p !0.5df" " = 0"
+ gdb_test "p !0.5dd" " = 0"
+ gdb_test "p !0.5dl" " = 0"
+
+ gdb_test "p 1.2df == 1.2df" " = 1"
+ gdb_test "p 1.2df == 1.2dd" " = 1"
+ gdb_test "p 1.2df == 1.2dl" " = 1"
+ gdb_test "p 1.2dd == 1.2df" " = 1"
+ gdb_test "p 1.2dd == 1.2dl" " = 1"
+ gdb_test "p 1.2dl == 1.2df" " = 1"
+ gdb_test "p 1.2dl == 1.2dd" " = 1"
+ gdb_test "p 1.2df == 1.3df" " = 0"
+ gdb_test "p 1.2df == 1.3dd" " = 0"
+ gdb_test "p 1.2df == 1.3dl" " = 0"
+ gdb_test "p 1.2dd == 1.3df" " = 0"
+ gdb_test "p 1.2dd == 1.3dl" " = 0"
+ gdb_test "p 1.2dl == 1.3df" " = 0"
+ gdb_test "p 1.2dl == 1.3dd" " = 0"
+
+ gdb_test "p +1.2df" " = 1.2"
+ gdb_test "p +1.2dd" " = 1.2"
+ gdb_test "p +1.2dl" " = 1.2"
+
+ gdb_test "p 1.2df < 1.3df" " = 1"
+ gdb_test "p 1.2df < 1.3dd" " = 1"
+ gdb_test "p 1.2dl < 1.3df" " = 1"
+ gdb_test "p 1.2dd < 1.3dd" " = 1"
+ gdb_test "p 1.2dd < 1.3dl" " = 1"
+ gdb_test "p 1.2dl < 1.3dl" " = 1"
+ gdb_test "p 1.2dl < 1.3df" " = 1"
+ gdb_test "p 1.2df > 1" " = 1"
+ gdb_test "p 1.2dl > 2" " = 0"
+ gdb_test "p 2 > 1.2dd" " = 1"
+ gdb_test "p 2 > 3.1dl" " = 0"
+}
- gdb_test "p 1.4df + 1.2df" "Argument to arithmetic operation not a number or boolean.*"
- gdb_test "p 1.4df - 1.2df" ".*Argument to arithmetic operation not a number or boolean.*"
- gdb_test "p 1.4df * 1.2df" "Argument to arithmetic operation not a number or boolean.*"
- gdb_test "p 1.4df / 1.2df" "Argument to arithmetic operation not a number or boolean.*"
+proc test_dfp_conversions {} {
+ # Test cast to and from DFP values, and between DFPs of different sizes
+ gdb_test "p (float) -0.1df" " = -0.099999994"
+ gdb_test "p (int) 8.3dd" " = 8"
+ gdb_test "p (_Decimal64) 3.1" " = 3.100000"
+ gdb_test "p (_Decimal128) 3.7df" " = 3.7"
+ gdb_test "p (_Decimal32) 4" " = 4"
}
# Start with a fresh gdb.
@@ -92,3 +180,4 @@ gdb_reinitialize_dir $srcdir/$subdir
test_dfp_literals_accepted
test_dfp_arithmetic_expressions
+test_dfp_conversions
Index: src-git/gdb/testsuite/gdb.base/dfp-test.c
===================================================================
--- src-git.orig/gdb/testsuite/gdb.base/dfp-test.c 2007-12-19 16:34:35.000000000 -0200
+++ src-git/gdb/testsuite/gdb.base/dfp-test.c 2007-12-19 16:55:05.000000000 -0200
@@ -26,6 +26,8 @@ struct decstruct
{
int int4;
long long8;
+ float float4;
+ double double8;
_Decimal32 dec32;
_Decimal64 dec64;
_Decimal128 dec128;
@@ -85,6 +87,8 @@ int main()
ds.int4 = 1;
ds.long8 = 2;
+ ds.float4 = 3.1;
+ ds.double8 = 4.2;
ds.dec32 = 1.2345df;
ds.dec64 = 1.2345dd;
ds.dec128 = 1.2345dl;
Index: src-git/gdb/testsuite/gdb.base/dfp-test.exp
===================================================================
--- src-git.orig/gdb/testsuite/gdb.base/dfp-test.exp 2007-12-19 16:34:35.000000000 -0200
+++ src-git/gdb/testsuite/gdb.base/dfp-test.exp 2007-12-20 01:39:19.000000000 -0200
@@ -18,6 +18,53 @@
# This file is part of the gdb testsuite. It is intended to test that
# gdb could correctly handle decimal floating point introduced in IEEE 754R.
+if $tracelevel then {
+ strace $tracelevel
+}
+
+set testfile "dfp-test"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+# Try to compile the test case. If we can't, assume the
+# toolchain does not yet provide DFP support and bail out.
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {quiet debug}] != "" } {
+ verbose "Skipping DFP tests."
+ return -1
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+if ![runto_main] then {
+ perror "couldn't run to breakpoint"
+ continue
+}
+
+# Detect the size of the target's basic types (from gdb.base/long_long.exp).
+
+proc get_valueof { fmt exp default } {
+ global gdb_prompt
+ send_gdb "print${fmt} ${exp}\n"
+ gdb_expect {
+ -re "\\$\[0-9\]* = (\[-\]*\[0-9\]*).*$gdb_prompt $" {
+ set val $expect_out(1,string)
+ }
+ timeout {
+ set val ${default}
+ }
+ }
+ return ${val}
+}
+
+proc get_sizeof { type default } {
+ return [get_valueof "/d" "sizeof (${type})" $default]
+}
+
+set sizeof_long [get_sizeof "long" 4]
+
proc d32_set_tests {} {
gdb_test "p d32=123.45df" " = 123.45"
@@ -43,10 +90,10 @@ proc d32_set_tests {} {
gdb_test "p d32=1.234567E+97df" " = Infinity" "1.234567E+97 is Infinity"
# Test that gdb could detect the errors in the string representation of _Decimal32
- gdb_test "p d32=12345.df" " = 12345" "12345. is an valid number"
+ gdb_test "p d32=12345.df" " = 12345" "12345. is a valid number"
gdb_test "p d32=12345df" ".*Invalid number.*" "12345 is an invalid number"
- gdb_test "p d32=1.23Edf" " = NaN" "1.23E is NaN (not a number)"
- gdb_test "p d32=1.23E45Adf" " = NaN" "1.23E45A is NaN (not a number)"
+ gdb_test "p d32=1.23Edf" ".*Conversion syntax.*" "1.23E is an invalid number"
+ gdb_test "p d32=1.23E45Adf" ".*Conversion syntax.*" "1.23E45A is an invalid number"
}
proc d64_set_tests {} {
@@ -75,8 +122,8 @@ proc d64_set_tests {} {
# Test that gdb could detect the errors in the string representation of _Decimal64
gdb_test "p d64=12345dd" ".*Invalid number.*" "12345dd is an invalid number"
- gdb_test "p d64=1.23Edd" " = NaN" "1.23E is NaN (not a number)"
- gdb_test "p d64=1.23E45Add" "= NaN" "1.23E45A is NaN (not a number)"
+ gdb_test "p d64=1.23Edd" ".*Conversion syntax.*" "1.23E is an invalid number"
+ gdb_test "p d64=1.23E45Add" ".*Conversion syntax.*" "1.23E45A is an invalid number"
}
proc d128_set_tests {} {
@@ -104,33 +151,8 @@ proc d128_set_tests {} {
# Test that gdb could detect the errors in the string representation of _Decimal128
gdb_test "p d128=12345dl" ".*Invalid number.*" "12345dl is an invalid number"
- gdb_test "p d128=1.23Edl" " = NaN" "1.23E is NaN (not a number)"
- gdb_test "p d128=1.23E45Adl" "= NaN" "1.23E45A is NaN (not a number)"
-}
-
-
-if $tracelevel {
- strace $tracelevel
-}
-
-set testfile "dfp-test"
-set srcfile ${testfile}.c
-set binfile ${objdir}/${subdir}/${testfile}
-# Try to compile the test case. If we can't, assume the
-# toolchain does not yet provide DFP support and bail out.
-if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {quiet debug}] != "" } {
- verbose "Skipping DFP tests."
- return -1
-}
-
-gdb_exit
-gdb_start
-gdb_reinitialize_dir $srcdir/$subdir
-gdb_load ${binfile}
-
-if ![runto_main] then {
- perror "couldn't run to breakpoint"
- continue
+ gdb_test "p d128=1.23Edl" ".*Conversion syntax.*" "1.23E is an invalid number"
+ gdb_test "p d128=1.23E45Adl" ".*Conversion syntax.*" "1.23E45A is an invalid number"
}
# Different tests on 32-bits decimal floating point, including the printing
@@ -237,6 +259,57 @@ gdb_test "print ds.dec32" " = 1.2345"
gdb_test "print ds.dec64" " = 1.2345"
gdb_test "print ds.dec128" " = 1.2345"
+# Test expressions with DFP variables.
+
+gdb_test "print d32 + ds.dec32" " = 1.3345"
+gdb_test "print d64 + ds.dec64" " = 1.3345"
+gdb_test "print d128 + ds.dec128" " = 1.3345"
+
+# Test conversion between different _Decimal sizes.
+
+gdb_test "ptype d64 + ds.dec32" " = volatile _Decimal64"
+gdb_test "ptype d128 + ds.dec32" " = volatile _Decimal128"
+gdb_test "ptype d128 + ds.dec64" " = volatile _Decimal128"
+
+# Mixture of Decimal and integral operands
+gdb_test "p d32 + 1" " = 1.1"
+gdb_test "p 2 + d64" " = 2.1"
+gdb_test "p ds.int4 + d128" " = 1.1"
+gdb_test "ptype d32 + 1" " = volatile _Decimal32"
+gdb_test "ptype ds.int4 + d128" " = volatile _Decimal128"
+
+# Test other operations with DFP operands
+gdb_test "p !d32" " = 0"
+gdb_test "p !d64" " = 0"
+gdb_test "p !d128" " = 0"
+gdb_test "p +d32" " = 0.1"
+gdb_test "p +d64" " = 0.1"
+gdb_test "p +d128" " = 0.1"
+gdb_test "p d64 == d128" " = 1"
+gdb_test "p d128 == ds.dec32" " = 0"
+gdb_test "p d128 == d32" " = 1"
+gdb_test "p ds.dec32 == ds.dec64" " = 1"
+gdb_test "p d32 < ds.dec32" " = 1"
+gdb_test "p d64 < ds.dec64" " = 1"
+gdb_test "p d128 < ds.dec128" " = 1"
+gdb_test "p ds.dec32 < d32" " = 0"
+gdb_test "p d64 > ds.dec64" " = 0"
+gdb_test "p ds.dec128 > d128 " " = 1"
+gdb_test "p d32 < ds.int4" " = 1"
+gdb_test "p ds.int4 > d32" " = 1"
+gdb_test "p ds.dec32 < ds.int4" " = 0"
+gdb_test "p ds.int4 > ds.dec64" " = 0"
+gdb_test "p ds.dec128 > ds.int4" " = 1"
+
+# Reject operation with integral larger than 32-bits
+if { ${sizeof_long} > 4 } {
+ gdb_test "p d32 + ds.long8" "Conversion of large integer to a decimal floating type is not supported."
+}
+
+# Reject operation with DFP and Binary FP
+gdb_test "p d64 + ds.float4" "Mixing decimal floating types with other floating types is not allowed."
+gdb_test "p ds.double8 + d128" "Mixing decimal floating types with other floating types is not allowed."
+
# The following tests are intended to verify that gdb can handle "d1=d2"
# and "d1=-d2" correctly.
@@ -246,3 +319,12 @@ gdb_test "print ds.dec128=d128" " = 0.1"
gdb_test "print ds.dec32 = -d32" " = -0.1"
gdb_test "print ds.dec64 = -d64" " = -0.1"
gdb_test "print ds.dec128 = -d128" " = -0.1"
+
+# Test cast to and from DFP values
+
+gdb_test "print ds.double8 = ds.dec64" " = -0.10000000000000001"
+gdb_test "print ds.dec64 = ds.float4" " = 3.100000"
+gdb_test "print ds.dec128 = -ds.double8" " = 0.100000"
+gdb_test "print ds.dec128 = ds.dec32" " = -0.1"
+gdb_test "print ds.dec32 = ds.int4" " = 1"
+gdb_test "print ds.int4 = 7.3dl" " = 7"
Index: src-git/gdb/valarith.c
===================================================================
--- src-git.orig/gdb/valarith.c 2007-12-19 16:34:35.000000000 -0200
+++ src-git/gdb/valarith.c 2007-12-19 16:55:05.000000000 -0200
@@ -28,6 +28,7 @@
#include "language.h"
#include "gdb_string.h"
#include "doublest.h"
+#include "dfp.h"
#include <math.h>
#include "infcall.h"
@@ -741,6 +742,60 @@ value_concat (struct value *arg1, struct
}
+/* Obtain decimal value of arguments for binary operation, converting from
+ other types if one of them is not decimal floating point. */
+static void
+value_args_as_decimal (struct value *arg1, struct value *arg2,
+ gdb_byte *x, int *len_x, gdb_byte *y, int *len_y)
+{
+ struct type *type1, *type2;
+
+ type1 = check_typedef (value_type (arg1));
+ type2 = check_typedef (value_type (arg2));
+
+ /* At least one of the arguments must be of decimal float type. */
+ gdb_assert (TYPE_CODE (type1) == TYPE_CODE_DECFLOAT
+ || TYPE_CODE (type2) == TYPE_CODE_DECFLOAT);
+
+ if (TYPE_CODE (type1) == TYPE_CODE_FLT
+ || TYPE_CODE (type2) == TYPE_CODE_FLT)
+ /* The DFP extension to the C language does not allow mixing of
+ * decimal float types with other float types in expressions
+ * (see WDTR 24732, page 12). */
+ error (_("Mixing decimal floating types with other floating types is not allowed."));
+
+ /* Obtain decimal value of arg1, converting from other types
+ if necessary. */
+
+ if (TYPE_CODE (type1) == TYPE_CODE_DECFLOAT)
+ {
+ *len_x = TYPE_LENGTH (type1);
+ memcpy (x, value_contents (arg1), *len_x);
+ }
+ else if (is_integral_type (type1))
+ {
+ *len_x = TYPE_LENGTH (type2);
+ decimal_from_integral (arg1, x, *len_x);
+ }
+ else
+ error (_("Don't know how to convert to decimal floating type."));
+
+ /* Obtain decimal value of arg2, converting from other types
+ if necessary. */
+
+ if (TYPE_CODE (type2) == TYPE_CODE_DECFLOAT)
+ {
+ *len_y = TYPE_LENGTH (type2);
+ memcpy (y, value_contents (arg2), *len_y);
+ }
+ else if (is_integral_type (type2))
+ {
+ *len_y = TYPE_LENGTH (type1);
+ decimal_from_integral (arg2, y, *len_y);
+ }
+ else
+ error (_("Don't know how to convert to decimal floating type."));
+}
/* Perform a binary operation on two operands which have reasonable
representations as integers or floats. This includes booleans,
@@ -759,14 +814,55 @@ value_binop (struct value *arg1, struct
type1 = check_typedef (value_type (arg1));
type2 = check_typedef (value_type (arg2));
- if ((TYPE_CODE (type1) != TYPE_CODE_FLT && !is_integral_type (type1))
+ if ((TYPE_CODE (type1) != TYPE_CODE_FLT
+ && TYPE_CODE (type1) != TYPE_CODE_DECFLOAT && !is_integral_type (type1))
||
- (TYPE_CODE (type2) != TYPE_CODE_FLT && !is_integral_type (type2)))
+ (TYPE_CODE (type2) != TYPE_CODE_FLT
+ && TYPE_CODE (type2) != TYPE_CODE_DECFLOAT && !is_integral_type (type2)))
error (_("Argument to arithmetic operation not a number or boolean."));
- if (TYPE_CODE (type1) == TYPE_CODE_FLT
+ if (TYPE_CODE (type1) == TYPE_CODE_DECFLOAT
||
- TYPE_CODE (type2) == TYPE_CODE_FLT)
+ TYPE_CODE (type2) == TYPE_CODE_DECFLOAT)
+ {
+ struct type *v_type;
+ int len_v1, len_v2, len_v;
+ gdb_byte v1[16], v2[16];
+ gdb_byte v[16];
+
+ value_args_as_decimal (arg1, arg2, v1, &len_v1, v2, &len_v2);
+
+ switch (op)
+ {
+ case BINOP_ADD:
+ case BINOP_SUB:
+ case BINOP_MUL:
+ case BINOP_DIV:
+ case BINOP_EXP:
+ decimal_binop (op, v1, len_v1, v2, len_v2, v, &len_v);
+ break;
+
+ default:
+ error (_("Integer-only operation on floating point number."));
+ }
+
+ if (TYPE_CODE (type1) != TYPE_CODE_DECFLOAT)
+ /* If arg1 is not a decimal float, the type of the result is the type
+ of the decimal float argument, arg2. */
+ v_type = type2;
+ else if (TYPE_CODE (type2) != TYPE_CODE_DECFLOAT)
+ /* Same logic, for the case where arg2 is not a decimal float. */
+ v_type = type1;
+ else
+ /* len_v is equal either to len_v1 or to len_v2. the type of the
+ result is the type of the argument with the same length as v. */
+ v_type = (len_v == len_v1)? type1 : type2;
+
+ val = value_from_decfloat (v_type, v);
+ }
+ else if (TYPE_CODE (type1) == TYPE_CODE_FLT
+ ||
+ TYPE_CODE (type2) == TYPE_CODE_FLT)
{
/* FIXME-if-picky-about-floating-accuracy: Should be doing this
in target format. real.c in GCC probably has the necessary
@@ -1177,6 +1273,8 @@ value_logical_not (struct value *arg1)
if (TYPE_CODE (type1) == TYPE_CODE_FLT)
return 0 == value_as_double (arg1);
+ else if (TYPE_CODE (type1) == TYPE_CODE_DECFLOAT)
+ return decimal_is_zero (value_contents (arg1), TYPE_LENGTH (type1));
len = TYPE_LENGTH (type1);
p = value_contents (arg1);
@@ -1255,6 +1353,16 @@ value_equal (struct value *arg1, struct
DOUBLEST d = value_as_double (arg1);
return d == value_as_double (arg2);
}
+ else if ((code1 == TYPE_CODE_DECFLOAT || is_int1)
+ && (code2 == TYPE_CODE_DECFLOAT || is_int2))
+ {
+ gdb_byte v1[16], v2[16];
+ int len_v1, len_v2;
+
+ value_args_as_decimal (arg1, arg2, v1, &len_v1, v2, &len_v2);
+
+ return decimal_compare (v1, len_v1, v2, len_v2) == 0;
+ }
/* FIXME: Need to promote to either CORE_ADDR or LONGEST, whichever
is bigger. */
@@ -1319,6 +1427,16 @@ value_less (struct value *arg1, struct v
DOUBLEST d = value_as_double (arg1);
return d < value_as_double (arg2);
}
+ else if ((code1 == TYPE_CODE_DECFLOAT || is_int1)
+ && (code2 == TYPE_CODE_DECFLOAT || is_int2))
+ {
+ gdb_byte v1[16], v2[16];
+ int len_v1, len_v2;
+
+ value_args_as_decimal (arg1, arg2, v1, &len_v1, v2, &len_v2);
+
+ return decimal_compare (v1, len_v1, v2, len_v2) == -1;
+ }
else if (code1 == TYPE_CODE_PTR && code2 == TYPE_CODE_PTR)
return value_as_address (arg1) < value_as_address (arg2);
@@ -1350,6 +1468,8 @@ value_pos (struct value *arg1)
if (TYPE_CODE (type) == TYPE_CODE_FLT)
return value_from_double (type, value_as_double (arg1));
+ else if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
+ return value_from_decfloat (type, value_contents (arg1));
else if (is_integral_type (type))
{
/* Perform integral promotion for ANSI C/C++. FIXME: What about
@@ -1382,7 +1502,7 @@ value_neg (struct value *arg1)
int len = TYPE_LENGTH (type);
gdb_byte decbytes[16]; /* a decfloat is at most 128 bits long */
- memcpy(decbytes, value_contents(arg1), len);
+ memcpy (decbytes, value_contents (arg1), len);
if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_LITTLE)
decbytes[len-1] = decbytes[len - 1] | 0x80;
Index: src-git/gdb/valops.c
===================================================================
--- src-git.orig/gdb/valops.c 2007-12-19 16:34:35.000000000 -0200
+++ src-git/gdb/valops.c 2007-12-19 19:54:50.000000000 -0200
@@ -36,6 +36,7 @@
#include "infcall.h"
#include "dictionary.h"
#include "cp-support.h"
+#include "dfp.h"
#include <errno.h>
#include "gdb_string.h"
@@ -338,7 +339,8 @@ value_cast (struct type *type, struct va
code2 = TYPE_CODE_INT;
scalar = (code2 == TYPE_CODE_INT || code2 == TYPE_CODE_FLT
- || code2 == TYPE_CODE_ENUM || code2 == TYPE_CODE_RANGE);
+ || code2 == TYPE_CODE_DECFLOAT || code2 == TYPE_CODE_ENUM
+ || code2 == TYPE_CODE_RANGE);
if (code1 == TYPE_CODE_STRUCT
&& code2 == TYPE_CODE_STRUCT
@@ -357,6 +359,22 @@ value_cast (struct type *type, struct va
}
if (code1 == TYPE_CODE_FLT && scalar)
return value_from_double (type, value_as_double (arg2));
+ else if (code1 == TYPE_CODE_DECFLOAT && scalar)
+ {
+ int dec_len = TYPE_LENGTH (type);
+ gdb_byte dec[16];
+
+ if (code2 == TYPE_CODE_FLT)
+ decimal_from_floating (arg2, dec, dec_len);
+ else if (code2 == TYPE_CODE_DECFLOAT)
+ decimal_convert (value_contents (arg2), TYPE_LENGTH (type2),
+ dec, dec_len);
+ else
+ /* The only option left is an integral type. */
+ decimal_from_integral (arg2, dec, dec_len);
+
+ return value_from_decfloat (type, dec);
+ }
else if ((code1 == TYPE_CODE_INT || code1 == TYPE_CODE_ENUM
|| code1 == TYPE_CODE_RANGE)
&& (scalar || code2 == TYPE_CODE_PTR
Index: src-git/gdb/value.c
===================================================================
--- src-git.orig/gdb/value.c 2007-12-19 16:34:35.000000000 -0200
+++ src-git/gdb/value.c 2007-12-19 17:57:08.000000000 -0200
@@ -982,6 +982,7 @@ value_as_double (struct value *val)
error (_("Invalid floating value found in program."));
return foo;
}
+
/* Extract a value as a C pointer. Does not deallocate the value.
Note that val's type may not actually be a pointer; value_as_long
handles all the cases. */
@@ -1127,6 +1128,11 @@ unpack_long (struct type *type, const gd
case TYPE_CODE_FLT:
return extract_typed_floating (valaddr, type);
+ case TYPE_CODE_DECFLOAT:
+ /* libdecnumber has a function to convert from decimal to integer, but
+ it doesn't work when the decimal number has a fractional part. */
+ return decimal_to_double (valaddr, len);
+
case TYPE_CODE_PTR:
case TYPE_CODE_REF:
/* Assume a CORE_ADDR can fit in a LONGEST (for now). Not sure
@@ -1184,6 +1190,8 @@ unpack_double (struct type *type, const
return extract_typed_floating (valaddr, type);
}
+ else if (code == TYPE_CODE_DECFLOAT)
+ return decimal_to_double (valaddr, len);
else if (nosign)
{
/* Unsigned -- be sure we compensate for signed LONGEST. */
@@ -1643,23 +1651,12 @@ value_from_double (struct type *type, DO
}
struct value *
-value_from_decfloat (struct type *expect_type, struct type *type,
- gdb_byte decbytes[16])
+value_from_decfloat (struct type *type, const gdb_byte *dec)
{
struct value *val = allocate_value (type);
- int len = TYPE_LENGTH (type);
- if (expect_type)
- {
- int expect_len = TYPE_LENGTH (expect_type);
- char decstr[MAX_DECIMAL_STRING];
- int real_len;
-
- decimal_to_string (decbytes, len, decstr);
- decimal_from_string (decbytes, expect_len, decstr);
- }
+ memcpy (value_contents_raw (val), dec, TYPE_LENGTH (type));
- memcpy (value_contents_raw (val), decbytes, len);
return val;
}
Index: src-git/gdb/value.h
===================================================================
--- src-git.orig/gdb/value.h 2007-12-19 16:34:35.000000000 -0200
+++ src-git/gdb/value.h 2007-12-19 16:55:05.000000000 -0200
@@ -280,9 +280,8 @@ extern void pack_long (gdb_byte *buf, st
extern struct value *value_from_longest (struct type *type, LONGEST num);
extern struct value *value_from_pointer (struct type *type, CORE_ADDR addr);
extern struct value *value_from_double (struct type *type, DOUBLEST num);
-extern struct value *value_from_decfloat (struct type *expect_type,
- struct type *type,
- gdb_byte decbytes[16]);
+extern struct value *value_from_decfloat (struct type *type,
+ const gdb_byte *decbytes);
extern struct value *value_from_string (char *string);
extern struct value *value_at (struct type *type, CORE_ADDR addr);
--
--
[]'s
Thiago Jung Bauermann
Software Engineer
IBM Linux Technology Center