From 0a12719e51c456a5220f75950ebf9c07457c753b Mon Sep 17 00:00:00 2001 From: Joel Brobecker Date: Sun, 15 Nov 2020 03:17:12 -0500 Subject: [PATCH] Add support for fixed-point type arithmetic This patch adds support for binary operations on fixed-point values, as well as for the negative unary operator. gdb/ChangeLog: * eval.c (binop_promote): Add fixed-point type handling. * valarith.c (fixed_point_binop): New function. (scalar_binop): Add fixed-point type handling. (value_neg): Add fixed-point type handling. * valops.c (value_cast_to_fixed_point): New function. (value_cast): Add fixed-point type handling. gdb/testsuite/ChangeLog: * gdb.dwarf2/dw2-fixed-point.exp: Add arithmetic tests. --- gdb/ChangeLog | 9 ++ gdb/eval.c | 3 + gdb/testsuite/ChangeLog | 4 + gdb/testsuite/gdb.dwarf2/dw2-fixed-point.exp | 18 ++++ gdb/valarith.c | 91 +++++++++++++++++++- gdb/valops.c | 74 +++++++++++++++- 6 files changed, 196 insertions(+), 3 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 56e54d3efad..e9f0afd9192 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,12 @@ +2020-11-15 Joel Brobecker + + * eval.c (binop_promote): Add fixed-point type handling. + * valarith.c (fixed_point_binop): New function. + (scalar_binop): Add fixed-point type handling. + (value_neg): Add fixed-point type handling. + * valops.c (value_cast_to_fixed_point): New function. + (value_cast): Add fixed-point type handling. + 2020-11-15 Joel Brobecker * ada-typeprint.c (ada_print_type): Add handing of fixed-point diff --git a/gdb/eval.c b/gdb/eval.c index 8d0c5747f39..308f4771482 100644 --- a/gdb/eval.c +++ b/gdb/eval.c @@ -430,6 +430,9 @@ binop_promote (const struct language_defn *language, struct gdbarch *gdbarch, && !is_integral_type (type2))) return; + if (is_fixed_point_type (type1) || is_fixed_point_type (type2)) + return; + if (type1->code () == TYPE_CODE_DECFLOAT || type2->code () == TYPE_CODE_DECFLOAT) { diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 2c7b5c5a522..3c53f6c666a 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2020-11-15 Joel Brobecker + + * gdb.dwarf2/dw2-fixed-point.exp: Add arithmetic tests. + 2020-11-15 Joel Brobecker * gdb.ada/fixed_points.exp: Add ptype tests. diff --git a/gdb/testsuite/gdb.dwarf2/dw2-fixed-point.exp b/gdb/testsuite/gdb.dwarf2/dw2-fixed-point.exp index 33a7e7485cb..0252195e1d4 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-fixed-point.exp +++ b/gdb/testsuite/gdb.dwarf2/dw2-fixed-point.exp @@ -143,6 +143,24 @@ gdb_test "print pck.fp1_range_var" \ gdb_test "print /x pck.fp1_range_var" \ " = 0x1" +gdb_test "print pck.fp1_var + 0.25" \ + " = 0.5" + +gdb_test "print pck.fp2_var - pck.fp2_var" \ + " = 0" + +gdb_test "print pck.fp3_var * 1" \ + " = 0.1" + +gdb_test "print pck.fp3_var / pck.fp3_var" \ + " = 1" + +gdb_test "print pck.fp1_range_var - 0.5" \ + " = 0.5" + +gdb_test "print -pck.fp1_var" \ + " = -0.25" + # Set the language to LANG and do a ptype test on pck__fp1_var, # pck__fp2_var and pck__fp3_var, verifying that the output matches # FP1_RE, FP2_RE, FP2_RE (resp.). diff --git a/gdb/valarith.c b/gdb/valarith.c index f6caf3d1804..65a6f13db5e 100644 --- a/gdb/valarith.c +++ b/gdb/valarith.c @@ -881,6 +881,84 @@ value_args_as_target_float (struct value *arg1, struct value *arg2, type2->name ()); } +/* Assuming at last one of ARG1 or ARG2 is a fixed point value, + perform the binary operation OP on these two operands, and return + the resulting value (also as a fixed point). */ + +static struct value * +fixed_point_binop (struct value *arg1, struct value *arg2, enum exp_opcode op) +{ + struct type *type1 = check_typedef (value_type (arg1)); + struct type *type2 = check_typedef (value_type (arg2)); + + struct value *val; + + gdb_assert (is_fixed_point_type (type1) || is_fixed_point_type (type2)); + if (!is_fixed_point_type (type1)) + { + arg1 = value_cast (type2, arg1); + type1 = type2; + } + if (!is_fixed_point_type (type2)) + { + arg2 = value_cast (type1, arg2); + type2 = type1; + } + + gdb_mpq v1, v2, res; + v1.read_fixed_point (value_contents (arg1), TYPE_LENGTH (type1), + type_byte_order (type1), type1->is_unsigned (), + fixed_point_scaling_factor (type1)); + v2.read_fixed_point (value_contents (arg2), TYPE_LENGTH (type2), + type_byte_order (type2), type2->is_unsigned (), + fixed_point_scaling_factor (type2)); + +#define INIT_VAL_WITH_FIXED_POINT_VAL(RESULT) \ + do { \ + val = allocate_value (type1); \ + (RESULT).write_fixed_point \ + (value_contents_raw (val), TYPE_LENGTH (type1), \ + type_byte_order (type1), type1->is_unsigned (), \ + fixed_point_scaling_factor (type1)); \ + } while (0) + + switch (op) + { + case BINOP_ADD: + mpq_add (res.val, v1.val, v2.val); + INIT_VAL_WITH_FIXED_POINT_VAL (res); + break; + + case BINOP_SUB: + mpq_sub (res.val, v1.val, v2.val); + INIT_VAL_WITH_FIXED_POINT_VAL (res); + break; + + case BINOP_MIN: + INIT_VAL_WITH_FIXED_POINT_VAL (mpq_cmp (v1.val, v2.val) < 0 ? v1 : v2); + break; + + case BINOP_MAX: + INIT_VAL_WITH_FIXED_POINT_VAL (mpq_cmp (v1.val, v2.val) > 0 ? v1 : v2); + break; + + case BINOP_MUL: + mpq_mul (res.val, v1.val, v2.val); + INIT_VAL_WITH_FIXED_POINT_VAL (res); + break; + + case BINOP_DIV: + mpq_div (res.val, v1.val, v2.val); + INIT_VAL_WITH_FIXED_POINT_VAL (res); + break; + + default: + error (_("Integer-only operation on fixed point number.")); + } + + return val; +} + /* A helper function that finds the type to use for a binary operation involving TYPE1 and TYPE2. */ @@ -1054,10 +1132,17 @@ scalar_binop (struct value *arg1, struct value *arg2, enum exp_opcode op) || type2->code () == TYPE_CODE_COMPLEX) return complex_binop (arg1, arg2, op); - if ((!is_floating_value (arg1) && !is_integral_type (type1)) - || (!is_floating_value (arg2) && !is_integral_type (type2))) + if ((!is_floating_value (arg1) + && !is_integral_type (type1) + && !is_fixed_point_type (type1)) + || (!is_floating_value (arg2) + && !is_integral_type (type2) + && !is_fixed_point_type (type2))) error (_("Argument to arithmetic operation not a number or boolean.")); + if (is_fixed_point_type (type1) || is_fixed_point_type (type2)) + return fixed_point_binop (arg1, arg2, op); + if (is_floating_type (type1) || is_floating_type (type2)) { result_type = promotion_type (type1, type2); @@ -1753,6 +1838,8 @@ value_neg (struct value *arg1) if (is_integral_type (type) || is_floating_type (type)) return value_binop (value_from_longest (type, 0), arg1, BINOP_SUB); + else if (is_fixed_point_type (type)) + return value_binop (value_zero (type, not_lval), arg1, BINOP_SUB); else if (type->code () == TYPE_CODE_ARRAY && type->is_vector ()) { struct value *tmp, *val = allocate_value (type); diff --git a/gdb/valops.c b/gdb/valops.c index 4df2538f16d..0f84a70ceb6 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -331,6 +331,60 @@ value_cast_pointers (struct type *type, struct value *arg2, return arg2; } +/* Assuming that TO_TYPE is a fixed point type, return a value + corresponding to the cast of FROM_VAL to that type. */ + +static struct value * +value_cast_to_fixed_point (struct type *to_type, struct value *from_val) +{ + struct type *from_type = value_type (from_val); + + if (from_type == to_type) + return from_val; + + gdb_mpq vq; + + /* Extract the value as a rational number. */ + + if (is_floating_type (from_type)) + { + double d = target_float_to_host_double (value_contents (from_val), + from_type); + mpq_set_d (vq.val, d); + } + + else if (is_integral_type (from_type) || is_fixed_point_type (from_type)) + { + gdb_mpz vz; + + vz.read (value_contents (from_val), TYPE_LENGTH (from_type), + type_byte_order (from_type), from_type->is_unsigned ()); + mpq_set_z (vq.val, vz.val); + + if (is_fixed_point_type (from_type)) + mpq_mul (vq.val, vq.val, fixed_point_scaling_factor (from_type).val); + } + + else + error (_("Invalid conversion from type %s to fixed point type %s"), + from_type->name (), to_type->name ()); + + /* Divide that value by the scaling factor to obtain the unscaled + value, first in rational form, and then in integer form. */ + + mpq_div (vq.val, vq.val, fixed_point_scaling_factor (to_type).val); + gdb_mpz unscaled = vq.get_rounded (); + + /* Finally, create the result value, and pack the unscaled value + in it. */ + struct value *result = allocate_value (to_type); + unscaled.write (value_contents_raw (result), + TYPE_LENGTH (to_type), type_byte_order (to_type), + to_type->is_unsigned ()); + + return result; +} + /* Cast value ARG2 to type TYPE and return as a value. More general than a C cast: accepts any two types of the same length, and if ARG2 is an lvalue it can be cast into anything at all. */ @@ -349,6 +403,9 @@ value_cast (struct type *type, struct value *arg2) if (value_type (arg2) == type) return arg2; + if (is_fixed_point_type (type)) + return value_cast_to_fixed_point (type, arg2); + /* Check if we are casting struct reference to struct reference. */ if (TYPE_IS_REFERENCE (check_typedef (type))) { @@ -439,7 +496,8 @@ value_cast (struct type *type, struct value *arg2) scalar = (code2 == TYPE_CODE_INT || code2 == TYPE_CODE_FLT || code2 == TYPE_CODE_DECFLOAT || code2 == TYPE_CODE_ENUM - || code2 == TYPE_CODE_RANGE); + || code2 == TYPE_CODE_RANGE + || is_fixed_point_type (type2)); if ((code1 == TYPE_CODE_STRUCT || code1 == TYPE_CODE_UNION) && (code2 == TYPE_CODE_STRUCT || code2 == TYPE_CODE_UNION) @@ -460,6 +518,20 @@ value_cast (struct type *type, struct value *arg2) value_contents_raw (v), type); return v; } + else if (is_fixed_point_type (type2)) + { + gdb_mpq fp_val; + + fp_val.read_fixed_point + (value_contents (arg2), TYPE_LENGTH (type2), + type_byte_order (type2), type2->is_unsigned (), + fixed_point_scaling_factor (type2)); + + struct value *v = allocate_value (to_type); + target_float_from_host_double (value_contents_raw (v), + to_type, mpq_get_d (fp_val.val)); + return v; + } /* The only option left is an integral type. */ if (type2->is_unsigned ()) -- 2.30.2