From 303a881f8789733248f27af0c872d356a34be009 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Wed, 1 Mar 2023 15:13:21 -0700 Subject: Use gdb_gmp for scalar arithmetic This changes gdb to use scalar arithmetic for expression evaluation. I suspect this patch is not truly complete, as there may be code paths that still don't correctly handle 128-bit integers. However, many things do work now. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30190 --- gdb/NEWS | 2 + gdb/testsuite/gdb.rust/onetwoeight.exp | 65 +++++ gdb/testsuite/gdb.rust/onetwoeight.rs | 31 +++ gdb/utils.c | 30 --- gdb/utils.h | 7 - gdb/valarith.c | 465 ++++++++++----------------------- gdb/valops.c | 14 +- 7 files changed, 247 insertions(+), 367 deletions(-) create mode 100644 gdb/testsuite/gdb.rust/onetwoeight.exp create mode 100644 gdb/testsuite/gdb.rust/onetwoeight.rs diff --git a/gdb/NEWS b/gdb/NEWS index cc262f1..d729aa2 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -3,6 +3,8 @@ *** Changes since GDB 13 +* GDB now has some support for integer types larger than 64 bits. + * Removed targets and native configurations GDB no longer supports AIX 4.x, AIX 5.x and AIX 6.x. The minimum supported diff --git a/gdb/testsuite/gdb.rust/onetwoeight.exp b/gdb/testsuite/gdb.rust/onetwoeight.exp new file mode 100644 index 0000000..317c848 --- /dev/null +++ b/gdb/testsuite/gdb.rust/onetwoeight.exp @@ -0,0 +1,65 @@ +# Copyright (C) 2023 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Test expression parsing and evaluation that requires Rust compiler. + +load_lib rust-support.exp +require allow_rust_tests + +set v [split [rust_compiler_version] .] +if {[lindex $v 0] == 1 && [lindex $v 1] < 43} { + untested "128-bit ints require rust 1.43 or greater" + return -1 +} + +standard_testfile .rs +if {[prepare_for_testing "failed to prepare" $testfile $srcfile {debug rust}]} { + return -1 +} + +set line [gdb_get_line_number "BREAK"] +if {![runto ${srcfile}:$line]} { + untested "could not run to breakpoint" + return -1 +} + +gdb_test "print x" " = 340282366920938463463374607431768211455" +gdb_test "print y" " = 170141183460469231731687303715884105727" + +gdb_test "print x / 2" " = 170141183460469231731687303715884105727" +gdb_test "print sm * 2" " = 170141183460469231731687303715884105726" +gdb_test "print sm + sm" " = 170141183460469231731687303715884105726" +gdb_test "print x - y" " = 170141183460469231731687303715884105728" +gdb_test "print -y" " = -170141183460469231731687303715884105727" +gdb_test "print +y" " = 170141183460469231731687303715884105727" + +gdb_test "print/x x" " = 0xffffffffffffffffffffffffffffffff" +gdb_test "print x % 4" " = 3" +gdb_test "print !x" " = 0" + +gdb_test "print x < 0" " = false" +gdb_test "print -y < 0" " = true" +gdb_test "print x > y" " = true" +gdb_test "print y >= y" " = true" +gdb_test "print y <= y" " = true" +gdb_test "print y == y" " = true" +gdb_test "print x != y" " = true" + +gdb_test "print sm << 2" "= 340282366920938463463374607431768211452" +gdb_test "print x >> 2" "= 85070591730234615865843651857942052863" + +gdb_test "print/x x & mask" " = 0xf0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0" +gdb_test "print/x x ^ mask" " = 0xf0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f" +gdb_test "print/x mask | (mask >> 4)" " = 0xffffffffffffffffffffffffffffffff" diff --git a/gdb/testsuite/gdb.rust/onetwoeight.rs b/gdb/testsuite/gdb.rust/onetwoeight.rs new file mode 100644 index 0000000..aa8cb1c --- /dev/null +++ b/gdb/testsuite/gdb.rust/onetwoeight.rs @@ -0,0 +1,31 @@ +// Copyright (C) 2023 Free Software Foundation, Inc. + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#![allow(dead_code)] +#![allow(unused_variables)] +#![allow(unused_assignments)] + + +fn empty() { +} + +fn main () { + let x : u128 = 340_282_366_920_938_463_463_374_607_431_768_211_455; + let sm : u128 = x /4; + let y : i128 = 170_141_183_460_469_231_731_687_303_715_884_105_727; + let mask : u128 = 0xf0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0; + + empty(); // BREAK +} diff --git a/gdb/utils.c b/gdb/utils.c index 8928295..4c7d665 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -723,36 +723,6 @@ myread (int desc, char *addr, int len) return orglen; } -/* See utils.h. */ - -ULONGEST -uinteger_pow (ULONGEST v1, LONGEST v2) -{ - if (v2 < 0) - { - if (v1 == 0) - error (_("Attempt to raise 0 to negative power.")); - else - return 0; - } - else - { - /* The Russian Peasant's Algorithm. */ - ULONGEST v; - - v = 1; - for (;;) - { - if (v2 & 1L) - v *= v1; - v2 >>= 1; - if (v2 == 0) - return v; - v1 *= v1; - } - } -} - /* An RAII class that sets up to handle input and then tears down diff --git a/gdb/utils.h b/gdb/utils.h index 6e240dec..a383036 100644 --- a/gdb/utils.h +++ b/gdb/utils.h @@ -303,13 +303,6 @@ extern pid_t wait_to_die_with_timeout (pid_t pid, int *status, int timeout); extern int myread (int, char *, int); -/* Integer exponentiation: Return V1**V2, where both arguments - are integers. - - Requires V1 != 0 if V2 < 0. - Returns 1 for 0 ** 0. */ -extern ULONGEST uinteger_pow (ULONGEST v1, LONGEST v2); - /* Resource limits used by getrlimit and setrlimit. */ enum resource_limit_kind diff --git a/gdb/valarith.c b/gdb/valarith.c index 25d72b7..637047f 100644 --- a/gdb/valarith.c +++ b/gdb/valarith.c @@ -34,13 +34,6 @@ static struct value *value_subscripted_rvalue (struct value *array, LONGEST index, LONGEST lowerbound); -/* Define whether or not the C operator '/' truncates towards zero for - differently signed operands (truncation direction is undefined in C). */ - -#ifndef TRUNCATION_TOWARDS_ZERO -#define TRUNCATION_TOWARDS_ZERO ((-5 / 2) == -2) -#endif - /* Given a pointer, return the size of its target. If the pointer type is void *, then return 1. If the target type is incomplete, then error out. @@ -726,36 +719,6 @@ value_concat (struct value *arg1, struct value *arg2) return result; } -/* Integer exponentiation: V1**V2, where both arguments are - integers. Requires V1 != 0 if V2 < 0. Returns 1 for 0 ** 0. */ - -static LONGEST -integer_pow (LONGEST v1, LONGEST v2) -{ - if (v2 < 0) - { - if (v1 == 0) - error (_("Attempt to raise 0 to negative power.")); - else - return 0; - } - else - { - /* The Russian Peasant's Algorithm. */ - LONGEST v; - - v = 1; - for (;;) - { - if (v2 & 1L) - v *= v1; - v2 >>= 1; - if (v2 == 0) - return v; - v1 *= v1; - } - } -} /* Obtain argument values for binary operation, converting from other types if one of them is not floating point. */ @@ -1099,33 +1062,39 @@ type_length_bits (type *type) both negative and too-large shift amounts, which are undefined, and would crash a GDB built with UBSan. Depending on the current language, if the shift is not valid, this either warns and returns - false, or errors out. Returns true if valid. */ + false, or errors out. Returns true and sets NBITS if valid. */ static bool check_valid_shift_count (enum exp_opcode op, type *result_type, - type *shift_count_type, ULONGEST shift_count) + type *shift_count_type, const gdb_mpz &shift_count, + unsigned long &nbits) { - if (!shift_count_type->is_unsigned () && (LONGEST) shift_count < 0) + if (!shift_count_type->is_unsigned ()) { - auto error_or_warning = [] (const char *msg) - { - /* Shifts by a negative amount are always an error in Go. Other - languages are more permissive and their compilers just warn or - have modes to disable the errors. */ - if (current_language->la_language == language_go) - error (("%s"), msg); - else - warning (("%s"), msg); - }; + LONGEST count = shift_count.as_integer (); + if (count < 0) + { + auto error_or_warning = [] (const char *msg) + { + /* Shifts by a negative amount are always an error in Go. Other + languages are more permissive and their compilers just warn or + have modes to disable the errors. */ + if (current_language->la_language == language_go) + error (("%s"), msg); + else + warning (("%s"), msg); + }; - if (op == BINOP_RSH) - error_or_warning (_("right shift count is negative")); - else - error_or_warning (_("left shift count is negative")); - return false; + if (op == BINOP_RSH) + error_or_warning (_("right shift count is negative")); + else + error_or_warning (_("left shift count is negative")); + return false; + } } - if (shift_count >= type_length_bits (result_type)) + nbits = shift_count.as_integer (); + if (nbits >= type_length_bits (result_type)) { /* In Go, shifting by large amounts is defined. Be silent and still return false, as the caller's error path does the right @@ -1249,283 +1218,127 @@ scalar_binop (struct value *arg1, struct value *arg2, enum exp_opcode op) else result_type = promotion_type (type1, type2); - if (result_type->is_unsigned ()) + gdb_mpz v1 = value_as_mpz (arg1); + gdb_mpz v2 = value_as_mpz (arg2); + gdb_mpz v; + + switch (op) { - LONGEST v2_signed = value_as_long (arg2); - ULONGEST v1, v2, v = 0; + case BINOP_ADD: + v = v1 + v2; + break; - v1 = (ULONGEST) value_as_long (arg1); - v2 = (ULONGEST) v2_signed; + case BINOP_SUB: + v = v1 - v2; + break; - switch (op) - { - case BINOP_ADD: - v = v1 + v2; - break; - - case BINOP_SUB: - v = v1 - v2; - break; - - case BINOP_MUL: - v = v1 * v2; - break; - - case BINOP_DIV: - case BINOP_INTDIV: - if (v2 != 0) - v = v1 / v2; - else - error (_("Division by zero")); - break; - - case BINOP_EXP: - v = uinteger_pow (v1, v2_signed); - break; - - case BINOP_REM: - if (v2 != 0) - v = v1 % v2; - else - error (_("Division by zero")); - break; - - case BINOP_MOD: - /* Knuth 1.2.4, integer only. Note that unlike the C '%' op, - v1 mod 0 has a defined value, v1. */ - if (v2 == 0) - { - v = v1; - } - else - { - v = v1 / v2; - /* Note floor(v1/v2) == v1/v2 for unsigned. */ - v = v1 - (v2 * v); - } - break; - - case BINOP_LSH: - if (!check_valid_shift_count (op, result_type, type2, v2)) - v = 0; - else - v = v1 << v2; - break; - - case BINOP_RSH: - if (!check_valid_shift_count (op, result_type, type2, v2)) - v = 0; - else - v = v1 >> v2; - break; - - case BINOP_BITWISE_AND: - v = v1 & v2; - break; - - case BINOP_BITWISE_IOR: - v = v1 | v2; - break; - - case BINOP_BITWISE_XOR: - v = v1 ^ v2; - break; - - case BINOP_MIN: - v = v1 < v2 ? v1 : v2; - break; - - case BINOP_MAX: - v = v1 > v2 ? v1 : v2; - break; - - case BINOP_EQUAL: - v = v1 == v2; - break; - - case BINOP_NOTEQUAL: - v = v1 != v2; - break; - - case BINOP_LESS: - v = v1 < v2; - break; - - case BINOP_GTR: - v = v1 > v2; - break; - - case BINOP_LEQ: - v = v1 <= v2; - break; - - case BINOP_GEQ: - v = v1 >= v2; - break; - - default: - error (_("Invalid binary operation on numbers.")); - } + case BINOP_MUL: + v = v1 * v2; + break; - val = value::allocate (result_type); - store_unsigned_integer (val->contents_raw ().data (), - val->type ()->length (), - type_byte_order (result_type), - v); - } - else - { - LONGEST v1, v2, v = 0; + case BINOP_DIV: + case BINOP_INTDIV: + if (v2.sgn () != 0) + v = v1 / v2; + else + error (_("Division by zero")); + break; - v1 = value_as_long (arg1); - v2 = value_as_long (arg2); + case BINOP_EXP: + v = v1.pow (v2.as_integer ()); + break; - switch (op) + case BINOP_REM: + if (v2.sgn () != 0) + v = v1 % v2; + else + error (_("Division by zero")); + break; + + case BINOP_MOD: + /* Knuth 1.2.4, integer only. Note that unlike the C '%' op, + v1 mod 0 has a defined value, v1. */ + if (v2.sgn () == 0) + { + v = v1; + } + else { - case BINOP_ADD: - v = v1 + v2; - break; - - case BINOP_SUB: - /* Avoid runtime error: signed integer overflow: \ - 0 - -9223372036854775808 cannot be represented in type - 'long int'. */ - v = (ULONGEST)v1 - (ULONGEST)v2; - break; - - case BINOP_MUL: - v = v1 * v2; - break; - - case BINOP_DIV: - case BINOP_INTDIV: - if (v2 != 0) - v = v1 / v2; - else - error (_("Division by zero")); - break; - - case BINOP_EXP: - v = integer_pow (v1, v2); - break; - - case BINOP_REM: - if (v2 != 0) - v = v1 % v2; - else - error (_("Division by zero")); - break; - - case BINOP_MOD: - /* Knuth 1.2.4, integer only. Note that unlike the C '%' op, - X mod 0 has a defined value, X. */ - if (v2 == 0) - { - v = v1; - } - else - { - v = v1 / v2; - /* Compute floor. */ - if (TRUNCATION_TOWARDS_ZERO && (v < 0) && ((v1 % v2) != 0)) - { - v--; - } - v = v1 - (v2 * v); - } - break; - - case BINOP_LSH: - if (!check_valid_shift_count (op, result_type, type2, v2)) - v = 0; - else - { - /* Cast to unsigned to avoid undefined behavior on - signed shift overflow (unless C++20 or later), - which would crash GDB when built with UBSan. - Note we don't warn on left signed shift overflow, - because starting with C++20, that is actually - defined behavior. Also, note GDB assumes 2's - complement throughout. */ - v = (ULONGEST) v1 << v2; - } - break; - - case BINOP_RSH: - if (!check_valid_shift_count (op, result_type, type2, v2)) - { - /* Pretend the too-large shift was decomposed in a - number of smaller shifts. An arithmetic signed - right shift of a negative number always yields -1 - with such semantics. This is the right thing to - do for Go, and we might as well do it for - languages where it is undefined. Also, pretend a - shift by a negative number was a shift by the - negative number cast to unsigned, which is the - same as shifting by a too-large number. */ - if (v1 < 0) - v = -1; - else - v = 0; - } - else - v = v1 >> v2; - break; - - case BINOP_BITWISE_AND: - v = v1 & v2; - break; - - case BINOP_BITWISE_IOR: - v = v1 | v2; - break; - - case BINOP_BITWISE_XOR: - v = v1 ^ v2; - break; - - case BINOP_MIN: - v = v1 < v2 ? v1 : v2; - break; - - case BINOP_MAX: - v = v1 > v2 ? v1 : v2; - break; - - case BINOP_EQUAL: - v = v1 == v2; - break; - - case BINOP_NOTEQUAL: - v = v1 != v2; - break; - - case BINOP_LESS: - v = v1 < v2; - break; - - case BINOP_GTR: - v = v1 > v2; - break; - - case BINOP_LEQ: - v = v1 <= v2; - break; - - case BINOP_GEQ: - v = v1 >= v2; - break; - - default: - error (_("Invalid binary operation on numbers.")); + v = v1 / v2; + /* Note floor(v1/v2) == v1/v2 for unsigned. */ + v = v1 - (v2 * v); } + break; + + case BINOP_LSH: + { + unsigned long nbits; + if (!check_valid_shift_count (op, result_type, type2, v2, nbits)) + v = 0; + else + v = v1 << nbits; + } + break; + + case BINOP_RSH: + { + unsigned long nbits; + if (!check_valid_shift_count (op, result_type, type2, v2, nbits)) + v = 0; + else + v = v1 >> nbits; + } + break; + + case BINOP_BITWISE_AND: + v = v1 & v2; + break; + + case BINOP_BITWISE_IOR: + v = v1 | v2; + break; + + case BINOP_BITWISE_XOR: + v = v1 ^ v2; + break; + + case BINOP_MIN: + v = v1 < v2 ? v1 : v2; + break; + + case BINOP_MAX: + v = v1 > v2 ? v1 : v2; + break; + + case BINOP_EQUAL: + v = v1 == v2; + break; + + case BINOP_NOTEQUAL: + v = v1 != v2; + break; - val = value::allocate (result_type); - store_signed_integer (val->contents_raw ().data (), - val->type ()->length (), - type_byte_order (result_type), - v); + case BINOP_LESS: + v = v1 < v2; + break; + + case BINOP_GTR: + v = v1 > v2; + break; + + case BINOP_LEQ: + v = v1 <= v2; + break; + + case BINOP_GEQ: + v = v1 >= v2; + break; + + default: + error (_("Invalid binary operation on numbers.")); } + + val = value_from_mpz (result_type, v); } return val; @@ -1944,7 +1757,11 @@ value_complement (struct value *arg1) type = check_typedef (arg1->type ()); if (is_integral_type (type)) - val = value_from_longest (type, ~value_as_long (arg1)); + { + gdb_mpz num = value_as_mpz (arg1); + num.complement (); + val = value_from_mpz (type, num); + } else if (type->code () == TYPE_CODE_ARRAY && type->is_vector ()) { struct type *eltype = check_typedef (type->target_type ()); diff --git a/gdb/valops.c b/gdb/valops.c index bf9d6a7..0274754 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -570,7 +570,7 @@ value_cast (struct type *type, struct value *arg2) && (scalar || code2 == TYPE_CODE_PTR || code2 == TYPE_CODE_MEMBERPTR)) { - LONGEST longest; + gdb_mpz longest; /* When we cast pointers to integers, we mustn't use gdbarch_pointer_to_address to find the address the pointer @@ -579,12 +579,14 @@ value_cast (struct type *type, struct value *arg2) sees a cast as a simple reinterpretation of the pointer's bits. */ if (code2 == TYPE_CODE_PTR) - longest = extract_unsigned_integer - (arg2->contents (), type_byte_order (type2)); + longest = extract_unsigned_integer (arg2->contents (), + type_byte_order (type2)); else - longest = value_as_long (arg2); - return value_from_longest (to_type, convert_to_boolean ? - (LONGEST) (longest ? 1 : 0) : longest); + longest = value_as_mpz (arg2); + if (convert_to_boolean) + longest = bool (longest); + + return value_from_mpz (to_type, longest); } else if (code1 == TYPE_CODE_PTR && (code2 == TYPE_CODE_INT || code2 == TYPE_CODE_ENUM -- cgit v1.1