summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Tromey <tromey@adacore.com>2023-03-01 15:13:21 -0700
committerTom Tromey <tromey@adacore.com>2023-03-27 08:20:29 -0600
commit303a881f8789733248f27af0c872d356a34be009 (patch)
tree9ffda24d7cd521e72e568bae8d8df20e5c94bc7a
parentd784fa8fb2936fb9bd2ebb8e1854b855ca206d96 (diff)
downloadbinutils-gdb-303a881f8789733248f27af0c872d356a34be009.tar.gz
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
-rw-r--r--gdb/NEWS2
-rw-r--r--gdb/testsuite/gdb.rust/onetwoeight.exp65
-rw-r--r--gdb/testsuite/gdb.rust/onetwoeight.rs31
-rw-r--r--gdb/utils.c30
-rw-r--r--gdb/utils.h7
-rw-r--r--gdb/valarith.c465
-rw-r--r--gdb/valops.c14
7 files changed, 247 insertions, 367 deletions
diff --git a/gdb/NEWS b/gdb/NEWS
index cc262f1f8a6..d729aa24056 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 00000000000..317c848a899
--- /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 <http://www.gnu.org/licenses/>.
+
+# 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 00000000000..aa8cb1cf7b1
--- /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 <http://www.gnu.org/licenses/>.
+
+#![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 8928295efdd..4c7d6657b1f 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 6e240deccbf..a383036bcfe 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 25d72b7f38c..637047fd853 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<LONGEST> ();
+ 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<unsigned long> ();
+ 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<unsigned long> ());
+ 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 bf9d6a78106..02747543a53 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