[4/8] Add value_as_mpz and value_from_mpz

Message ID 20230303211207.1053037-5-tromey@adacore.com
State New
Headers
Series Arithmetic for 128-bit types |

Commit Message

Tom Tromey March 3, 2023, 9:12 p.m. UTC
  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(+)
  

Comments

Lancelot SIX March 8, 2023, 10:47 a.m. UTC | #1
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.
  
Tom Tromey March 8, 2023, 3:48 p.m. UTC | #2
>> +      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
  

Patch

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);