diff mbox series

[RFC,v4,10/15] GDB: Add concept of variable-size registers to the regcache

Message ID 20241102025635.586759-11-thiago.bauermann@linaro.org
State New
Headers show
Series gdbserver improvements for AArch64 SVE support | expand

Commit Message

Thiago Jung Bauermann Nov. 2, 2024, 2:56 a.m. UTC
The contents of variable-size registers are placed in a separate buffer,
because their size will only be known a while after the buffer is
created but some fixed-size registers (particularly the program counter)
need to be stored or fetched before then.  This is also why the state
related to variable-size registers is lazily initialised at the moment
it's first needed (by calling the initialize_variable_size_registers ()
method).

Simon suggested placing the variable-size registers at the end of the
existing contents buffer to avoid having to use a separate one.  I will
experiment with that idea and see if it simplifies the code.

The regcache now also stores the resolved type and size of the
variable-size registers.  Some places needed to be changed from calling
the register_size () function to calling the register_size () method
instead, because they may call them for variable-size registers.  The
frame-unwinding code doesn't have a regcache readily available, so the
register_size () function is changed to optionally accept a
frame_info_ptr which it uses to create a readonly_detached_regcache.

When debugging a remote target, if the regcache has variable-size
registers the maximum packet size may change with new values of
expedited registers.  Therefore, update the maximum packet size when
expedited registers are supplied, or load-early registers are fetched.

Finally, there are FIXMEs related to the const_casts needed to remove
the const from the "this" pointer when calling
reg_buffer::initialize_variable_size_registers ().  I'm still thinking
about what to do with them.  I tried the simple solution of changing the
calling methods to be non-const, but the change escalates quickly.
---
 gdb/aarch64-tdep.c |   9 +-
 gdb/eval.c         |  13 ++-
 gdb/findvar.c      |   5 +-
 gdb/frame.c        |  45 +++++---
 gdb/record-full.c  |   3 +-
 gdb/regcache.c     | 269 +++++++++++++++++++++++++++++++++++++--------
 gdb/regcache.h     |  39 ++++++-
 gdb/remote.c       |  62 +++++++++--
 gdb/value.c        |  16 ++-
 gdbsupport/tdesc.h |   4 +-
 10 files changed, 377 insertions(+), 88 deletions(-)
diff mbox series

Patch

diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 8a2a9b1e23c1..04442f22ea3f 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -1728,7 +1728,7 @@  pass_in_v (struct gdbarch *gdbarch,
     {
       int regnum = AARCH64_V0_REGNUM + info->nsrn;
       /* Enough space for a full vector register.  */
-      gdb::byte_vector reg (register_size (gdbarch, regnum), 0);
+      gdb::byte_vector reg (regcache->register_size (regnum), 0);
       gdb_assert (len <= reg.size ());
 
       info->argnum++;
@@ -2543,7 +2543,7 @@  aarch64_extract_return_value (struct type *type, struct regcache *regs,
 	{
 	  int regno = AARCH64_V0_REGNUM + i;
 	  /* Enough space for a full vector register.  */
-	  gdb::byte_vector buf (register_size (gdbarch, regno));
+	  gdb::byte_vector buf (regs->register_size (regno));
 	  gdb_assert (len <= buf.size ());
 
 	  aarch64_debug_printf
@@ -2657,7 +2657,7 @@  aarch64_store_return_value (struct type *type, struct regcache *regs,
 	{
 	  int regno = AARCH64_V0_REGNUM + i;
 	  /* Enough space for a full vector register.  */
-	  gdb::byte_vector tmpbuf (register_size (gdbarch, regno));
+	  gdb::byte_vector tmpbuf (regs->register_size (regno));
 	  gdb_assert (len <= tmpbuf.size ());
 
 	  aarch64_debug_printf
@@ -3308,7 +3308,8 @@  aarch64_pseudo_write_1 (gdbarch *gdbarch, const frame_info_ptr &next_frame,
      various 'scalar' pseudo registers to behavior like architectural
      writes, register width bytes are written the remainder are set to
      zero.  */
-  gdb::byte_vector raw_buf (register_size (gdbarch, raw_regnum), 0);
+  int raw_reg_size = register_size (gdbarch, raw_regnum, &next_frame);
+  gdb::byte_vector raw_buf (raw_reg_size, 0);
   static_assert (AARCH64_V0_REGNUM == AARCH64_SVE_Z0_REGNUM);
 
   gdb::array_view<gdb_byte> raw_view (raw_buf);
diff --git a/gdb/eval.c b/gdb/eval.c
index 457a4362289d..9f20ec557db3 100644
--- a/gdb/eval.c
+++ b/gdb/eval.c
@@ -1151,13 +1151,14 @@  eval_op_register (struct type *expect_type, struct expression *exp,
   if (regno == -1)
     error (_("Register $%s not available."), name);
 
-  /* In EVAL_AVOID_SIDE_EFFECTS mode, we only need to return
-     a value with the appropriate register type.  Unfortunately,
-     we don't have easy access to the type of user registers.
-     So for these registers, we fetch the register value regardless
-     of the evaluation mode.  */
+  /* In EVAL_AVOID_SIDE_EFFECTS mode, we only need to return a value with
+     the appropriate register type.  Unfortunately, we don't have easy
+     access to the type of user registers and variable-size registers.  So
+     for these registers, we fetch the register value regardless of the
+     evaluation mode.  */
   if (noside == EVAL_AVOID_SIDE_EFFECTS
-      && regno < gdbarch_num_cooked_regs (exp->gdbarch))
+      && regno < gdbarch_num_cooked_regs (exp->gdbarch)
+      && !register_is_variable_size (exp->gdbarch, regno))
     val = value::zero (register_type (exp->gdbarch, regno), not_lval);
   else
     val = value_of_register
diff --git a/gdb/findvar.c b/gdb/findvar.c
index d65bf2fc278c..1ecfd3558383 100644
--- a/gdb/findvar.c
+++ b/gdb/findvar.c
@@ -562,11 +562,12 @@  read_frame_register_value (value *value)
   LONGEST reg_offset = value->offset ();
   int regnum = value->regnum ();
   int len = type_length_units (check_typedef (value->type ()));
+  int reg_size = register_size (gdbarch, regnum, &next_frame);
 
   /* Skip registers wholly inside of REG_OFFSET.  */
-  while (reg_offset >= register_size (gdbarch, regnum))
+  while (reg_offset >= reg_size)
     {
-      reg_offset -= register_size (gdbarch, regnum);
+      reg_offset -= reg_size;
       regnum++;
     }
 
diff --git a/gdb/frame.c b/gdb/frame.c
index 663ff7fa773b..0f480ce75b09 100644
--- a/gdb/frame.c
+++ b/gdb/frame.c
@@ -1322,12 +1322,24 @@  frame_unwind_register_value (const frame_info_ptr &next_frame, int regnum)
 	    gdb_printf (&debug_file, " lazy");
 	  else if (value->entirely_available ())
 	    {
-	      int i;
+	      int i, reg_size;
 	      gdb::array_view<const gdb_byte> buf = value->contents ();
 
 	      gdb_printf (&debug_file, " bytes=");
 	      gdb_printf (&debug_file, "[");
-	      for (i = 0; i < register_size (gdbarch, regnum); i++)
+
+	      if (register_is_variable_size (gdbarch, regnum))
+		/* To get the size of a variable-size register, we need to
+		   call frame_save_as_regcache () so that we can call
+		   regcache::register_size ().  Unfortunatlly the former
+		   ends up calling this function so we enter into an
+		   infinite recursion.  So just assume that the value has
+		   the correct size.  */
+		reg_size = buf.size ();
+	      else
+		reg_size = register_size (gdbarch, regnum);
+
+	      for (i = 0; i < reg_size; i++)
 		gdb_printf (&debug_file, "%02x", buf[i]);
 	      gdb_printf (&debug_file, "]");
 	    }
@@ -1446,7 +1458,7 @@  put_frame_register (const frame_info_ptr &next_frame, int regnum,
   int unavail;
   enum lval_type lval;
   CORE_ADDR addr;
-  int size = register_size (gdbarch, regnum);
+  int size = register_size (gdbarch, regnum, &next_frame);
 
   gdb_assert (buf.size () == size);
 
@@ -1462,8 +1474,9 @@  put_frame_register (const frame_info_ptr &next_frame, int regnum,
 	break;
       }
     case lval_register:
-      /* Not sure if that's always true... but we have a problem if not.  */
-      gdb_assert (size == register_size (gdbarch, realnum));
+      if (regnum != realnum)
+	/* Not sure if that's always true... but we have a problem if not.  */
+	gdb_assert (size == register_size (gdbarch, realnum));
 
       if (realnum < gdbarch_num_regs (gdbarch)
 	  || !gdbarch_pseudo_register_write_p (gdbarch))
@@ -1509,9 +1522,9 @@  get_frame_register_bytes (const frame_info_ptr &next_frame, int regnum,
   gdbarch *gdbarch = frame_unwind_arch (next_frame);
 
   /* Skip registers wholly inside of OFFSET.  */
-  while (offset >= register_size (gdbarch, regnum))
+  while (offset >= register_size (gdbarch, regnum, &next_frame))
     {
-      offset -= register_size (gdbarch, regnum);
+      offset -= register_size (gdbarch, regnum, &next_frame);
       regnum++;
     }
 
@@ -1521,7 +1534,7 @@  get_frame_register_bytes (const frame_info_ptr &next_frame, int regnum,
   int numregs = gdbarch_num_cooked_regs (gdbarch);
   for (int i = regnum; i < numregs; i++)
     {
-      int thissize = register_size (gdbarch, i);
+      int thissize = register_size (gdbarch, i, &next_frame);
 
       if (thissize == 0)
 	break;	/* This register is not available on this architecture.  */
@@ -1535,10 +1548,10 @@  get_frame_register_bytes (const frame_info_ptr &next_frame, int regnum,
   /* Copy the data.  */
   while (!buffer.empty ())
     {
-      int curr_len = std::min<int> (register_size (gdbarch, regnum) - offset,
-				    buffer.size ());
+      int reg_size = register_size (gdbarch, regnum, &next_frame);
+      int curr_len = std::min<int> (reg_size - offset, buffer.size ());
 
-      if (curr_len == register_size (gdbarch, regnum))
+      if (curr_len == reg_size)
 	{
 	  enum lval_type lval;
 	  CORE_ADDR addr;
@@ -1586,19 +1599,19 @@  put_frame_register_bytes (const frame_info_ptr &next_frame, int regnum,
   gdbarch *gdbarch = frame_unwind_arch (next_frame);
 
   /* Skip registers wholly inside of OFFSET.  */
-  while (offset >= register_size (gdbarch, regnum))
+  while (offset >= register_size (gdbarch, regnum, &next_frame))
     {
-      offset -= register_size (gdbarch, regnum);
+      offset -= register_size (gdbarch, regnum, &next_frame);
       regnum++;
     }
 
   /* Copy the data.  */
   while (!buffer.empty ())
     {
-      int curr_len = std::min<int> (register_size (gdbarch, regnum) - offset,
-				    buffer.size ());
+      int reg_size = register_size (gdbarch, regnum, &next_frame);
+      int curr_len = std::min<int> (reg_size - offset, buffer.size ());
 
-      if (curr_len == register_size (gdbarch, regnum))
+      if (curr_len == reg_size)
 	put_frame_register (next_frame, regnum, buffer.slice (0, curr_len));
       else
 	{
diff --git a/gdb/record-full.c b/gdb/record-full.c
index 8e4915abd083..0ce370ae32bf 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -399,12 +399,11 @@  static inline struct record_full_entry *
 record_full_reg_alloc (struct regcache *regcache, int regnum)
 {
   struct record_full_entry *rec;
-  struct gdbarch *gdbarch = regcache->arch ();
 
   rec = XCNEW (struct record_full_entry);
   rec->type = record_full_reg;
   rec->u.reg.num = regnum;
-  rec->u.reg.len = register_size (gdbarch, regnum);
+  rec->u.reg.len = regcache->register_size (regnum);
   if (rec->u.reg.len > sizeof (rec->u.reg.u.buf))
     rec->u.reg.u.ptr = (gdb_byte *) xmalloc (rec->u.reg.len);
 
diff --git a/gdb/regcache.c b/gdb/regcache.c
index 61289d46f4b1..dfcfffb02aa0 100644
--- a/gdb/regcache.c
+++ b/gdb/regcache.c
@@ -51,7 +51,7 @@  struct regcache_descr
      redundant information - if the PC is constructed from two
      registers then those registers and not the PC lives in the raw
      cache.  */
-  long sizeof_raw_registers = 0;
+  long sizeof_fixed_size_raw_registers = 0;
 
   /* The cooked register space.  Each cooked register in the range
      [0..NR_RAW_REGISTERS) is direct-mapped onto the corresponding raw
@@ -60,7 +60,7 @@  struct regcache_descr
      both raw registers and memory by the architecture methods
      gdbarch_pseudo_register_read and gdbarch_pseudo_register_write.  */
   int nr_cooked_registers = 0;
-  long sizeof_cooked_registers = 0;
+  long sizeof_fixed_size_cooked_registers = 0;
 
   /* Offset and size (in 8 bit bytes), of each register in the
      register cache.  All registers (including those in the range
@@ -69,9 +69,16 @@  struct regcache_descr
   long *register_offset = nullptr;
   long *sizeof_register = nullptr;
 
-  /* Registers that need to be loaded early in the register cache.  */
+  /* Registers that need to be loaded early in the register cache, because
+     variable-size registers depend on them to calculate their size.  */
   std::set<int> load_early_regs;
 
+  /* Vector indicating whether a given register is variable-size.  */
+  bool *register_is_variable_size = nullptr;
+
+  /* Does the regcache contains any variable-size register?  */
+  bool has_variable_size_registers = false;
+
   /* Cached table containing the type of each register.  */
   struct type **register_type = nullptr;
 };
@@ -119,26 +126,52 @@  init_regcache_descr (struct gdbarch *gdbarch)
       = GDBARCH_OBSTACK_CALLOC (gdbarch, descr->nr_cooked_registers, long);
     descr->register_offset
       = GDBARCH_OBSTACK_CALLOC (gdbarch, descr->nr_cooked_registers, long);
+    descr->register_is_variable_size
+      = GDBARCH_OBSTACK_CALLOC (gdbarch, descr->nr_cooked_registers, bool);
+
     for (i = 0; i < gdbarch_num_regs (gdbarch); i++)
       {
-	descr->sizeof_register[i] = descr->register_type[i]->length ();
-	descr->register_offset[i] = offset;
-	offset += descr->sizeof_register[i];
+	descr->register_is_variable_size[i]
+	  = is_dynamic_type (descr->register_type[i]);
+	if (descr->register_is_variable_size[i])
+	  {
+	    descr->sizeof_register[i] = -1;
+	    descr->register_offset[i] = -1;
+	    descr->has_variable_size_registers = true;
+	  }
+	else
+	  {
+	    descr->sizeof_register[i] = descr->register_type[i]->length ();
+	    descr->register_offset[i] = offset;
+	    offset += descr->sizeof_register[i];
+	  }
 
 	if (tdesc_register_is_early_load (gdbarch, i))
 	  descr->load_early_regs.insert (i);
       }
+
     /* Set the real size of the raw register cache buffer.  */
-    descr->sizeof_raw_registers = offset;
+    descr->sizeof_fixed_size_raw_registers = offset;
 
     for (; i < descr->nr_cooked_registers; i++)
       {
-	descr->sizeof_register[i] = descr->register_type[i]->length ();
-	descr->register_offset[i] = offset;
-	offset += descr->sizeof_register[i];
+	descr->register_is_variable_size[i]
+	  = is_dynamic_type (descr->register_type[i]);
+	if (descr->register_is_variable_size[i])
+	  {
+	    descr->sizeof_register[i] = -1;
+	    descr->register_offset[i] = -1;
+	    descr->has_variable_size_registers = true;
+	  }
+	else
+	  {
+	    descr->sizeof_register[i] = descr->register_type[i]->length ();
+	    descr->register_offset[i] = offset;
+	    offset += descr->sizeof_register[i];
+	  }
       }
     /* Set the real size of the readonly register cache buffer.  */
-    descr->sizeof_cooked_registers = offset;
+    descr->sizeof_fixed_size_cooked_registers = offset;
   }
 
   return descr;
@@ -160,27 +193,60 @@  regcache_descr (struct gdbarch *gdbarch)
 /* Utility functions returning useful register attributes stored in
    the regcache descr.  */
 
+/* See gdb/regcache.h.  */
+
 struct type *
 register_type (struct gdbarch *gdbarch, int regnum)
 {
   struct regcache_descr *descr = regcache_descr (gdbarch);
 
   gdb_assert (regnum >= 0 && regnum < descr->nr_cooked_registers);
-  return descr->register_type[regnum];
+
+  struct type *type = descr->register_type[regnum];
+
+  if (descr->register_is_variable_size[regnum])
+    {
+      frame_info_ptr current_frame = get_current_frame ();
+      type = resolve_dynamic_type (type, {}, 0, &current_frame);
+    }
+
+  return type;
 }
 
-/* Utility functions returning useful register attributes stored in
-   the regcache descr.  */
+/* See gdb/regcache.h.  */
 
 int
-register_size (struct gdbarch *gdbarch, int regnum)
+register_size (struct gdbarch *gdbarch, int regnum,
+	       const frame_info_ptr *next_frame)
 {
   struct regcache_descr *descr = regcache_descr (gdbarch);
-  int size;
 
-  gdb_assert (regnum >= 0 && regnum < gdbarch_num_cooked_regs (gdbarch));
-  size = descr->sizeof_register[regnum];
-  return size;
+  gdb_assert (regnum >= 0);
+  gdb_assert (regnum < descr->nr_cooked_registers);
+
+  if (descr->register_is_variable_size[regnum])
+    {
+      gdb_assert (next_frame != nullptr);
+
+      std::unique_ptr<readonly_detached_regcache> regcache =
+	frame_save_as_regcache (get_prev_frame (*next_frame));
+      return regcache->register_size (regnum);
+    }
+
+  return descr->sizeof_register[regnum];
+}
+
+/* See gdb/regcache.h.  */
+
+bool
+register_is_variable_size (struct gdbarch *gdbarch, int regnum)
+{
+  struct regcache_descr *descr = regcache_descr (gdbarch);
+
+  gdb_assert (regnum >= 0);
+  gdb_assert (regnum < descr->nr_cooked_registers);
+
+  return descr->register_is_variable_size[regnum];
 }
 
 /* See gdbsupport/common-regcache.h.  */
@@ -188,7 +254,47 @@  register_size (struct gdbarch *gdbarch, int regnum)
 int
 reg_buffer::register_size (int regnum) const
 {
-  return ::register_size (this->arch (), regnum);
+  gdb_assert (regnum >= 0);
+  gdb_assert (regnum < m_descr->nr_cooked_registers);
+
+  int size;
+
+  if (m_descr->register_is_variable_size[regnum])
+    {
+      if (!m_variable_size_registers)
+	const_cast<reg_buffer *> (this)
+	  ->initialize_variable_size_registers (); // FIXME: Remove cast.
+
+      size = m_variable_size_register_sizeof[regnum];
+    }
+  else
+    size = m_descr->sizeof_register[regnum];
+
+  return size;
+}
+
+/* See gdb/regcache.h.  */
+
+struct type *
+reg_buffer::register_type (int regnum) const
+{
+  gdb_assert (regnum >= 0);
+  gdb_assert (regnum < m_descr->nr_cooked_registers);
+
+  struct type *type;
+
+  if (m_descr->register_is_variable_size[regnum])
+    {
+      if (!m_variable_size_registers)
+	const_cast<reg_buffer *> (this)
+	  ->initialize_variable_size_registers (); // FIXME: Remove cast.
+
+      type = m_variable_size_register_type[regnum];
+    }
+  else
+    type = m_descr->register_type[regnum];
+
+  return type;
 }
 
 reg_buffer::reg_buffer (gdbarch *gdbarch, bool has_pseudo)
@@ -202,13 +308,13 @@  reg_buffer::reg_buffer (gdbarch *gdbarch, bool has_pseudo)
      REG_VALID.  */
   if (has_pseudo)
     {
-      m_registers.reset (new gdb_byte[m_descr->sizeof_cooked_registers]);
+      m_fixed_size_registers.reset (new gdb_byte[m_descr->sizeof_fixed_size_cooked_registers]);
       m_register_status.reset
 	(new register_status[m_descr->nr_cooked_registers] ());
     }
   else
     {
-      m_registers.reset (new gdb_byte[m_descr->sizeof_raw_registers]);
+      m_fixed_size_registers.reset (new gdb_byte[m_descr->sizeof_fixed_size_raw_registers]);
       m_register_status.reset
 	(new register_status[gdbarch_num_regs (gdbarch)] ());
     }
@@ -237,9 +343,6 @@  reg_buffer::arch () const
   return m_descr->gdbarch;
 }
 
-/* Utility functions returning useful register attributes stored in
-   the regcache descr.  */
-
 /* See regcache.h.  */
 
 bool
@@ -281,6 +384,55 @@  reg_buffer::load_early_registers ()
   return m_descr->load_early_regs;
 }
 
+/* See regcache.h.  */
+
+void
+reg_buffer::initialize_variable_size_registers ()
+{
+  unsigned long total_size = 0;
+
+  /* We need load early registers to resolve types of variable-size
+     registers.  */
+  if (!this->fetch_load_early_registers ())
+    error (_("Missing register contents for variable-size registers."));
+
+  m_variable_size_register_type.resize (m_descr->nr_cooked_registers);
+  m_variable_size_register_sizeof.resize (m_descr->nr_cooked_registers);
+  m_variable_size_register_offset.resize (m_descr->nr_cooked_registers);
+
+  for (unsigned int i = 0; i < m_descr->nr_cooked_registers; i++)
+    {
+      if (!m_descr->register_is_variable_size[i])
+	{
+	  m_variable_size_register_type[i] = nullptr;
+	  m_variable_size_register_sizeof[i] = -1;
+	  m_variable_size_register_offset[i] = -1;
+	  continue;
+	}
+
+      m_variable_size_register_type[i]
+	= resolve_dynamic_type (m_descr->register_type[i], {},
+				/* Unused address.  */ 0, nullptr, this);
+
+      ULONGEST size = m_variable_size_register_type[i]->length ();
+      gdb_assert (size != 0);
+      m_variable_size_register_sizeof[i] = size;
+      m_variable_size_register_offset[i] = total_size;
+      m_register_status[i] = REG_UNKNOWN;
+      total_size += size;
+    }
+
+  m_variable_size_registers.reset (new gdb_byte[total_size]);
+}
+
+/* See regcache.h.  */
+
+bool
+reg_buffer::has_variable_size_registers ()
+{
+  return m_descr->has_variable_size_registers;
+}
+
 /* Helper for reg_buffer::register_buffer.  */
 
 template<typename ElemType>
@@ -288,8 +440,19 @@  gdb::array_view<ElemType>
 reg_buffer::register_buffer (int regnum) const
 {
   assert_regnum (regnum);
-  ElemType *start = &m_registers[m_descr->register_offset[regnum]];
-  int size = m_descr->sizeof_register[regnum];
+  ElemType *start;
+
+  if (m_descr->register_is_variable_size[regnum])
+    {
+      if (!m_variable_size_registers)
+	const_cast<reg_buffer *>(this)->initialize_variable_size_registers (); // FIXME: Remove cast.
+
+      start = &m_variable_size_registers[m_variable_size_register_offset[regnum]];
+    }
+  else
+    start = &m_fixed_size_registers[m_descr->register_offset[regnum]];
+
+  int size = register_size (regnum);
   return gdb::array_view<ElemType> (start, size);
 }
 
@@ -317,9 +480,14 @@  reg_buffer::save (register_read_ftype cooked_read)
   /* It should have pseudo registers.  */
   gdb_assert (m_has_pseudo);
   /* Clear the dest.  */
-  memset (m_registers.get (), 0, m_descr->sizeof_cooked_registers);
+  memset (m_fixed_size_registers.get (), 0, m_descr->sizeof_fixed_size_cooked_registers);
   memset (m_register_status.get (), REG_UNKNOWN, m_descr->nr_cooked_registers);
 
+  /* For data related to variable-size registers, we only need to reset
+     their buffer at this point.  Other data will be resized/re-set by
+     initialize_variable_size_registers ().  */
+  m_variable_size_registers.reset ();
+
   /* Save load early registers first.  */
   for (int regnum : m_descr->load_early_regs)
     {
@@ -379,6 +547,12 @@  regcache::restore (readonly_detached_regcache *src)
 	continue;
 
       cooked_write (regnum, src->register_buffer (regnum));
+
+      if (m_variable_size_registers)
+	/* For data related to variable-size registers, we only need to reset
+	   their buffer at this point.  Other data will be resized/re-set by
+	   initialize_variable_size_registers ().  */
+	m_variable_size_registers.reset ();
     }
 
   /* Copy over any registers, being careful to only restore those that
@@ -709,7 +883,7 @@  register_status
 readable_regcache::raw_read (int regnum, gdb::array_view<gdb_byte> dst)
 {
   assert_regnum (regnum);
-  gdb_assert (dst.size () == m_descr->sizeof_register[regnum]);
+  gdb_assert (dst.size () == register_size (regnum));
 
   raw_update (regnum);
 
@@ -725,7 +899,7 @@  register_status
 readable_regcache::raw_read (int regnum, gdb_byte *dst)
 {
   assert_regnum (regnum);
-  int size = m_descr->sizeof_register[regnum];
+  int size = register_size (regnum);
   return raw_read (regnum, gdb::make_array_view (dst, size));
 }
 
@@ -741,7 +915,7 @@  enum register_status
 readable_regcache::raw_read (int regnum, T *val)
 {
   assert_regnum (regnum);
-  size_t size = m_descr->sizeof_register[regnum];
+  size_t size = register_size (regnum);
   gdb_byte *buf = (gdb_byte *) alloca (size);
   auto view = gdb::make_array_view (buf, size);
   register_status status = raw_read (regnum, view);
@@ -776,7 +950,7 @@  regcache::raw_write (int regnum, T val)
 {
   assert_regnum (regnum);
 
-  int size = m_descr->sizeof_register[regnum];
+  int size = register_size (regnum);
   gdb_byte *buf = (gdb_byte *) alloca (size);
   auto view = gdb::make_array_view (buf, size);
   store_integer (view, gdbarch_byte_order (m_descr->gdbarch), val);
@@ -815,7 +989,7 @@  readable_regcache::cooked_read (int regnum, gdb::array_view<gdb_byte> dst)
   if (regnum < num_raw_registers ())
     return raw_read (regnum, dst);
 
-  gdb_assert (dst.size () == m_descr->sizeof_register[regnum]);
+  gdb_assert (dst.size () == register_size (regnum));
 
   if (m_has_pseudo && m_register_status[regnum] != REG_UNKNOWN)
     {
@@ -857,7 +1031,7 @@  readable_regcache::cooked_read (int regnum, gdb_byte *dst)
   gdb_assert (regnum >= 0);
   gdb_assert (regnum < m_descr->nr_cooked_registers);
 
-  int size = m_descr->sizeof_register[regnum];
+  int size = register_size (regnum);
   return cooked_read (regnum, gdb::make_array_view (dst, size));
 }
 
@@ -871,8 +1045,12 @@  readable_regcache::cooked_read_value (int regnum)
       || (m_has_pseudo && m_register_status[regnum] != REG_UNKNOWN)
       || !gdbarch_pseudo_register_read_value_p (m_descr->gdbarch))
     {
+      /* Provide the register type if its size is fixed to avoid infinite
+	 recursion in the case of variable-size registers.  */
+      struct type *type = (m_descr->register_is_variable_size[regnum] ?
+			   nullptr : m_descr->register_type[regnum]);
       value *result = value::allocate_register
-	(get_next_frame_sentinel_okay (get_current_frame ()), regnum);
+	(get_next_frame_sentinel_okay (get_current_frame ()), regnum, type);
 
       /* It is more efficient in general to do this delegation in this
 	 direction than in the other one, even though the value-based
@@ -902,7 +1080,7 @@  enum register_status
 readable_regcache::cooked_read (int regnum, T *val)
 {
   gdb_assert (regnum >= 0 && regnum < m_descr->nr_cooked_registers);
-  size_t size = m_descr->sizeof_register[regnum];
+  size_t size = register_size (regnum);
   gdb_byte *buf = (gdb_byte *) alloca (size);
   auto view = gdb::make_array_view (buf, size);
   register_status status = cooked_read (regnum, view);
@@ -936,7 +1114,7 @@  regcache::cooked_write (int regnum, T val)
   gdb_assert (regnum >= 0);
   gdb_assert (regnum < m_descr->nr_cooked_registers);
 
-  int size = m_descr->sizeof_register[regnum];
+  int size = register_size (regnum);
   gdb_byte *buf = (gdb_byte *) alloca (size);
   auto view = gdb::make_array_view (buf, size);
   store_integer (view, gdbarch_byte_order (m_descr->gdbarch), val);
@@ -955,7 +1133,7 @@  void
 regcache::raw_write (int regnum, gdb::array_view<const gdb_byte> src)
 {
   assert_regnum (regnum);
-  gdb_assert (src.size () == m_descr->sizeof_register[regnum]);
+  gdb_assert (src.size () == register_size (regnum));
 
   /* On the sparc, writing %g0 is a no-op, so we don't even want to
      change the registers array if something writes to this register.  */
@@ -992,7 +1170,7 @@  regcache::raw_write (int regnum, const gdb_byte *src)
 {
   assert_regnum (regnum);
 
-  int size = m_descr->sizeof_register[regnum];
+  int size = register_size (regnum);
   raw_write (regnum, gdb::make_array_view (src, size));
 }
 
@@ -1023,7 +1201,7 @@  regcache::cooked_write (int regnum, const gdb_byte *src)
   gdb_assert (regnum >= 0);
   gdb_assert (regnum < m_descr->nr_cooked_registers);
 
-  int size = m_descr->sizeof_register[regnum];
+  int size = register_size (regnum);
   return cooked_write (regnum, gdb::make_array_view (src, size));
 }
 
@@ -1235,6 +1413,11 @@  reg_buffer::raw_supply (int regnum, gdb::array_view<const gdb_byte> src)
     {
       copy (src, dst);
       m_register_status[regnum] = REG_VALID;
+
+      if (this->is_load_early_register (regnum)
+	  && memcmp (src.data (), dst.data (), dst.size ()))
+	/* Invalidate variable-size registers.  */
+	this->initialize_variable_size_registers ();
     }
   else
     {
@@ -1253,7 +1436,7 @@  reg_buffer::raw_supply (int regnum, const void *src)
 {
   assert_regnum (regnum);
 
-  int size = m_descr->sizeof_register[regnum];
+  int size = register_size (regnum);
   raw_supply (regnum, gdb::make_array_view ((const gdb_byte *) src, size));
 }
 
@@ -1297,7 +1480,7 @@  reg_buffer::raw_collect (int regnum, void *dst) const
 {
   assert_regnum (regnum);
 
-  int size = m_descr->sizeof_register[regnum];
+  int size = register_size (regnum);
   return raw_collect (regnum, gdb::make_array_view ((gdb_byte *) dst, size));
 }
 
@@ -1372,7 +1555,7 @@  regcache::transfer_regset (const struct regset *regset, int regbase,
 	regno += regbase;
 
       if (slot_size == 0 && regno != REGCACHE_MAP_SKIP)
-	slot_size = m_descr->sizeof_register[regno];
+	slot_size = register_size (regnum);
 
       if (regno == REGCACHE_MAP_SKIP
 	  || (regnum != -1
diff --git a/gdb/regcache.h b/gdb/regcache.h
index 5c28fec4af11..6b6687b9f349 100644
--- a/gdb/regcache.h
+++ b/gdb/regcache.h
@@ -165,10 +165,15 @@  extern bool regcache_map_supplies (const struct regcache_map_entry *map,
 extern struct type *register_type (struct gdbarch *gdbarch, int regnum);
 
 
-/* Return the size of register REGNUM.  All registers should have only
-   one size.  */
+/* Return the size of register REGNUM.  FRAME is needed in case regnum is a
+   variable-size register.  */
    
-extern int register_size (struct gdbarch *gdbarch, int regnum);
+extern int register_size (struct gdbarch *gdbarch, int regnum,
+			  const frame_info_ptr *next_frame = nullptr);
+
+/* Return whether REGNUM  is a variable-size register.  */
+
+extern bool register_is_variable_size (struct gdbarch *gdbarch, int regnum);
 
 using register_read_ftype
   = gdb::function_view<register_status (int, gdb::array_view<gdb_byte>)>;
@@ -266,6 +271,12 @@  class reg_buffer : public reg_buffer_common
   /* Return set of regnums which need to be loaded before other registers.  */
   const std::set<int> &load_early_registers ();
 
+  /* Whether any register in this regcache has a dynamic type.  */
+  bool has_variable_size_registers ();
+
+  /* Return type of register REGNUM.  */
+  struct type *register_type (int regnum) const;
+
   /* See gdbsupport/common-regcache.h.  */
   int register_size (int regnum) const override;
 
@@ -279,6 +290,10 @@  class reg_buffer : public reg_buffer_common
      Return false if they aren't.  */
   virtual bool fetch_load_early_registers ();
 
+  /* Initialize (or reset) information about variable-size registers in this
+     reg_buffer.  */
+  void initialize_variable_size_registers ();
+
   /* Return a view on register REGNUM's buffer cache.  */
   template <typename ElemType>
   gdb::array_view<ElemType> register_buffer (int regnum) const;
@@ -293,11 +308,25 @@  class reg_buffer : public reg_buffer_common
   struct regcache_descr *m_descr;
 
   bool m_has_pseudo;
-  /* The register buffers.  */
-  std::unique_ptr<gdb_byte[]> m_registers;
+
+  /* The fixed-size register buffers.  */
+  std::unique_ptr<gdb_byte[]> m_fixed_size_registers;
+
+  /* The variable-size register buffers (if any).  */
+  std::unique_ptr<gdb_byte[]> m_variable_size_registers;
+
   /* Register cache status.  */
   std::unique_ptr<register_status[]> m_register_status;
 
+  /* The resolved types for variable-size registers (if any).  */
+  std::vector<struct type *> m_variable_size_register_type;
+
+  /* The size of resolved types for variable-size registers (if any).  */
+  std::vector<long> m_variable_size_register_sizeof;
+
+  /* The offset of resolved types for variable-size registers (if any).  */
+  std::vector<long> m_variable_size_register_offset;
+
   friend class regcache;
   friend class detached_regcache;
 };
diff --git a/gdb/remote.c b/gdb/remote.c
index 0217c05bce52..0930977d4304 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -471,6 +471,8 @@  struct packet_reg
      at present.  */
 };
 
+struct remote_state;
+
 struct remote_arch_state
 {
   explicit remote_arch_state (struct gdbarch *gdbarch);
@@ -493,6 +495,10 @@  struct remote_arch_state
   /* This is the maximum size (in chars) of a non read/write packet.
      It is also used as a cap on the size of read/write packets.  */
   long remote_packet_size;
+
+  /* Make sure the maximum packet size reflects current size of variable-size
+     registers.  */
+  void update_packet_size (const struct regcache *regcache, remote_state *rs);
 };
 
 /* Description of the remote protocol state for the currently
@@ -1897,7 +1903,8 @@  show_remote_exec_file (struct ui_file *file, int from_tty,
 }
 
 static int
-map_regcache_remote_table (struct gdbarch *gdbarch, struct packet_reg *regs)
+map_regcache_remote_table (struct gdbarch *gdbarch, struct packet_reg *regs,
+			   const struct regcache *regcache = nullptr)
 {
   int regnum, num_remote_regs, offset;
   struct packet_reg **remote_regs;
@@ -1905,8 +1912,15 @@  map_regcache_remote_table (struct gdbarch *gdbarch, struct packet_reg *regs)
   for (regnum = 0; regnum < gdbarch_num_regs (gdbarch); regnum++)
     {
       struct packet_reg *r = &regs[regnum];
+      bool skip_register;
 
-      if (register_size (gdbarch, regnum) == 0)
+      if (regcache == nullptr)
+	skip_register = (register_is_variable_size (gdbarch, regnum)
+			 || register_size (gdbarch, regnum) == 0);
+      else
+	skip_register = regcache->register_size (regnum) == 0;
+
+      if (skip_register)
 	/* Do not try to fetch zero-sized (placeholder) registers.  */
 	r->pnum = -1;
       else
@@ -1934,7 +1948,10 @@  map_regcache_remote_table (struct gdbarch *gdbarch, struct packet_reg *regs)
     {
       remote_regs[regnum]->in_g_packet = 1;
       remote_regs[regnum]->offset = offset;
-      offset += register_size (gdbarch, remote_regs[regnum]->regnum);
+      if (regcache == nullptr)
+	offset += register_size (gdbarch, remote_regs[regnum]->regnum);
+      else
+	offset += regcache->register_size (remote_regs[regnum]->regnum);
     }
 
   return offset;
@@ -1995,6 +2012,29 @@  remote_arch_state::remote_arch_state (struct gdbarch *gdbarch)
     this->remote_packet_size = (this->sizeof_g_packet * 2 + 32);
 }
 
+/* See remote_arch_state class declaration.  */
+
+void
+remote_arch_state::update_packet_size (const struct regcache *regcache,
+				       remote_state *rs)
+{
+  /* Record the maximum possible size of the g packet - it may turn out
+     to be smaller.  */
+  this->sizeof_g_packet
+    = map_regcache_remote_table (regcache->arch (), this->regs.get (),
+				 regcache);
+
+  this->remote_packet_size = 400 - 1;
+
+  if (this->sizeof_g_packet > (this->remote_packet_size - 32) / 2)
+    this->remote_packet_size = this->sizeof_g_packet * 2 + 32;
+
+  /* Make sure that the packet buffer is plenty big enough for
+     this architecture.  */
+  if (rs->buf.size () < this->remote_packet_size)
+    rs->buf.resize (2 * this->remote_packet_size);
+}
+
 /* Get a pointer to the current remote target.  If not connected to a
    remote target, return NULL.  */
 
@@ -8553,6 +8593,10 @@  remote_target::supply_expedited_regs (struct regcache *regcache,
       regcache->raw_supply (reg.num, reg.data);
       rs->last_seen_expedited_registers.insert (reg.num);
     }
+
+  if (regcache->has_variable_size_registers ())
+    get_remote_state ()->get_remote_arch_state (regcache->arch ())
+      ->update_packet_size (regcache, &m_remote_state);
 }
 
 /* Called when it is decided that STOP_REPLY holds the info of the
@@ -8979,6 +9023,9 @@  remote_target::fetch_early_registers (struct regcache *regcache)
       if (res == 0)
 	error (_("Could not load early register %d using p packet."), regnum);
     }
+
+  get_remote_state ()->get_remote_arch_state (regcache->arch ())
+    ->update_packet_size (regcache, &m_remote_state);
 }
 
 /* Fetch the registers included in the target's 'g' packet.  */
@@ -9054,7 +9101,7 @@  remote_target::process_g_packet (struct regcache *regcache)
       for (i = 0; i < gdbarch_num_regs (gdbarch); i++)
 	{
 	  long offset = rsa->regs[i].offset;
-	  long reg_size = register_size (gdbarch, i);
+	  long reg_size = regcache->register_size (i);
 
 	  if (rsa->regs[i].pnum == -1)
 	    continue;
@@ -9102,7 +9149,7 @@  remote_target::process_g_packet (struct regcache *regcache)
   for (i = 0; i < gdbarch_num_regs (gdbarch); i++)
     {
       struct packet_reg *r = &rsa->regs[i];
-      long reg_size = register_size (gdbarch, i);
+      long reg_size = regcache->register_size (i);
 
       if (r->in_g_packet)
 	{
@@ -9240,7 +9287,8 @@  remote_target::store_register_using_P (const struct regcache *regcache,
   struct remote_state *rs = get_remote_state ();
   /* Try storing a single register.  */
   char *buf = rs->buf.data ();
-  gdb_byte *regp = (gdb_byte *) alloca (register_size (gdbarch, reg->regnum));
+  int reg_size = regcache->register_size (reg->regnum);
+  gdb_byte *regp = (gdb_byte *) alloca (reg_size);
   char *p;
 
   if (m_features.packet_support (PACKET_P) == PACKET_DISABLE)
@@ -9252,7 +9300,7 @@  remote_target::store_register_using_P (const struct regcache *regcache,
   xsnprintf (buf, get_remote_packet_size (), "P%s=", phex_nz (reg->pnum, 0));
   p = buf + strlen (buf);
   regcache->raw_collect (reg->regnum, regp);
-  bin2hex (regp, p, register_size (gdbarch, reg->regnum));
+  bin2hex (regp, p, reg_size);
   putpkt (rs->buf);
   getpkt (&rs->buf);
 
diff --git a/gdb/value.c b/gdb/value.c
index d9b3c6ece04c..2b88279bd845 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -4071,12 +4071,24 @@  value::fetch_lazy_register ()
 
 	  if (new_val->entirely_available ())
 	    {
-	      int i;
+	      int i, reg_size;
 	      gdb::array_view<const gdb_byte> buf = new_val->contents ();
 
 	      gdb_printf (&debug_file, " bytes=");
 	      gdb_printf (&debug_file, "[");
-	      for (i = 0; i < register_size (gdbarch, regnum); i++)
+
+	      if (register_is_variable_size (gdbarch, regnum))
+		/* To get the size of a variable-size register, we need to
+		   call frame_save_as_regcache () so that we can call
+		   regcache::register_size ().  Unfortunatlly the former
+		   ends up calling this function so we enter into an
+		   infinite recursion.  So just assume that the value has
+		   the correct size.  */
+		reg_size = buf.size ();
+	      else
+		reg_size = register_size (gdbarch, regnum);
+
+	      for (i = 0; i < reg_size; i++)
 		gdb_printf (&debug_file, "%02x", buf[i]);
 	      gdb_printf (&debug_file, "]");
 	    }
diff --git a/gdbsupport/tdesc.h b/gdbsupport/tdesc.h
index 5f20e6ab8627..74c5ff4deaf1 100644
--- a/gdbsupport/tdesc.h
+++ b/gdbsupport/tdesc.h
@@ -115,7 +115,9 @@  struct tdesc_reg : tdesc_element
   /* The target-described type corresponding to TYPE, if found.  */
   struct tdesc_type *tdesc_type;
 
-  /* Whether this register needs to be loaded early in GDB's regcache.
+  /* Whether this register needs to be loaded early in GDB's regcache,
+     because it is used to evaluate the DWARF expression giving the size of
+     another register.
 
      In addition, if true gdbserver will send it as an expedited register
      in stop replies.  */