* [PATCH 1/8] Add many operators to gdb_mpz
2023-03-03 21:11 [PATCH 0/8] Arithmetic for 128-bit types Tom Tromey
@ 2023-03-03 21:12 ` Tom Tromey
2023-03-07 13:38 ` Alexandra Petlanova Hajkova
2023-03-03 21:12 ` [PATCH 2/8] Avoid a copy in gdb_mpz::safe_export Tom Tromey
` (8 subsequent siblings)
9 siblings, 1 reply; 15+ messages in thread
From: Tom Tromey @ 2023-03-03 21:12 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
This adds many operator overloads and other useful methods to gdb_mpz.
This is preparation for using this class for scalar arithmetic in gdb
expression evaluation.
---
gdb/gmp-utils.h | 138 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 138 insertions(+)
diff --git a/gdb/gmp-utils.h b/gdb/gmp-utils.h
index 274c28c0ce8..d4e5015e345 100644
--- a/gdb/gmp-utils.h
+++ b/gdb/gmp-utils.h
@@ -90,6 +90,12 @@ struct gdb_mpz
return *this;
}
+ gdb_mpz &operator= (bool src)
+ {
+ mpz_set_ui (m_val, (unsigned long) src);
+ return *this;
+ }
+
/* Initialize this value from a string and a base. Returns true if
the string was parsed successfully, false otherwise. */
bool set (const char *str, int base)
@@ -105,6 +111,14 @@ struct gdb_mpz
return result;
}
+ /* Return a new value that is this value raised to EXP. */
+ gdb_mpz pow (unsigned long exp) const
+ {
+ gdb_mpz result;
+ mpz_pow_ui (result.m_val, m_val, exp);
+ return result;
+ }
+
/* Convert VAL to an integer of the given type.
The return type can signed or unsigned, with no size restriction. */
@@ -137,35 +151,154 @@ struct gdb_mpz
mpz_neg (m_val, m_val);
}
+ /* Take the one's complement in place. */
+ void complement ()
+ { mpz_com (m_val, m_val); }
+
+ /* Mask this value to N bits, in place. */
+ void mask (unsigned n)
+ { mpz_tdiv_r_2exp (m_val, m_val, n); }
+
+ /* Return the sign of this value. This returns -1 for a negative
+ value, 0 if the value is 0, and 1 for a positive value. */
+ int sgn () const
+ { return mpz_sgn (m_val); }
+
+ explicit operator bool () const
+ { return sgn () != 0; }
+
gdb_mpz &operator*= (long other)
{
mpz_mul_si (m_val, m_val, other);
return *this;
}
+ gdb_mpz operator* (const gdb_mpz &other) const
+ {
+ gdb_mpz result;
+ mpz_mul (result.m_val, m_val, other.m_val);
+ return result;
+ }
+
+ gdb_mpz operator/ (const gdb_mpz &other) const
+ {
+ gdb_mpz result;
+ mpz_tdiv_q (result.m_val, m_val, other.m_val);
+ return result;
+ }
+
+ gdb_mpz operator% (const gdb_mpz &other) const
+ {
+ gdb_mpz result;
+ mpz_tdiv_r (result.m_val, m_val, other.m_val);
+ return result;
+ }
+
gdb_mpz &operator+= (unsigned long other)
{
mpz_add_ui (m_val, m_val, other);
return *this;
}
+ gdb_mpz &operator+= (const gdb_mpz &other)
+ {
+ mpz_add (m_val, m_val, other.m_val);
+ return *this;
+ }
+
+ gdb_mpz operator+ (const gdb_mpz &other) const
+ {
+ gdb_mpz result;
+ mpz_add (result.m_val, m_val, other.m_val);
+ return result;
+ }
+
gdb_mpz &operator-= (unsigned long other)
{
mpz_sub_ui (m_val, m_val, other);
return *this;
}
+ gdb_mpz &operator-= (const gdb_mpz &other)
+ {
+ mpz_sub (m_val, m_val, other.m_val);
+ return *this;
+ }
+
+ gdb_mpz operator- (const gdb_mpz &other) const
+ {
+ gdb_mpz result;
+ mpz_sub (result.m_val, m_val, other.m_val);
+ return result;
+ }
+
gdb_mpz &operator<<= (unsigned long nbits)
{
mpz_mul_2exp (m_val, m_val, nbits);
return *this;
}
+ gdb_mpz operator<< (unsigned long nbits) const
+ {
+ gdb_mpz result;
+ mpz_mul_2exp (result.m_val, m_val, nbits);
+ return result;
+ }
+
+ gdb_mpz operator>> (unsigned long nbits) const
+ {
+ gdb_mpz result;
+ mpz_tdiv_q_2exp (result.m_val, m_val, nbits);
+ return result;
+ }
+
+ gdb_mpz &operator>>= (unsigned long nbits)
+ {
+ mpz_tdiv_q_2exp (m_val, m_val, nbits);
+ return *this;
+ }
+
+ gdb_mpz operator& (const gdb_mpz &other) const
+ {
+ gdb_mpz result;
+ mpz_and (result.m_val, m_val, other.m_val);
+ return result;
+ }
+
+ gdb_mpz operator| (const gdb_mpz &other) const
+ {
+ gdb_mpz result;
+ mpz_ior (result.m_val, m_val, other.m_val);
+ return result;
+ }
+
+ gdb_mpz operator^ (const gdb_mpz &other) const
+ {
+ gdb_mpz result;
+ mpz_xor (result.m_val, m_val, other.m_val);
+ return result;
+ }
+
bool operator> (const gdb_mpz &other) const
{
return mpz_cmp (m_val, other.m_val) > 0;
}
+ bool operator>= (const gdb_mpz &other) const
+ {
+ return mpz_cmp (m_val, other.m_val) >= 0;
+ }
+
+ bool operator< (const gdb_mpz &other) const
+ {
+ return mpz_cmp (m_val, other.m_val) < 0;
+ }
+
+ bool operator<= (const gdb_mpz &other) const
+ {
+ return mpz_cmp (m_val, other.m_val) <= 0;
+ }
+
bool operator< (int other) const
{
return mpz_cmp_si (m_val, other) < 0;
@@ -181,6 +314,11 @@ struct gdb_mpz
return mpz_cmp (m_val, other.m_val) == 0;
}
+ bool operator!= (const gdb_mpz &other) const
+ {
+ return mpz_cmp (m_val, other.m_val) != 0;
+ }
+
private:
/* Helper template for constructor and operator=. */
--
2.39.1
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 2/8] Avoid a copy in gdb_mpz::safe_export
2023-03-03 21:11 [PATCH 0/8] Arithmetic for 128-bit types Tom Tromey
2023-03-03 21:12 ` [PATCH 1/8] Add many operators to gdb_mpz Tom Tromey
@ 2023-03-03 21:12 ` Tom Tromey
2023-03-03 21:12 ` [PATCH 3/8] Add truncation mode to gdb_mpz Tom Tromey
` (7 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2023-03-03 21:12 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
Currently, gdb_mpz::safe_export will always make a copy of *this.
However, this copy isn't always needed. This patch makes this code
slightly more efficient, by avoiding the copy when possible.
---
gdb/gmp-utils.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/gdb/gmp-utils.c b/gdb/gmp-utils.c
index d134bc32a1d..57f5f9766b9 100644
--- a/gdb/gmp-utils.c
+++ b/gdb/gmp-utils.c
@@ -81,7 +81,8 @@ gdb_mpz::safe_export (gdb::array_view<gdb_byte> buf,
{
gdb_assert (buf.size () > 0);
- if (mpz_sgn (m_val) == 0)
+ int sign = mpz_sgn (m_val);
+ if (sign == 0)
{
/* Our value is zero, so no need to call mpz_export to do the work,
especially since mpz_export's documentation explicitly says
@@ -121,17 +122,16 @@ gdb_mpz::safe_export (gdb::array_view<gdb_byte> buf,
lo.str ().c_str (),
hi.str ().c_str ());
- gdb_mpz exported_val (m_val);
-
- if (mpz_cmp_ui (exported_val.m_val, 0) < 0)
+ const gdb_mpz *exported_val = this;
+ gdb_mpz un_signed;
+ if (sign < 0)
{
/* mpz_export does not handle signed values, so create a positive
value whose bit representation as an unsigned of the same length
would be the same as our negative value. */
- gdb_mpz neg_offset;
-
- mpz_ui_pow_ui (neg_offset.m_val, 2, buf.size () * HOST_CHAR_BIT);
- mpz_add (exported_val.m_val, exported_val.m_val, neg_offset.m_val);
+ gdb_mpz neg_offset = gdb_mpz::pow (2, buf.size () * HOST_CHAR_BIT);
+ un_signed = *exported_val + neg_offset;
+ exported_val = &un_signed;
}
/* Do the export into a buffer allocated by GMP itself; that way,
@@ -147,7 +147,7 @@ gdb_mpz::safe_export (gdb::array_view<gdb_byte> buf,
size_t word_countp;
gdb::unique_xmalloc_ptr<void> exported
(mpz_export (NULL, &word_countp, -1 /* order */, buf.size () /* size */,
- endian, 0 /* nails */, exported_val.m_val));
+ endian, 0 /* nails */, exported_val->m_val));
gdb_assert (word_countp == 1);
--
2.39.1
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 3/8] Add truncation mode to gdb_mpz
2023-03-03 21:11 [PATCH 0/8] Arithmetic for 128-bit types Tom Tromey
2023-03-03 21:12 ` [PATCH 1/8] Add many operators to gdb_mpz Tom Tromey
2023-03-03 21:12 ` [PATCH 2/8] Avoid a copy in gdb_mpz::safe_export Tom Tromey
@ 2023-03-03 21:12 ` Tom Tromey
2023-03-03 21:12 ` [PATCH 4/8] Add value_as_mpz and value_from_mpz Tom Tromey
` (6 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2023-03-03 21:12 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
This renames gdb_mpz::safe_export to export_bits, and adds a new flag
to export a truncated value. This is needed by value arithmetic.
---
gdb/gmp-utils.c | 93 ++++++++++++++++++++++++++++---------------------
gdb/gmp-utils.h | 29 +++++++++++----
2 files changed, 76 insertions(+), 46 deletions(-)
diff --git a/gdb/gmp-utils.c b/gdb/gmp-utils.c
index 57f5f9766b9..0afa344781b 100644
--- a/gdb/gmp-utils.c
+++ b/gdb/gmp-utils.c
@@ -66,18 +66,8 @@ gdb_mpz::read (gdb::array_view<const gdb_byte> buf, enum bfd_endian byte_order,
/* See gmp-utils.h. */
void
-gdb_mpz::write (gdb::array_view<gdb_byte> buf, enum bfd_endian byte_order,
- bool unsigned_p) const
-{
- this->safe_export
- (buf, byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */, unsigned_p);
-}
-
-/* See gmp-utils.h. */
-
-void
-gdb_mpz::safe_export (gdb::array_view<gdb_byte> buf,
- int endian, bool unsigned_p) const
+gdb_mpz::export_bits (gdb::array_view<gdb_byte> buf, int endian, bool unsigned_p,
+ bool safe) const
{
gdb_assert (buf.size () > 0);
@@ -92,36 +82,39 @@ gdb_mpz::safe_export (gdb::array_view<gdb_byte> buf,
return;
}
- /* Determine the maximum range of values that our buffer can hold,
- and verify that VAL is within that range. */
-
- gdb_mpz lo, hi;
- const size_t max_usable_bits = buf.size () * HOST_CHAR_BIT;
- if (unsigned_p)
- {
- lo = 0;
-
- mpz_ui_pow_ui (hi.m_val, 2, max_usable_bits);
- mpz_sub_ui (hi.m_val, hi.m_val, 1);
- }
- else
+ if (safe)
{
- mpz_ui_pow_ui (lo.m_val, 2, max_usable_bits - 1);
- mpz_neg (lo.m_val, lo.m_val);
-
- mpz_ui_pow_ui (hi.m_val, 2, max_usable_bits - 1);
- mpz_sub_ui (hi.m_val, hi.m_val, 1);
+ /* Determine the maximum range of values that our buffer can
+ hold, and verify that VAL is within that range. */
+
+ gdb_mpz lo, hi;
+ const size_t max_usable_bits = buf.size () * HOST_CHAR_BIT;
+ if (unsigned_p)
+ {
+ lo = 0;
+
+ mpz_ui_pow_ui (hi.m_val, 2, max_usable_bits);
+ mpz_sub_ui (hi.m_val, hi.m_val, 1);
+ }
+ else
+ {
+ mpz_ui_pow_ui (lo.m_val, 2, max_usable_bits - 1);
+ mpz_neg (lo.m_val, lo.m_val);
+
+ mpz_ui_pow_ui (hi.m_val, 2, max_usable_bits - 1);
+ mpz_sub_ui (hi.m_val, hi.m_val, 1);
+ }
+
+ if (mpz_cmp (m_val, lo.m_val) < 0 || mpz_cmp (m_val, hi.m_val) > 0)
+ error (_("Cannot export value %s as %zu-bits %s integer"
+ " (must be between %s and %s)"),
+ this->str ().c_str (),
+ max_usable_bits,
+ unsigned_p ? _("unsigned") : _("signed"),
+ lo.str ().c_str (),
+ hi.str ().c_str ());
}
- if (mpz_cmp (m_val, lo.m_val) < 0 || mpz_cmp (m_val, hi.m_val) > 0)
- error (_("Cannot export value %s as %zu-bits %s integer"
- " (must be between %s and %s)"),
- this->str ().c_str (),
- max_usable_bits,
- unsigned_p ? _("unsigned") : _("signed"),
- lo.str ().c_str (),
- hi.str ().c_str ());
-
const gdb_mpz *exported_val = this;
gdb_mpz un_signed;
if (sign < 0)
@@ -134,6 +127,28 @@ gdb_mpz::safe_export (gdb::array_view<gdb_byte> buf,
exported_val = &un_signed;
}
+ /* If the value is too large, truncate it. */
+ if (!safe
+ && mpz_sizeinbase (exported_val->m_val, 2) > buf.size () * HOST_CHAR_BIT)
+ {
+ /* If we don't already have a copy, make it now. */
+ if (exported_val != &un_signed)
+ {
+ un_signed = *exported_val;
+ exported_val = &un_signed;
+ }
+
+ un_signed.mask (buf.size () * HOST_CHAR_BIT);
+ }
+
+ /* It's possible that one of the above results in zero, which has to
+ be handled specially. */
+ if (exported_val->sgn () == 0)
+ {
+ memset (buf.data (), 0, buf.size ());
+ return;
+ }
+
/* Do the export into a buffer allocated by GMP itself; that way,
we can detect cases where BUF is not large enough to export
our value, and thus avoid a buffer overlow. Normally, this should
diff --git a/gdb/gmp-utils.h b/gdb/gmp-utils.h
index d4e5015e345..d378499d287 100644
--- a/gdb/gmp-utils.h
+++ b/gdb/gmp-utils.h
@@ -137,7 +137,20 @@ struct gdb_mpz
UNSIGNED_P indicates whether the number has an unsigned type. */
void write (gdb::array_view<gdb_byte> buf, enum bfd_endian byte_order,
- bool unsigned_p) const;
+ bool unsigned_p) const
+ {
+ export_bits (buf, byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */,
+ unsigned_p, true /* safe */);
+ }
+
+ /* Like write, but truncates the value to the desired number of
+ bytes. */
+ void truncate (gdb::array_view<gdb_byte> buf, enum bfd_endian byte_order,
+ bool unsigned_p) const
+ {
+ export_bits (buf, byte_order == BFD_ENDIAN_BIG ? 1 : -1 /* endian */,
+ unsigned_p, false /* safe */);
+ }
/* Return a string containing VAL. */
std::string str () const { return gmp_string_printf ("%Zd", m_val); }
@@ -337,10 +350,11 @@ struct gdb_mpz
. -1 for least significant byte first; or
. 0 for native endianness.
- An error is raised if BUF is not large enough to contain the value
- being exported. */
- void safe_export (gdb::array_view<gdb_byte> buf,
- int endian, bool unsigned_p) const;
+ If SAFE is true, an error is raised if BUF is not large enough to
+ contain the value being exported. If SAFE is false, the value is
+ truncated to fit in BUF. */
+ void export_bits (gdb::array_view<gdb_byte> buf, int endian, bool unsigned_p,
+ bool safe) const;
friend struct gdb_mpq;
friend struct gdb_mpf;
@@ -590,9 +604,10 @@ gdb_mpz::as_integer () const
{
T result;
- this->safe_export ({(gdb_byte *) &result, sizeof (result)},
+ this->export_bits ({(gdb_byte *) &result, sizeof (result)},
0 /* endian (0 = native) */,
- !std::is_signed<T>::value /* unsigned_p */);
+ !std::is_signed<T>::value /* unsigned_p */,
+ true /* safe */);
return result;
}
--
2.39.1
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 4/8] Add value_as_mpz and value_from_mpz
2023-03-03 21:11 [PATCH 0/8] Arithmetic for 128-bit types Tom Tromey
` (2 preceding siblings ...)
2023-03-03 21:12 ` [PATCH 3/8] Add truncation mode to gdb_mpz Tom Tromey
@ 2023-03-03 21:12 ` Tom Tromey
2023-03-08 10:47 ` Lancelot SIX
2023-03-03 21:12 ` [PATCH 5/8] Simplify binop_promote Tom Tromey
` (5 subsequent siblings)
9 siblings, 1 reply; 15+ messages in thread
From: Tom Tromey @ 2023-03-03 21:12 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
This adds the two new functions, value_as_mpz and value_from_mpz,
useful for manipulation values via gdb_mpz.
---
gdb/value.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb/value.h | 7 ++++
2 files changed, 113 insertions(+)
diff --git a/gdb/value.c b/gdb/value.c
index e9afa89c243..19da9e1a00b 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -2551,6 +2551,76 @@ value_as_long (struct value *val)
return unpack_long (val->type (), val->contents ().data ());
}
+/* See value.h. */
+
+gdb_mpz
+value_as_mpz (struct value *val)
+{
+ val = coerce_array (val);
+ struct type *type = check_typedef (val->type ());
+
+ switch (type->code ())
+ {
+ case TYPE_CODE_ENUM:
+ case TYPE_CODE_BOOL:
+ case TYPE_CODE_INT:
+ case TYPE_CODE_CHAR:
+ case TYPE_CODE_RANGE:
+ break;
+
+ default:
+ return gdb_mpz (value_as_long (val));
+ }
+
+ gdb_mpz result;
+
+ gdb::array_view<const gdb_byte> valbytes = val->contents ();
+ enum bfd_endian byte_order = type_byte_order (type);
+
+ /* Handle integers that are either not a multiple of the word size,
+ or that are stored at some bit offset. */
+ unsigned bit_off = 0, bit_size = 0;
+ if (type->bit_size_differs_p ())
+ {
+ bit_size = type->bit_size ();
+ if (bit_size == 0)
+ {
+ /* We can just handle this immediately. */
+ return result;
+ }
+
+ bit_off = type->bit_offset ();
+
+ unsigned n_bytes = ((bit_off % 8) + bit_size + 7) / 8;
+ valbytes = valbytes.slice (bit_off / 8, n_bytes);
+
+ if (byte_order == BFD_ENDIAN_BIG)
+ bit_off = (n_bytes * 8 - bit_off % 8 - bit_size);
+ else
+ bit_off %= 8;
+ }
+
+ result.read (val->contents (), byte_order, type->is_unsigned ());
+
+ /* Shift off any low bits, if needed. */
+ if (bit_off != 0)
+ result >>= bit_off;
+
+ /* Mask off any high bits, if needed. */
+ if (bit_size)
+ result.mask (bit_size);
+
+ /* Now handle any range bias. */
+ if (type->code () == TYPE_CODE_RANGE && type->bounds ()->bias != 0)
+ {
+ /* Unfortunately we have to box here, because LONGEST is
+ probably wider than long. */
+ result += gdb_mpz (type->bounds ()->bias);
+ }
+
+ return result;
+}
+
/* 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. */
@@ -3378,6 +3448,42 @@ value_from_ulongest (struct type *type, ULONGEST num)
return val;
}
+/* See value.h. */
+
+struct value *
+value_from_mpz (struct type *type, const gdb_mpz &v)
+{
+ struct type *real_type = check_typedef (type);
+
+ const gdb_mpz *val = &v;
+ gdb_mpz storage;
+ if (real_type->code () == TYPE_CODE_RANGE && type->bounds ()->bias != 0)
+ {
+ storage = *val;
+ val = &storage;
+ storage -= type->bounds ()->bias;
+ }
+
+ if (type->bit_size_differs_p ())
+ {
+ unsigned bit_off = type->bit_offset ();
+ unsigned bit_size = type->bit_size ();
+
+ if (val != &storage)
+ {
+ storage = *val;
+ val = &storage;
+ }
+
+ storage.mask ((ULONGEST) 1 << bit_size);
+ storage <<= bit_off;
+ }
+
+ struct value *result = value::allocate (type);
+ val->truncate (result->contents_raw (), type_byte_order (type),
+ type->is_unsigned ());
+ return result;
+}
/* Create a value representing a pointer of type TYPE to the address
ADDR. */
diff --git a/gdb/value.h b/gdb/value.h
index d83c4ab3674..1b4eff22f37 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -1029,6 +1029,11 @@ extern bool is_floating_value (struct value *val);
extern LONGEST value_as_long (struct value *val);
extern CORE_ADDR value_as_address (struct value *val);
+/* Extract the value from VAL as a MPZ. This coerces arrays and
+ handles various integer-like types as well. */
+
+extern gdb_mpz value_as_mpz (struct value *val);
+
extern LONGEST unpack_long (struct type *type, const gdb_byte *valaddr);
extern CORE_ADDR unpack_pointer (struct type *type, const gdb_byte *valaddr);
@@ -1075,6 +1080,8 @@ extern struct value *value_from_history_ref (const char *, const char **);
extern struct value *value_from_component (struct value *, struct type *,
LONGEST);
+/* Convert the value V into a newly allocated value. */
+extern struct value *value_from_mpz (struct type *type, const gdb_mpz &v);
extern struct value *value_at (struct type *type, CORE_ADDR addr);
extern struct value *value_at_lazy (struct type *type, CORE_ADDR addr);
--
2.39.1
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 4/8] Add value_as_mpz and value_from_mpz
2023-03-03 21:12 ` [PATCH 4/8] Add value_as_mpz and value_from_mpz Tom Tromey
@ 2023-03-08 10:47 ` Lancelot SIX
2023-03-08 15:48 ` Tom Tromey
0 siblings, 1 reply; 15+ messages in thread
From: Lancelot SIX @ 2023-03-08 10:47 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
Hi,
On Fri, Mar 03, 2023 at 02:12:03PM -0700, Tom Tromey via Gdb-patches wrote:
> This adds the two new functions, value_as_mpz and value_from_mpz,
> useful for manipulation values via gdb_mpz.
> ---
> gdb/value.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> gdb/value.h | 7 ++++
> 2 files changed, 113 insertions(+)
>
> diff --git a/gdb/value.c b/gdb/value.c
> index e9afa89c243..19da9e1a00b 100644
> --- a/gdb/value.c
> +++ b/gdb/value.c
> @@ -2551,6 +2551,76 @@ value_as_long (struct value *val)
> return unpack_long (val->type (), val->contents ().data ());
> }
>
> +/* See value.h. */
> +
> +gdb_mpz
> +value_as_mpz (struct value *val)
> +{
> + val = coerce_array (val);
> + struct type *type = check_typedef (val->type ());
> +
> + switch (type->code ())
> + {
> + case TYPE_CODE_ENUM:
> + case TYPE_CODE_BOOL:
> + case TYPE_CODE_INT:
> + case TYPE_CODE_CHAR:
> + case TYPE_CODE_RANGE:
> + break;
> +
> + default:
> + return gdb_mpz (value_as_long (val));
> + }
> +
> + gdb_mpz result;
> +
> + gdb::array_view<const gdb_byte> valbytes = val->contents ();
> + enum bfd_endian byte_order = type_byte_order (type);
> +
> + /* Handle integers that are either not a multiple of the word size,
> + or that are stored at some bit offset. */
> + unsigned bit_off = 0, bit_size = 0;
> + if (type->bit_size_differs_p ())
> + {
> + bit_size = type->bit_size ();
> + if (bit_size == 0)
> + {
> + /* We can just handle this immediately. */
> + return result;
> + }
> +
> + bit_off = type->bit_offset ();
> +
> + unsigned n_bytes = ((bit_off % 8) + bit_size + 7) / 8;
This is a bit out of the scope of this patch as gdbvalue.h says that
bit_off / bit_size are 8 based, but should they be HOST_CHAR_BIT based
instead? The n_bytes is used to to access a
"gdb::array_view<const gdb_byte>".
> + valbytes = valbytes.slice (bit_off / 8, n_bytes);
> +
> + if (byte_order == BFD_ENDIAN_BIG)
> + bit_off = (n_bytes * 8 - bit_off % 8 - bit_size);
> + else
> + bit_off %= 8;
> + }
> +
> + result.read (val->contents (), byte_order, type->is_unsigned ());
> +
> + /* Shift off any low bits, if needed. */
> + if (bit_off != 0)
> + result >>= bit_off;
> +
> + /* Mask off any high bits, if needed. */
> + if (bit_size)
> + result.mask (bit_size);
> +
> + /* Now handle any range bias. */
> + if (type->code () == TYPE_CODE_RANGE && type->bounds ()->bias != 0)
> + {
> + /* Unfortunately we have to box here, because LONGEST is
> + probably wider than long. */
> + result += gdb_mpz (type->bounds ()->bias);
> + }
> +
> + return result;
> +}
> +
> /* 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. */
> @@ -3378,6 +3448,42 @@ value_from_ulongest (struct type *type, ULONGEST num)
> return val;
> }
>
> +/* See value.h. */
> +
> +struct value *
> +value_from_mpz (struct type *type, const gdb_mpz &v)
> +{
> + struct type *real_type = check_typedef (type);
> +
> + const gdb_mpz *val = &v;
> + gdb_mpz storage;
> + if (real_type->code () == TYPE_CODE_RANGE && type->bounds ()->bias != 0)
> + {
> + storage = *val;
> + val = &storage;
> + storage -= type->bounds ()->bias;
> + }
> +
> + if (type->bit_size_differs_p ())
> + {
> + unsigned bit_off = type->bit_offset ();
> + unsigned bit_size = type->bit_size ();
> +
> + if (val != &storage)
> + {
> + storage = *val;
> + val = &storage;
> + }
> +
> + storage.mask ((ULONGEST) 1 << bit_size);
I do not think you should have the "(ULONGEST) 1 <<" part here.
gdb_mpz::mask does mask N bits (N being its arg).
> + storage <<= bit_off;
> + }
Best,
Lancelot.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 4/8] Add value_as_mpz and value_from_mpz
2023-03-08 10:47 ` Lancelot SIX
@ 2023-03-08 15:48 ` Tom Tromey
0 siblings, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2023-03-08 15:48 UTC (permalink / raw)
To: Lancelot SIX; +Cc: Tom Tromey, gdb-patches
>> + unsigned n_bytes = ((bit_off % 8) + bit_size + 7) / 8;
Lancelot> This is a bit out of the scope of this patch as gdbvalue.h says that
Lancelot> bit_off / bit_size are 8 based, but should they be HOST_CHAR_BIT based
Lancelot> instead? The n_bytes is used to to access a
Lancelot> "gdb::array_view<const gdb_byte>".
Yeah, I do not know. I am not sure I've ever understood how gdb intends
to handle these scenarios, if it even really does. I do see uses of
HOST_CHAR_BIT, but also plain '8'.
Also there's this weirdness:
#if defined (CHAR_BIT)
#define HOST_CHAR_BIT CHAR_BIT
#else
#define HOST_CHAR_BIT TARGET_CHAR_BIT
#endif
It's hard to understand how this can ever make sense, even despite the
comment (which may be obsolete).
TARGET_CHAR_BIT is even worse because surely it cannot be a constant.
Anyway, this code in question in this patch is just copied from
elsewhere.
>> + storage.mask ((ULONGEST) 1 << bit_size);
Lancelot> I do not think you should have the "(ULONGEST) 1 <<" part here.
Lancelot> gdb_mpz::mask does mask N bits (N being its arg).
Thank you.
Tom
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 5/8] Simplify binop_promote
2023-03-03 21:11 [PATCH 0/8] Arithmetic for 128-bit types Tom Tromey
` (3 preceding siblings ...)
2023-03-03 21:12 ` [PATCH 4/8] Add value_as_mpz and value_from_mpz Tom Tromey
@ 2023-03-03 21:12 ` Tom Tromey
2023-03-03 21:12 ` [PATCH 6/8] Use value_true in value_equal and value_less Tom Tromey
` (4 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2023-03-03 21:12 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
binop_promote currently only handles integer sizes up to
builtin_long_long. However, this may not handle 128-bit types.
Simplify this code, unify the C and non-C (but not OpenCL, as I don't
know how to test this) cases, and handle 128-bit integers as well.
This still doesn't exactly follow C or C++ rules. This could be
implemented, but if so, I think it makes more sense as a C-specific
expression node.
---
gdb/eval.c | 55 +++++++++++++++++++-----------------------------------
1 file changed, 19 insertions(+), 36 deletions(-)
diff --git a/gdb/eval.c b/gdb/eval.c
index f8bbb9ef766..6b362f46424 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -371,29 +371,6 @@ binop_promote (const struct language_defn *language, struct gdbarch *gdbarch,
switch (language->la_language)
{
- case language_c:
- case language_cplus:
- case language_asm:
- case language_objc:
- if (result_len <= builtin->builtin_int->length ())
- {
- promoted_type = (unsigned_operation
- ? builtin->builtin_unsigned_int
- : builtin->builtin_int);
- }
- else if (result_len <= builtin->builtin_long->length ())
- {
- promoted_type = (unsigned_operation
- ? builtin->builtin_unsigned_long
- : builtin->builtin_long);
- }
- else
- {
- promoted_type = (unsigned_operation
- ? builtin->builtin_unsigned_long_long
- : builtin->builtin_long_long);
- }
- break;
case language_opencl:
if (result_len
<= lookup_signed_typename (language, "int")->length())
@@ -413,23 +390,29 @@ binop_promote (const struct language_defn *language, struct gdbarch *gdbarch,
}
break;
default:
- /* For other languages the result type is unchanged from gdb
- version 6.7 for backward compatibility.
- If either arg was long long, make sure that value is also long
- long. Otherwise use long. */
- if (unsigned_operation)
+ if (result_len <= builtin->builtin_int->length ())
{
- if (result_len > gdbarch_long_bit (gdbarch) / HOST_CHAR_BIT)
- promoted_type = builtin->builtin_unsigned_long_long;
- else
- promoted_type = builtin->builtin_unsigned_long;
+ promoted_type = (unsigned_operation
+ ? builtin->builtin_unsigned_int
+ : builtin->builtin_int);
+ }
+ else if (result_len <= builtin->builtin_long->length ())
+ {
+ promoted_type = (unsigned_operation
+ ? builtin->builtin_unsigned_long
+ : builtin->builtin_long);
+ }
+ else if (result_len <= builtin->builtin_long_long->length ())
+ {
+ promoted_type = (unsigned_operation
+ ? builtin->builtin_unsigned_long_long
+ : builtin->builtin_long_long);
}
else
{
- if (result_len > gdbarch_long_bit (gdbarch) / HOST_CHAR_BIT)
- promoted_type = builtin->builtin_long_long;
- else
- promoted_type = builtin->builtin_long;
+ promoted_type = (unsigned_operation
+ ? builtin->builtin_uint128
+ : builtin->builtin_int128);
}
break;
}
--
2.39.1
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 6/8] Use value_true in value_equal and value_less
2023-03-03 21:11 [PATCH 0/8] Arithmetic for 128-bit types Tom Tromey
` (4 preceding siblings ...)
2023-03-03 21:12 ` [PATCH 5/8] Simplify binop_promote Tom Tromey
@ 2023-03-03 21:12 ` Tom Tromey
2023-03-03 21:12 ` [PATCH 7/8] Use gdb_gmp for scalar arithmetic Tom Tromey
` (3 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2023-03-03 21:12 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
Both value_equal and value_less use value_as_long to check a
presumably boolean result of calling value_binop. However,
value_binop in this case actually returns an int as wide as its
arguments, and this approach can then fail for integers wider than
LONGEST. Instead, rewrite this in a form that works for any size
integer.
---
gdb/valarith.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/gdb/valarith.c b/gdb/valarith.c
index 9d681dc9d97..99597bef695 100644
--- a/gdb/valarith.c
+++ b/gdb/valarith.c
@@ -1761,8 +1761,7 @@ value_equal (struct value *arg1, struct value *arg2)
is_int2 = is_integral_type (type2);
if (is_int1 && is_int2)
- return longest_to_int (value_as_long (value_binop (arg1, arg2,
- BINOP_EQUAL)));
+ return value_true (value_binop (arg1, arg2, BINOP_EQUAL));
else if ((is_floating_value (arg1) || is_int1)
&& (is_floating_value (arg2) || is_int2))
{
@@ -1849,8 +1848,7 @@ value_less (struct value *arg1, struct value *arg2)
if ((is_int1 && is_int2)
|| (is_fixed_point_type (type1) && is_fixed_point_type (type2)))
- return longest_to_int (value_as_long (value_binop (arg1, arg2,
- BINOP_LESS)));
+ return value_true (value_binop (arg1, arg2, BINOP_LESS));
else if ((is_floating_value (arg1) || is_int1)
&& (is_floating_value (arg2) || is_int2))
{
--
2.39.1
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 7/8] Use gdb_gmp for scalar arithmetic
2023-03-03 21:11 [PATCH 0/8] Arithmetic for 128-bit types Tom Tromey
` (5 preceding siblings ...)
2023-03-03 21:12 ` [PATCH 6/8] Use value_true in value_equal and value_less Tom Tromey
@ 2023-03-03 21:12 ` Tom Tromey
2023-03-03 21:12 ` [PATCH 8/8] Fix 128-bit integer bug in Ada Tom Tromey
` (2 subsequent siblings)
9 siblings, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2023-03-03 21:12 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
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/testsuite/gdb.rust/onetwoeight.exp | 65 ++++
gdb/testsuite/gdb.rust/onetwoeight.rs | 31 ++
gdb/utils.c | 30 --
gdb/utils.h | 7 -
gdb/valarith.c | 489 ++++++++-----------------
gdb/valops.c | 14 +-
6 files changed, 253 insertions(+), 383 deletions(-)
create mode 100644 gdb/testsuite/gdb.rust/onetwoeight.exp
create mode 100644 gdb/testsuite/gdb.rust/onetwoeight.rs
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 0138c8e9fb6..9ca83246b24 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;
- }
- }
-}
-
\f
/* 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 99597bef695..a8ae17e1730 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;
}
\f
-/* 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,299 +1218,135 @@ 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_LOGICAL_AND:
- v = v1 && v2;
- break;
-
- case BINOP_LOGICAL_OR:
- 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;
+
+ case BINOP_REM:
+ if (v2.sgn () != 0)
+ v = v1 % v2;
+ else
+ error (_("Division by zero"));
+ break;
- switch (op)
+ 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_LOGICAL_AND:
- v = v1 && v2;
- break;
-
- case BINOP_LOGICAL_OR:
- 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;
- val = value::allocate (result_type);
- store_signed_integer (val->contents_raw ().data (),
- val->type ()->length (),
- type_byte_order (result_type),
- v);
+ 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_LOGICAL_AND:
+ v = v1 && v2;
+ break;
+
+ case BINOP_LOGICAL_OR:
+ 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;
@@ -1960,7 +1765,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 2cef24fc4c5..f06969e92e6 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -569,7 +569,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
@@ -578,12 +578,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
--
2.39.1
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 8/8] Fix 128-bit integer bug in Ada
2023-03-03 21:11 [PATCH 0/8] Arithmetic for 128-bit types Tom Tromey
` (6 preceding siblings ...)
2023-03-03 21:12 ` [PATCH 7/8] Use gdb_gmp for scalar arithmetic Tom Tromey
@ 2023-03-03 21:12 ` Tom Tromey
2023-03-04 7:04 ` [PATCH 0/8] Arithmetic for 128-bit types Eli Zaretskii
2023-03-27 14:20 ` Tom Tromey
9 siblings, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2023-03-03 21:12 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
While working on 128-bit integer support, I found one spot in Ada that
needed a fix as well.
---
gdb/ada-lang.c | 27 ++++--------------
gdb/testsuite/gdb.ada/verylong.exp | 37 +++++++++++++++++++++++++
gdb/testsuite/gdb.ada/verylong/prog.adb | 20 +++++++++++++
3 files changed, 63 insertions(+), 21 deletions(-)
create mode 100644 gdb/testsuite/gdb.ada/verylong.exp
create mode 100644 gdb/testsuite/gdb.ada/verylong/prog.adb
diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 7b07c4f9473..d1afb1a27b1 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -62,14 +62,6 @@
#include "charset.h"
#include "ax-gdb.h"
-/* Define whether or not the C operator '/' truncates towards zero for
- differently signed operands (truncation direction is undefined in C).
- Copied from valarith.c. */
-
-#ifndef TRUNCATION_TOWARDS_ZERO
-#define TRUNCATION_TOWARDS_ZERO ((-5 / 2) == -2)
-#endif
-
static struct type *desc_base_type (struct type *);
static struct type *desc_bounds_type (struct type *);
@@ -9351,9 +9343,7 @@ coerce_for_assign (struct type *type, struct value *val)
static struct value *
ada_value_binop (struct value *arg1, struct value *arg2, enum exp_opcode op)
{
- struct value *val;
struct type *type1, *type2;
- LONGEST v, v1, v2;
arg1 = coerce_ref (arg1);
arg2 = coerce_ref (arg2);
@@ -9374,8 +9364,8 @@ ada_value_binop (struct value *arg1, struct value *arg2, enum exp_opcode op)
return value_binop (arg1, arg2, op);
}
- v2 = value_as_long (arg2);
- if (v2 == 0)
+ gdb_mpz v2 = value_as_mpz (arg2);
+ if (v2.sgn () == 0)
{
const char *name;
if (op == BINOP_MOD)
@@ -9394,13 +9384,12 @@ ada_value_binop (struct value *arg1, struct value *arg2, enum exp_opcode op)
if (type1->is_unsigned () || op == BINOP_MOD)
return value_binop (arg1, arg2, op);
- v1 = value_as_long (arg1);
+ gdb_mpz v1 = value_as_mpz (arg1);
+ gdb_mpz v;
switch (op)
{
case BINOP_DIV:
v = v1 / v2;
- if (!TRUNCATION_TOWARDS_ZERO && v1 * (v1 % v2) < 0)
- v += v > 0 ? -1 : 1;
break;
case BINOP_REM:
v = v1 % v2;
@@ -9409,14 +9398,10 @@ ada_value_binop (struct value *arg1, struct value *arg2, enum exp_opcode op)
break;
default:
/* Should not reach this point. */
- v = 0;
+ gdb_assert_not_reached ("invalid operator");
}
- val = value::allocate (type1);
- store_unsigned_integer (val->contents_raw ().data (),
- val->type ()->length (),
- type_byte_order (type1), v);
- return val;
+ return value_from_mpz (type1, v);
}
static int
diff --git a/gdb/testsuite/gdb.ada/verylong.exp b/gdb/testsuite/gdb.ada/verylong.exp
new file mode 100644
index 00000000000..93df785862b
--- /dev/null
+++ b/gdb/testsuite/gdb.ada/verylong.exp
@@ -0,0 +1,37 @@
+# Copyright 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/>.
+
+load_lib "ada.exp"
+
+require allow_ada_tests
+
+standard_ada_testfile prog
+
+if {[gdb_compile_ada "${srcfile}" "${binfile}" executable {debug}] != ""} {
+ return -1
+}
+
+clean_restart ${testfile}
+
+set bp_location [gdb_get_line_number "START" ${testdir}/prog.adb]
+runto "prog.adb:$bp_location"
+
+gdb_test "print x" " = 170141183460469231731687303715884105727"
+gdb_test "print x / 2" " = 85070591730234615865843651857942052863"
+gdb_test "print (x / 4) * 2" " = 85070591730234615865843651857942052862"
+gdb_test "print x - x" " = 0"
+gdb_test "print x - 99 + 1" " = 170141183460469231731687303715884105629"
+gdb_test "print -x" " = -170141183460469231731687303715884105727"
+gdb_test "print +x" " = 170141183460469231731687303715884105727"
diff --git a/gdb/testsuite/gdb.ada/verylong/prog.adb b/gdb/testsuite/gdb.ada/verylong/prog.adb
new file mode 100644
index 00000000000..766419d0a4f
--- /dev/null
+++ b/gdb/testsuite/gdb.ada/verylong/prog.adb
@@ -0,0 +1,20 @@
+-- Copyright 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/>.
+
+procedure Main is
+ X : Long_Long_Long_Integer := Long_Long_Long_Integer'Last;
+begin
+ null; -- START
+end Main;
--
2.39.1
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/8] Arithmetic for 128-bit types
2023-03-03 21:11 [PATCH 0/8] Arithmetic for 128-bit types Tom Tromey
` (7 preceding siblings ...)
2023-03-03 21:12 ` [PATCH 8/8] Fix 128-bit integer bug in Ada Tom Tromey
@ 2023-03-04 7:04 ` Eli Zaretskii
2023-03-08 16:17 ` Tom Tromey
2023-03-27 14:20 ` Tom Tromey
9 siblings, 1 reply; 15+ messages in thread
From: Eli Zaretskii @ 2023-03-04 7:04 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
> Date: Fri, 3 Mar 2023 14:11:59 -0700
> X-Spam-Status: No, score=-5.6 required=5.0 tests=BAYES_00, DKIM_SIGNED,
> DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,
> SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6
> From: Tom Tromey via Gdb-patches <gdb-patches@sourceware.org>
>
> This series adss basic arithmetic for 128-bit integer types to gdb.
Can you tell where and in what situations will this be useful?
Also, don't we need to announce this, at least in NEWS?
Thanks.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/8] Arithmetic for 128-bit types
2023-03-04 7:04 ` [PATCH 0/8] Arithmetic for 128-bit types Eli Zaretskii
@ 2023-03-08 16:17 ` Tom Tromey
0 siblings, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2023-03-08 16:17 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: Tom Tromey, gdb-patches
>>>>> "Eli" == Eli Zaretskii <eliz@gnu.org> writes:
>> Date: Fri, 3 Mar 2023 14:11:59 -0700
>> X-Spam-Status: No, score=-5.6 required=5.0 tests=BAYES_00, DKIM_SIGNED,
>> DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,
>> SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.6
>> From: Tom Tromey via Gdb-patches <gdb-patches@sourceware.org>
>>
>> This series adss basic arithmetic for 128-bit integer types to gdb.
Eli> Can you tell where and in what situations will this be useful?
Right now, if the inferior uses 128-bit integer types, then 'print'
works but no other operations work. So for example, you cannot "print x + 1".
128-bit types are somewhat more common now. Several of the languages
gdb supports have them (at least C, C++, Rust, and Ada).
Eli> Also, don't we need to announce this, at least in NEWS?
I added this:
* GDB now has some support for integer types larger than 64 bits.
Tom
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 0/8] Arithmetic for 128-bit types
2023-03-03 21:11 [PATCH 0/8] Arithmetic for 128-bit types Tom Tromey
` (8 preceding siblings ...)
2023-03-04 7:04 ` [PATCH 0/8] Arithmetic for 128-bit types Eli Zaretskii
@ 2023-03-27 14:20 ` Tom Tromey
9 siblings, 0 replies; 15+ messages in thread
From: Tom Tromey @ 2023-03-27 14:20 UTC (permalink / raw)
To: Tom Tromey via Gdb-patches; +Cc: Tom Tromey
>>>>> "Tom" == Tom Tromey via Gdb-patches <gdb-patches@sourceware.org> writes:
Tom> This series adss basic arithmetic for 128-bit integer types to gdb.
Tom> This is done using GMP, which gdb already requires. While this
Tom> involves extra allocation, my view is that this hardly matters in most
Tom> expression contexts. However, if it does turn out to matter, we can
Tom> try to switch to GCC's wide-integer code.
Tom> While this series, or something like it, is necessary for supporting
Tom> 128-bit types, it is not sufficient. In particular, none of the
Tom> lexers or parsers are touched, and integers that are part of types
Tom> (e.g., range bounds) are not handled.
Tom> This series is based on my earlier series to clean up the GMP classes.
Tom> Regression tested on x86-64 Fedora 36.
I've rebased this and re-run the regression tests.
I'm going to check it in now.
Tom
^ permalink raw reply [flat|nested] 15+ messages in thread