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
This commit is contained in:
Tom Tromey 2023-03-01 15:13:21 -07:00
parent d784fa8fb2
commit 303a881f87
7 changed files with 248 additions and 368 deletions

View File

@ -3,6 +3,8 @@
*** Changes since GDB 13 *** Changes since GDB 13
* GDB now has some support for integer types larger than 64 bits.
* Removed targets and native configurations * Removed targets and native configurations
GDB no longer supports AIX 4.x, AIX 5.x and AIX 6.x. The minimum supported GDB no longer supports AIX 4.x, AIX 5.x and AIX 6.x. The minimum supported

View File

@ -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"

View File

@ -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
}

View File

@ -723,36 +723,6 @@ myread (int desc, char *addr, int len)
return orglen; 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 /* An RAII class that sets up to handle input and then tears down

View File

@ -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); 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. */ /* Resource limits used by getrlimit and setrlimit. */
enum resource_limit_kind enum resource_limit_kind

View File

@ -34,13 +34,6 @@ static struct value *value_subscripted_rvalue (struct value *array,
LONGEST index, LONGEST index,
LONGEST lowerbound); 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. /* Given a pointer, return the size of its target.
If the pointer type is void *, then return 1. If the pointer type is void *, then return 1.
If the target type is incomplete, then error out. If the target type is incomplete, then error out.
@ -726,36 +719,6 @@ value_concat (struct value *arg1, struct value *arg2)
return result; 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 /* Obtain argument values for binary operation, converting from
other types if one of them is not floating point. */ 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 both negative and too-large shift amounts, which are undefined, and
would crash a GDB built with UBSan. Depending on the current would crash a GDB built with UBSan. Depending on the current
language, if the shift is not valid, this either warns and returns 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 static bool
check_valid_shift_count (enum exp_opcode op, type *result_type, 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) LONGEST count = shift_count.as_integer<LONGEST> ();
{ if (count < 0)
/* Shifts by a negative amount are always an error in Go. Other {
languages are more permissive and their compilers just warn or auto error_or_warning = [] (const char *msg)
have modes to disable the errors. */ {
if (current_language->la_language == language_go) /* Shifts by a negative amount are always an error in Go. Other
error (("%s"), msg); languages are more permissive and their compilers just warn or
else have modes to disable the errors. */
warning (("%s"), msg); if (current_language->la_language == language_go)
}; error (("%s"), msg);
else
warning (("%s"), msg);
};
if (op == BINOP_RSH) if (op == BINOP_RSH)
error_or_warning (_("right shift count is negative")); error_or_warning (_("right shift count is negative"));
else else
error_or_warning (_("left shift count is negative")); error_or_warning (_("left shift count is negative"));
return false; 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 /* In Go, shifting by large amounts is defined. Be silent and
still return false, as the caller's error path does the right 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 else
result_type = promotion_type (type1, type2); 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); case BINOP_ADD:
ULONGEST v1, v2, v = 0; v = v1 + v2;
break;
v1 = (ULONGEST) value_as_long (arg1); case BINOP_SUB:
v2 = (ULONGEST) v2_signed; v = v1 - v2;
break;
switch (op) case BINOP_MUL:
v = v1 * v2;
break;
case BINOP_DIV:
case BINOP_INTDIV:
if (v2.sgn () != 0)
v = v1 / v2;
else
error (_("Division by zero"));
break;
case BINOP_EXP:
v = v1.pow (v2.as_integer<unsigned long> ());
break;
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)
{ {
case BINOP_ADD: v = v1;
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."));
} }
else
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;
v1 = value_as_long (arg1);
v2 = value_as_long (arg2);
switch (op)
{ {
case BINOP_ADD: v = v1 / v2;
v = v1 + v2; /* Note floor(v1/v2) == v1/v2 for unsigned. */
break; v = v1 - (v2 * v);
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."));
} }
break;
val = value::allocate (result_type); case BINOP_LSH:
store_signed_integer (val->contents_raw ().data (), {
val->type ()->length (), unsigned long nbits;
type_byte_order (result_type), if (!check_valid_shift_count (op, result_type, type2, v2, nbits))
v); 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;
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; return val;
@ -1944,7 +1757,11 @@ value_complement (struct value *arg1)
type = check_typedef (arg1->type ()); type = check_typedef (arg1->type ());
if (is_integral_type (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 ()) else if (type->code () == TYPE_CODE_ARRAY && type->is_vector ())
{ {
struct type *eltype = check_typedef (type->target_type ()); struct type *eltype = check_typedef (type->target_type ());

View File

@ -570,7 +570,7 @@ value_cast (struct type *type, struct value *arg2)
&& (scalar || code2 == TYPE_CODE_PTR && (scalar || code2 == TYPE_CODE_PTR
|| code2 == TYPE_CODE_MEMBERPTR)) || code2 == TYPE_CODE_MEMBERPTR))
{ {
LONGEST longest; gdb_mpz longest;
/* When we cast pointers to integers, we mustn't use /* When we cast pointers to integers, we mustn't use
gdbarch_pointer_to_address to find the address the pointer 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 sees a cast as a simple reinterpretation of the pointer's
bits. */ bits. */
if (code2 == TYPE_CODE_PTR) if (code2 == TYPE_CODE_PTR)
longest = extract_unsigned_integer longest = extract_unsigned_integer (arg2->contents (),
(arg2->contents (), type_byte_order (type2)); type_byte_order (type2));
else else
longest = value_as_long (arg2); longest = value_as_mpz (arg2);
return value_from_longest (to_type, convert_to_boolean ? if (convert_to_boolean)
(LONGEST) (longest ? 1 : 0) : longest); longest = bool (longest);
return value_from_mpz (to_type, longest);
} }
else if (code1 == TYPE_CODE_PTR && (code2 == TYPE_CODE_INT else if (code1 == TYPE_CODE_PTR && (code2 == TYPE_CODE_INT
|| code2 == TYPE_CODE_ENUM || code2 == TYPE_CODE_ENUM