diff --git a/gdb/features/gdb-target.dtd b/gdb/features/gdb-target.dtd
index d07703fca8b6..d38ca6f90bce 100644
--- a/gdb/features/gdb-target.dtd
+++ b/gdb/features/gdb-target.dtd
@@ -20,7 +20,7 @@
 <!ELEMENT compatible	(#PCDATA)>
 
 <!ELEMENT feature
-	((vector | flags | struct | union )*, reg*)>
+	(architecture?, (vector | flags | struct | union )*, reg*)>
 <!ATTLIST feature
 	name		ID	#REQUIRED>
 
@@ -34,11 +34,22 @@
 	group		CDATA	#IMPLIED
 	>
 
-<!ELEMENT vector	EMPTY>
+<!ELEMENT vector	(count?)>
 <!ATTLIST vector
 	id		CDATA	#REQUIRED
 	type		CDATA	#REQUIRED
-	count		CDATA	#REQUIRED>
+	count		CDATA	#IMPLIED>
+
+<!ELEMENT count		(math?)>
+<!ATTLIST count
+	id		ID	#IMPLIED
+	idref		IDREF	#IMPLIED>
+<!ELEMENT math		(apply | ci | cn)>
+<!ELEMENT apply		((divide | times), (ci | cn)+)>
+<!ELEMENT ci		(#PCDATA)>
+<!ELEMENT cn		(#PCDATA)>
+<!ELEMENT divide	EMPTY>
+<!ELEMENT times		EMPTY>
 
 <!ELEMENT flags		(field+)>
 <!ATTLIST flags
diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c
index 0d00cd7c9933..00afb37d7ef8 100644
--- a/gdb/gdbarch-gen.c
+++ b/gdb/gdbarch-gen.c
@@ -89,6 +89,7 @@ struct gdbarch
   gdbarch_ecoff_reg_to_regnum_ftype *ecoff_reg_to_regnum = no_op_reg_to_regnum;
   gdbarch_sdb_reg_to_regnum_ftype *sdb_reg_to_regnum = no_op_reg_to_regnum;
   gdbarch_dwarf2_reg_to_regnum_ftype *dwarf2_reg_to_regnum = no_op_reg_to_regnum;
+  gdbarch_regnum_to_dwarf2_reg_ftype *regnum_to_dwarf2_reg = nullptr;
   gdbarch_register_name_ftype *register_name = nullptr;
   gdbarch_register_type_ftype *register_type = nullptr;
   gdbarch_dummy_id_ftype *dummy_id = default_dummy_id;
@@ -347,6 +348,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of ecoff_reg_to_regnum, invalid_p == 0.  */
   /* Skip verify of sdb_reg_to_regnum, invalid_p == 0.  */
   /* Skip verify of dwarf2_reg_to_regnum, invalid_p == 0.  */
+  /* Skip verify of regnum_to_dwarf2_reg, has predicate.  */
   if (gdbarch->register_name == 0)
     log.puts ("\n\tregister_name");
   if (gdbarch->register_type == 0)
@@ -711,6 +713,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   gdb_printf (file,
 	      "gdbarch_dump: dwarf2_reg_to_regnum = <%s>\n",
 	      host_address_to_string (gdbarch->dwarf2_reg_to_regnum));
+  gdb_printf (file,
+	      "gdbarch_dump: gdbarch_regnum_to_dwarf2_reg_p() = %d\n",
+	      gdbarch_regnum_to_dwarf2_reg_p (gdbarch));
+  gdb_printf (file,
+	      "gdbarch_dump: regnum_to_dwarf2_reg = <%s>\n",
+	      host_address_to_string (gdbarch->regnum_to_dwarf2_reg));
   gdb_printf (file,
 	      "gdbarch_dump: register_name = <%s>\n",
 	      host_address_to_string (gdbarch->register_name));
@@ -2201,6 +2209,30 @@ set_gdbarch_dwarf2_reg_to_regnum (struct gdbarch *gdbarch,
   gdbarch->dwarf2_reg_to_regnum = dwarf2_reg_to_regnum;
 }
 
+bool
+gdbarch_regnum_to_dwarf2_reg_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->regnum_to_dwarf2_reg != NULL;
+}
+
+int
+gdbarch_regnum_to_dwarf2_reg (struct gdbarch *gdbarch, int regnum)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->regnum_to_dwarf2_reg != NULL);
+  if (gdbarch_debug >= 2)
+    gdb_printf (gdb_stdlog, "gdbarch_regnum_to_dwarf2_reg called\n");
+  return gdbarch->regnum_to_dwarf2_reg (gdbarch, regnum);
+}
+
+void
+set_gdbarch_regnum_to_dwarf2_reg (struct gdbarch *gdbarch,
+				  gdbarch_regnum_to_dwarf2_reg_ftype regnum_to_dwarf2_reg)
+{
+  gdbarch->regnum_to_dwarf2_reg = regnum_to_dwarf2_reg;
+}
+
 const char *
 gdbarch_register_name (struct gdbarch *gdbarch, int regnr)
 {
diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index b982fd7cd092..1d4d19cf6774 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -308,6 +308,15 @@ typedef int (gdbarch_dwarf2_reg_to_regnum_ftype) (struct gdbarch *gdbarch, int d
 extern int gdbarch_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int dwarf2_regnr);
 extern void set_gdbarch_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, gdbarch_dwarf2_reg_to_regnum_ftype *dwarf2_reg_to_regnum);
 
+/* Provide a default mapping from a gdb REGNUM to a DWARF2 register number.
+   Return -1 for bad REGNUM. */
+
+extern bool gdbarch_regnum_to_dwarf2_reg_p (struct gdbarch *gdbarch);
+
+typedef int (gdbarch_regnum_to_dwarf2_reg_ftype) (struct gdbarch *gdbarch, int regnum);
+extern int gdbarch_regnum_to_dwarf2_reg (struct gdbarch *gdbarch, int regnum);
+extern void set_gdbarch_regnum_to_dwarf2_reg (struct gdbarch *gdbarch, gdbarch_regnum_to_dwarf2_reg_ftype *regnum_to_dwarf2_reg);
+
 /* Return the name of register REGNR for the specified architecture.
    REGNR can be any value greater than, or equal to zero, and less than
    'gdbarch_num_cooked_regs (GDBARCH)'.  If REGNR is not supported for
diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index 4006380076dc..0764a6c31d92 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -589,6 +589,17 @@ Return -1 for bad REGNUM.  Note: Several targets get this wrong.
     invalid=False,
 )
 
+Method(
+    comment="""
+Provide a default mapping from a gdb REGNUM to a DWARF2 register number.
+Return -1 for bad REGNUM.
+""",
+    type="int",
+    name="regnum_to_dwarf2_reg",
+    params=[("int", "regnum")],
+    predicate=True,
+)
+
 Method(
     comment="""
 Return the name of register REGNR for the specified architecture.
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index ea8bad4e826c..8037283e3706 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -1030,6 +1030,40 @@ create_static_range_type (type_allocator &alloc, struct type *index_type,
   return result_type;
 }
 
+/* Create a range type using ALLOC.
+
+   Indices will be of type INDEX_TYPE, and will range from 0 to the value
+   LOC_EXPR_ evaluates to, inclusive.  */
+
+static struct type *
+create_dynamic_range_type (type_allocator &alloc, struct type *index_type,
+			   const gdb::array_view<const gdb_byte> locexpr_)
+{
+  struct dynamic_prop low, high;
+  struct dwarf2_property_baton *baton
+    = GDBARCH_OBSTACK_ZALLOC (alloc.arch (), dwarf2_property_baton);
+  size_t len = locexpr_.size () + 2;
+  gdb_byte *locexpr = GDBARCH_OBSTACK_CALLOC (alloc.arch (), len, gdb_byte);
+
+  memcpy (locexpr, locexpr_.data (), locexpr_.size());
+
+  /* The high bound is inclusive, so decrement the element count by one.  */
+  locexpr[len - 2] = DW_OP_lit1;
+  locexpr[len - 1] = DW_OP_minus;
+
+  baton->property_type = builtin_type (alloc.arch ())->builtin_unsigned_int;
+  baton->locexpr.data = locexpr;
+  baton->locexpr.size = len;
+  baton->locexpr.is_reference = false;
+  baton->locexpr.per_objfile = nullptr;
+  baton->locexpr.per_cu = nullptr;
+
+  low.set_const_val (0);
+  high.set_locexpr (baton);
+
+  return create_range_type (alloc, index_type, &low, &high, 0);
+}
+
 /* Predicate tests whether BOUNDS are static.  Returns 1 if all bounds values
    are static, otherwise returns 0.  */
 
@@ -1409,6 +1443,21 @@ lookup_array_range_type (struct type *element_type,
   return create_array_type (alloc, element_type, range_type);
 }
 
+/* Create type for array ranges where the low bound is 0 and the high
+   bound is given by the provided DWARF LOCEXPR_.  */
+
+static struct type *
+lookup_array_range_type (struct type *element_type,
+			 const gdb::array_view<const gdb_byte> locexpr_)
+{
+  type_allocator alloc (element_type);
+  struct type *index_type, *range_type;
+
+  index_type = builtin_type (element_type->arch ())->builtin_unsigned_int;
+  range_type = create_dynamic_range_type (alloc, index_type, locexpr_);
+  return create_array_type (alloc, element_type, range_type);
+}
+
 /* See gdbtypes.h.  */
 
 struct type *
@@ -1497,6 +1546,18 @@ init_vector_type (struct type *elt_type, int n)
   return array_type;
 }
 
+/* Create vector type of elements of type ELT_TYPE, where the number of
+   elements is given by the provided DWARF LOCEXPR.  */
+
+struct type *
+init_vector_type (struct type *elt_type,
+		  const gdb::array_view<const gdb_byte> locexpr)
+{
+  struct type *array_type = lookup_array_range_type (elt_type, locexpr);
+  make_vector_type (array_type);
+  return array_type;
+}
+
 /* Internal routine called by TYPE_SELF_TYPE to return the type that TYPE
    belongs to.  In c++ this is the class of "this", but TYPE_THIS_TYPE is too
    confusing.  "self" is a common enough replacement for "this".
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index 514af300de3b..46103ea44656 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -2427,6 +2427,8 @@ extern void append_flags_type_flag (struct type *type, int bitpos,
 
 extern void make_vector_type (struct type *array_type);
 extern struct type *init_vector_type (struct type *elt_type, int n);
+extern struct type *init_vector_type (struct type *elt_type,
+			       const gdb::array_view<const gdb_byte> locexpr);
 
 extern struct type *lookup_reference_type (struct type *, enum type_code);
 extern struct type *lookup_lvalue_reference_type (struct type *);
diff --git a/gdb/target-descriptions.c b/gdb/target-descriptions.c
index 82e4d96276e3..9c9cc2ceadba 100644
--- a/gdb/target-descriptions.c
+++ b/gdb/target-descriptions.c
@@ -159,7 +159,10 @@ make_gdb_type (struct gdbarch *gdbarch, struct tdesc_type *ttype)
 	return;
 
       type *element_gdb_type = make_gdb_type (m_gdbarch, e->element_type);
-      m_type = init_vector_type (element_gdb_type, e->count);
+      if (e->locexpr.size() > 0)
+	m_type = init_vector_type (element_gdb_type, e->locexpr);
+      else
+	m_type = init_vector_type (element_gdb_type, e->count);
       m_type->set_name (xstrdup (e->name.c_str ()));
       return;
     }
@@ -364,6 +367,10 @@ struct target_desc : tdesc_element
   /* The features associated with this target.  */
   std::vector<tdesc_feature_up> features;
 
+  /* Location expressions for variable-size vector registers may use constants
+     defined in the DWARF header.  */
+  bool include_dwarf_header = false;
+
   /* Used to cache the generated xml version of the target description.  */
   mutable char *xmltarget = nullptr;
 
@@ -452,6 +459,23 @@ get_arch_data (struct gdbarch *gdbarch)
   return result;
 }
 
+/* See gdbsupport/tdesc.h.  */
+
+int
+tdesc_dwarf_reg_to_regnum (const char *arch, int dwarf_reg)
+{
+  struct gdbarch_info info;
+  info.bfd_arch_info = bfd_scan_arch (arch);
+  if (info.bfd_arch_info == nullptr)
+    return -1;
+
+  gdbarch *gdbarch = gdbarch_find_by_info (info);
+  if (gdbarch == nullptr)
+    return -1;
+
+  return gdbarch_dwarf2_reg_to_regnum (gdbarch, dwarf_reg);
+}
+
 /* The string manipulated by the "set tdesc filename ..." command.  */
 
 static std::string tdesc_filename_cmd_string;
@@ -1214,6 +1238,14 @@ set_tdesc_osabi (struct target_desc *target_desc, enum gdb_osabi osabi)
 {
   target_desc->osabi = osabi;
 }
+
+/* See gdb/target-descriptions.h.  */
+
+void
+set_tdesc_include_dwarf_header (struct target_desc *target_desc, bool include)
+{
+  target_desc->include_dwarf_header = include;
+}
 
 
 static struct cmd_list_element *tdesc_set_cmdlist, *tdesc_show_cmdlist;
@@ -1372,9 +1404,45 @@ class print_c_tdesc : public tdesc_element_visitor
     gdb_printf
       ("  element_type = tdesc_named_type (feature, \"%s\");\n",
        type->element_type->name.c_str ());
-    gdb_printf
-      ("  tdesc_create_vector (feature, \"%s\", element_type, %d);\n",
-       type->name.c_str (), type->count);
+
+    if (type->count == TDESC_REG_VARIABLE_SIZE)
+      {
+	const std::string *prefix;
+
+	if (type->locexpr_id.has_value ())
+	  prefix = &*type->locexpr_id;
+	else if (type->locexpr_idref.has_value ())
+	  prefix = &*type->locexpr_idref;
+	else
+	  prefix = &type->name;
+
+	if (!type->locexpr_idref.has_value ())
+	  {
+	    gdb_printf ("  gdb_byte %s_locexpr[%zu];\n", prefix->c_str (),
+			type->locexpr.size ());
+	    for (unsigned int i = 0; i < type->locexpr.size (); i++)
+	      {
+		const char *op_name;
+
+		if (type->locexpr_str.has_value ())
+		  op_name = type->locexpr_str->at (i);
+		else
+		  op_name = nullptr;
+
+		if (op_name == nullptr)
+		  gdb_printf ("  %s_locexpr[%u] = %u;\n", prefix->c_str (), i,
+			      type->locexpr[i]);
+		else
+		  gdb_printf ("  %s_locexpr[%u] = %s;\n", prefix->c_str (), i,
+			      op_name);
+	      }
+	  }
+	gdb_printf ("  tdesc_create_vector (feature, \"%s\", element_type, %s_locexpr);\n",
+		    type->name.c_str (), prefix->c_str ());
+      }
+    else
+      gdb_printf ("  tdesc_create_vector (feature, \"%s\", element_type, %d);\n",
+		  type->name.c_str (), type->count);
 
     gdb_printf ("\n");
   }
@@ -1494,7 +1562,13 @@ class print_c_tdesc : public tdesc_element_visitor
       gdb_printf ("\"%s\", ", reg->group.c_str ());
     else
       gdb_printf ("NULL, ");
-    gdb_printf ("%d, \"%s\"", reg->bitsize, reg->type.c_str ());
+
+    if (reg->bitsize == TDESC_REG_VARIABLE_SIZE)
+      gdb_printf ("TDESC_REG_VARIABLE_SIZE, ");
+    else
+      gdb_printf ("%d, ", reg->bitsize);
+
+    gdb_printf ("\"%s\"", reg->type.c_str ());
 
     if (reg->load_early)
       gdb_printf (", true");
@@ -1558,6 +1632,8 @@ class print_c_feature : public print_c_tdesc
 		lbasename (m_filename_after_features.c_str ()));
 
     gdb_printf ("#include \"gdbsupport/tdesc.h\"\n");
+    if (e->include_dwarf_header)
+      gdb_printf ("#include \"dwarf2.h\"\n");
     gdb_printf ("\n");
   }
 
@@ -1642,7 +1718,13 @@ class print_c_feature : public print_c_tdesc
       gdb_printf ("\"%s\", ", reg->group.c_str ());
     else
       gdb_printf ("NULL, ");
-    gdb_printf ("%d, \"%s\"", reg->bitsize, reg->type.c_str ());
+
+    if (reg->bitsize == TDESC_REG_VARIABLE_SIZE)
+      gdb_printf ("TDESC_REG_VARIABLE_SIZE, ");
+    else
+      gdb_printf ("%d, ", reg->bitsize);
+
+    gdb_printf ("\"%s\"", reg->type.c_str ());
 
     if (reg->load_early)
       gdb_printf (", true");
diff --git a/gdb/target-descriptions.h b/gdb/target-descriptions.h
index e40c7db5f79f..c65e5d203933 100644
--- a/gdb/target-descriptions.h
+++ b/gdb/target-descriptions.h
@@ -229,6 +229,12 @@ void set_tdesc_property (struct target_desc *,
 void tdesc_add_compatible (struct target_desc *,
 			   const struct bfd_arch_info *);
 
+/* Set whether the C version of the target description should include
+   dwarf2.h.  */
+
+void set_tdesc_include_dwarf_header (struct target_desc *target_desc,
+				     bool include);
+
 #if GDB_SELF_TEST
 namespace selftests {
 
diff --git a/gdb/xml-tdesc.c b/gdb/xml-tdesc.c
index 436c493d4f91..2f28968838d6 100644
--- a/gdb/xml-tdesc.c
+++ b/gdb/xml-tdesc.c
@@ -71,21 +71,62 @@ static std::unordered_map<std::string, target_desc_up> xml_cache;
 struct tdesc_parsing_data
 {
   /* The target description we are building.  */
-  struct target_desc *tdesc;
+  struct target_desc *tdesc = nullptr;
 
   /* The target feature we are currently parsing, or last parsed.  */
-  struct tdesc_feature *current_feature;
+  struct tdesc_feature *current_feature = nullptr;
 
   /* The register number to use for the next register we see, if
      it does not have its own.  This starts at zero.  */
-  int next_regnum;
+  int next_regnum = 0;
 
   /* The struct or union we are currently parsing, or last parsed.  */
-  tdesc_type_with_fields *current_type;
+  tdesc_type_with_fields *current_type = nullptr;
 
   /* The byte size of the current struct/flags type, if specified.  Zero
      if not specified.  Flags values must specify a size.  */
-  int current_type_size;
+  int current_type_size = 0;
+
+  /* Map used to implement id/idref attribute support for the <count>
+     element in a vector type.  The fist member is the count id, and the
+     second is the DWARF expression it corresponds to.  */
+  std::unordered_map<std::string, gdb::byte_vector> vector_count_locexprs;
+
+  /* Information needed to parse <vector> element.  */
+  struct {
+    /* id attribute for the <vector> element being processed.  */
+    std::string id;
+
+    /* Type of field in <vector> element being processed.  */
+    struct tdesc_type *field_type = nullptr;
+
+    /* Holds number of elements, if <vector> has "count" attribute.  */
+    std::optional<int> count;
+
+    /* Holds DWARF expression, if <vector> has a <math> child.  */
+    gdb::byte_vector locexpr;
+
+    /* Holds string representation of opcodes in locexpr.  Used to provide
+       more readable code when converting to C.  Needs to have the same
+       number of elements as locexpr.  */
+    std::optional<std::vector<const char *>> locexpr_str;
+
+    /* id attribute for the <count> element being processed.  */
+    std::string count_id;
+
+    std::string count_idref;
+
+    std::optional<gdb_byte> apply_operator;
+
+    std::optional<const char *> apply_operator_str;
+  } current_vector;
+
+  /* gdbarch that roughly corresponds to the XML target description being
+     parsed.  Used to gather basic facts about the architecture such as the
+     DWARF register numbering.  */
+  gdbarch *preliminary_gdbarch = nullptr;
+
+  std::set<int> load_early_regnums;
 };
 
 /* Handle the end of an <architecture> element and its value.  */
@@ -166,6 +207,36 @@ tdesc_start_feature (struct gdb_xml_parser *parser,
   data->current_feature = tdesc_create_feature (data->tdesc, name);
 }
 
+/* Handle the end of a <feature> element.  */
+
+static void
+tdesc_end_feature (struct gdb_xml_parser *parser,
+		   const struct gdb_xml_element *element, void *user_data,
+		   const char *body_text)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+  for (int regnum : data->load_early_regnums)
+    {
+      std::vector<tdesc_reg_up> &registers = data->current_feature->registers;
+      auto reg = std::find_if (registers.begin (), registers.end (),
+			       [=] (tdesc_reg_up &i) {
+				 if (i->target_regnum == regnum)
+				   return true;
+				 else
+				   return false;
+			       });
+      if (reg == registers.end ())
+	gdb_xml_error (parser,
+		       _("Register number %d used in expression but not found in feature."),
+		       regnum);
+
+      (*reg)->load_early = true;
+    }
+
+  data->load_early_regnums.clear ();
+}
+
 /* Handle the start of a <reg> element.  Fill in the optional
    attributes and attach it to the containing feature.  */
 
@@ -474,27 +545,287 @@ tdesc_start_vector (struct gdb_xml_parser *parser,
 		    void *user_data, std::vector<gdb_xml_value> &attributes)
 {
   struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
-  struct tdesc_type *field_type;
-  char *id, *field_type_id;
-  ULONGEST count;
+  char *field_type_id;
 
-  id = (char *) attributes[0].value.get ();
+  data->current_vector.id = (char *) attributes[0].value.get ();
   field_type_id = (char *) attributes[1].value.get ();
-  count = * (ULONGEST *) attributes[2].value.get ();
 
-  if (count > MAX_VECTOR_SIZE)
+  if (attributes.size () >= 3)
     {
-      gdb_xml_error (parser,
-		     _("Vector size %s is larger than maximum (%d)"),
-		     pulongest (count), MAX_VECTOR_SIZE);
+      ULONGEST count = *(ULONGEST *)attributes[2].value.get ();
+
+      if (count > MAX_VECTOR_SIZE)
+	gdb_xml_error (parser,
+		       _ ("Vector \"%s\": size %s is larger than maximum (%d)"),
+		       data->current_vector.id.c_str (), pulongest (count),
+		       MAX_VECTOR_SIZE);
+
+      data->current_vector.count = (int) count;
     }
 
-  field_type = tdesc_named_type (data->current_feature, field_type_id);
-  if (field_type == NULL)
+  data->current_vector.field_type = tdesc_named_type (data->current_feature,
+						      field_type_id);
+  if (data->current_vector.field_type == nullptr)
     gdb_xml_error (parser, _("Vector \"%s\" references undefined type \"%s\""),
-		   id, field_type_id);
+		   data->current_vector.id.c_str (), field_type_id);
+}
+
+/* Handle the end of a <vector> element.  */
+
+static void
+tdesc_end_vector (struct gdb_xml_parser *parser,
+		  const struct gdb_xml_element *element, void *user_data,
+		  const char *body_text)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+  if (data->current_vector.count.has_value ()
+      && !data->current_vector.locexpr.empty ())
+    gdb_xml_error (parser,
+		   _ ("Vector \"%s\" has both a count attribute and a count expression"),
+		   data->current_vector.id.c_str ());
+  else if (!data->current_vector.count.has_value ()
+	   && data->current_vector.locexpr.empty ())
+    gdb_xml_error (parser,
+		   _ ("Vector \"%s\" has neither a count attribute nor a count expression"),
+		   data->current_vector.id.c_str ());
+
+  if (data->current_vector.count.has_value ())
+    tdesc_create_vector (data->current_feature,
+			 data->current_vector.id.c_str (),
+			 data->current_vector.field_type,
+			 *data->current_vector.count);
+  else
+    {
+      tdesc_type *type = tdesc_create_vector (data->current_feature,
+					      data->current_vector.id.c_str (),
+					      data->current_vector.field_type,
+					      data->current_vector.locexpr,
+					      data->current_vector.locexpr_str);
+      tdesc_type_vector *type_vector = static_cast<tdesc_type_vector *> (type);
+
+      if (!data->current_vector.count_id.empty ())
+	type_vector->locexpr_id = data->current_vector.count_id;
+      else if (!data->current_vector.count_idref.empty ())
+	type_vector->locexpr_idref = data->current_vector.count_idref;
+
+      set_tdesc_include_dwarf_header (data->tdesc, true);
+    }
+
+  data->current_vector = {};
+}
+
+/* Handle the start of a <count> element.  Store any given count ID and process
+   IDREFs.  */
+
+static void
+tdesc_start_count (struct gdb_xml_parser *parser,
+		   const struct gdb_xml_element *element,
+		   void *user_data, std::vector<gdb_xml_value> &attributes)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+  const char *id = nullptr, *idref = nullptr;
+
+  for (gdb_xml_value &attr : attributes)
+    {
+      if (!strcmp (attr.name, "id"))
+	id = (const char *) attr.value.get ();
+      else if (!strcmp (attr.name, "idref"))
+	idref = (const char *) attr.value.get ();
+    }
+
+  if (id != nullptr && idref != nullptr)
+    gdb_xml_error (parser,
+		   _ ("Vector \"%s\": count expression has both an id and an idref"),
+		   data->current_vector.id.c_str ());
+
+  if (id != nullptr)
+    {
+      std::string current_count_id = id;
+
+      if (data->vector_count_locexprs.find (current_count_id)
+	  != data->vector_count_locexprs.end ())
+	gdb_xml_error (parser,
+		       _ ("Vector \"%s\": count expression has duplicate id \"%s\""),
+		       data->current_vector.id.c_str (), id);
+
+      data->current_vector.count_id = std::move (current_count_id);
+    }
+  else if (idref != nullptr)
+    {
+      std::string current_count_idref = idref;
+
+      if (data->vector_count_locexprs.find (current_count_idref)
+	  == data->vector_count_locexprs.end ())
+	gdb_xml_error (parser,
+		       _ ("Vector \"%s\": count expression references unknown id \"%s\""),
+		       data->current_vector.id.c_str (), idref);
 
-  tdesc_create_vector (data->current_feature, id, field_type, count);
+      data->current_vector.count_idref = idref;
+    }
+}
+
+/* Handle the end of a <count> element.  If the element has an id attribute, store the
+   location expression in the map.  If it has an idref attribute, fetch the location
+   expression from the map.  */
+
+static void
+tdesc_end_count (struct gdb_xml_parser *parser,
+		 const struct gdb_xml_element *element, void *user_data,
+		 const char *body_text)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+  if (!data->current_vector.count_id.empty ())
+    {
+      bool inserted
+	  = data->vector_count_locexprs
+		.insert ({ data->current_vector.count_id,
+			   data->current_vector.locexpr })
+		.second;
+
+      /* Insertion isn't supposed to fail: we already checked in the start
+	 handler that this id isn't present in the map.  */
+      gdb_assert (inserted);
+    }
+  else if (!data->current_vector.count_idref.empty ())
+    data->current_vector.locexpr
+	= data->vector_count_locexprs.at (data->current_vector.count_idref);
+}
+
+/* Handle the start of a <math> element.  It just has to check whether it's inside
+   a <count> element with an idref and initialize data->current_vector.locexpr_str.  */
+
+static void
+tdesc_start_math (struct gdb_xml_parser *parser,
+		  const struct gdb_xml_element *element, void *user_data,
+		  std::vector<gdb_xml_value> &attributes)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *)user_data;
+
+  if (!data->current_vector.count_idref.empty ())
+    gdb_xml_error (parser,
+		   _ ("Vector \"%s\": count element with an idref isn't empty."),
+		   data->current_vector.id.c_str ());
+
+  data->current_vector.locexpr_str.emplace ();
+}
+
+/* Handle the end of an <apply> element.  We just need to store the expression
+   operator at the end of the location expression.  */
+
+static void
+tdesc_end_apply (struct gdb_xml_parser *parser,
+		 const struct gdb_xml_element *element, void *user_data,
+		 const char *body_text)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+  if (!data->current_vector.apply_operator.has_value ())
+    gdb_xml_error (parser,
+		   _ ("Vector \"%s\": apply element doesn't have an operator."),
+		   data->current_vector.id.c_str ());
+
+  data->current_vector.locexpr.push_back (*data->current_vector.apply_operator);
+  data->current_vector.locexpr_str->push_back (*data->current_vector.apply_operator_str);
+}
+
+/* Handle the end of a <ci> element and its value.  */
+
+static void
+tdesc_end_ci (struct gdb_xml_parser *parser,
+	      const struct gdb_xml_element *element, void *user_data,
+	      const char *body_text)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+  if (data->preliminary_gdbarch == nullptr) {
+    gdbarch_info arch_info;
+    arch_info.bfd_arch_info = tdesc_architecture (data->tdesc);
+    arch_info.osabi = tdesc_osabi (data->tdesc);
+    data->preliminary_gdbarch = gdbarch_find_by_info (arch_info);
+
+    if (data->preliminary_gdbarch == nullptr)
+      gdb_xml_error (parser,
+		     _("Vector \"%s\": couldn't find basic architecture information."),
+		     data->current_vector.id.c_str ());
+
+    if (!gdbarch_regnum_to_dwarf2_reg_p (data->preliminary_gdbarch))
+      gdb_xml_error (parser,
+		     _("Vector \"%s\": architecture %s doesn't support vector count based on another register."),
+		     data->current_vector.id.c_str (),
+		     gdbarch_bfd_arch_info (data->preliminary_gdbarch)->printable_name);
+  }
+
+  ULONGEST regnum = gdb_xml_parse_ulongest (parser, body_text);
+  if (regnum > INT_MAX)
+    gdb_xml_error (parser,
+		   _("Vector \"%s\": count expression uses register number %s out of range."),
+		   data->current_vector.id.c_str (), pulongest (regnum));
+
+  int dwarf_reg = gdbarch_regnum_to_dwarf2_reg (data->preliminary_gdbarch,
+						regnum);
+  if (dwarf_reg < 0 || dwarf_reg > 255)
+    gdb_xml_error (parser,
+		   _("Vector \"%s\": count expression uses register number %s out of range."),
+		   data->current_vector.id.c_str (), pulongest (dwarf_reg));
+
+  data->current_vector.locexpr.push_back (DW_OP_bregx);
+  data->current_vector.locexpr.push_back ((gdb_byte) dwarf_reg);
+  data->current_vector.locexpr.push_back (0);
+
+  data->current_vector.locexpr_str->push_back ("DW_OP_bregx");
+  data->current_vector.locexpr_str->push_back (nullptr);
+  data->current_vector.locexpr_str->push_back (nullptr);
+
+  data->load_early_regnums.insert (regnum);
+}
+
+/* Handle the end of a <cn> element and its value.  */
+
+static void
+tdesc_end_cn (struct gdb_xml_parser *parser,
+	      const struct gdb_xml_element *element, void *user_data,
+	      const char *body_text)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+  ULONGEST number = gdb_xml_parse_ulongest (parser, body_text);
+
+  /* We only support DW_OP_lit0 to DW_OP_lit31.  */
+  if (number > 31)
+    gdb_xml_error (parser,
+		   _ ("Vector \"%s\": count expression uses value %s larger than maximum of 31."),
+		   data->current_vector.id.c_str (), pulongest (number));
+
+  gdb_byte value = DW_OP_lit0 + number;
+  data->current_vector.locexpr.push_back (value);
+  data->current_vector.locexpr_str->push_back (get_DW_OP_name (value));
+}
+
+/* Handle the end of a <divide> element.  */
+
+static void
+tdesc_end_divide (struct gdb_xml_parser *parser,
+		  const struct gdb_xml_element *element, void *user_data,
+		  const char *body_text)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+  data->current_vector.apply_operator = DW_OP_div;
+  data->current_vector.apply_operator_str = "DW_OP_div";
+}
+
+/* Handle the end of a <times> element.  */
+
+static void
+tdesc_end_times (struct gdb_xml_parser *parser,
+		 const struct gdb_xml_element *element, void *user_data,
+		 const char *body_text)
+{
+  struct tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+
+  data->current_vector.apply_operator = DW_OP_mul;
+  data->current_vector.apply_operator_str = "DW_OP_mul";
 }
 
 /* The elements and attributes of an XML target description.  */
@@ -525,6 +856,43 @@ static const struct gdb_xml_element enum_children[] = {
   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
 };
 
+/* MathML allows recursing <apply> elements, but we don't have a need for it
+   yet, so for simplicity we don't.  */
+static const struct gdb_xml_element apply_children[] = {
+  { "ci", nullptr, nullptr, GDB_XML_EF_NONE, nullptr, tdesc_end_ci },
+  { "cn", nullptr, nullptr, GDB_XML_EF_OPTIONAL, nullptr, tdesc_end_cn },
+  { "divide", nullptr, nullptr, GDB_XML_EF_OPTIONAL, nullptr,
+    tdesc_end_divide },
+  { "times", nullptr, nullptr, GDB_XML_EF_OPTIONAL, nullptr, tdesc_end_times },
+  { nullptr, nullptr, nullptr, GDB_XML_EF_NONE, nullptr, nullptr }
+};
+
+static const struct gdb_xml_element math_children[] = {
+  { "apply", nullptr, apply_children, GDB_XML_EF_OPTIONAL, nullptr,
+    tdesc_end_apply },
+  { "ci", nullptr, nullptr, GDB_XML_EF_OPTIONAL, nullptr, tdesc_end_ci },
+  { "cn", nullptr, nullptr, GDB_XML_EF_OPTIONAL, nullptr, tdesc_end_cn },
+  { nullptr, nullptr, nullptr, GDB_XML_EF_NONE, nullptr, nullptr }
+};
+
+static const struct gdb_xml_element count_children[] = {
+  { "math", nullptr, math_children, GDB_XML_EF_OPTIONAL, tdesc_start_math,
+    nullptr },
+  { nullptr, nullptr, nullptr, GDB_XML_EF_NONE, nullptr, nullptr }
+};
+
+static const struct gdb_xml_attribute count_attributes[] = {
+  { "id", GDB_XML_AF_OPTIONAL, nullptr, nullptr },
+  { "idref", GDB_XML_AF_OPTIONAL, nullptr, nullptr },
+  { nullptr, GDB_XML_AF_NONE, nullptr, nullptr }
+};
+
+static const struct gdb_xml_element vector_children[] = {
+  { "count", count_attributes, count_children, GDB_XML_EF_OPTIONAL,
+    tdesc_start_count, tdesc_end_count },
+  { nullptr, nullptr, nullptr, GDB_XML_EF_NONE, nullptr, nullptr }
+};
+
 static const struct gdb_xml_attribute reg_attributes[] = {
   { "name", GDB_XML_AF_NONE, NULL, NULL },
   { "bitsize", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
@@ -557,7 +925,7 @@ static const struct gdb_xml_attribute enum_attributes[] = {
 static const struct gdb_xml_attribute vector_attributes[] = {
   { "id", GDB_XML_AF_NONE, NULL, NULL },
   { "type", GDB_XML_AF_NONE, NULL, NULL },
-  { "count", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "count", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
   { NULL, GDB_XML_AF_NONE, NULL, NULL }
 };
 
@@ -582,9 +950,10 @@ static const struct gdb_xml_element feature_children[] = {
   { "enum", enum_attributes, enum_children,
     GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
     tdesc_start_enum, NULL },
-  { "vector", vector_attributes, NULL,
-    GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
-    tdesc_start_vector, NULL },
+  { "vector", vector_attributes, vector_children,
+    GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE, tdesc_start_vector,
+    tdesc_end_vector },
+  { "architecture", NULL, NULL, GDB_XML_EF_OPTIONAL, NULL, tdesc_end_arch },
   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
 };
 
@@ -602,7 +971,7 @@ static const struct gdb_xml_element target_children[] = {
     NULL, tdesc_end_compatible },
   { "feature", feature_attributes, feature_children,
     GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
-    tdesc_start_feature, NULL },
+    tdesc_start_feature, tdesc_end_feature },
   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
 };
 
@@ -636,7 +1005,6 @@ tdesc_parse_xml (const char *document, xml_fetch_another fetcher)
   if (it != xml_cache.end ())
     return it->second.get ();
 
-  memset (&data, 0, sizeof (struct tdesc_parsing_data));
   target_desc_up description = allocate_target_description ();
   data.tdesc = description.get ();
 
diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index 6148ccf9121b..3315a0c95f19 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -208,6 +208,7 @@ SFILES = \
 	$(srcdir)/linux-sparc-low.cc \
 	$(srcdir)/linux-x86-low.cc \
 	$(srcdir)/linux-xtensa-low.cc \
+	$(srcdir)/locexpr.cc \
 	$(srcdir)/mem-break.cc \
 	$(srcdir)/netbsd-aarch64-low.cc \
 	$(srcdir)/netbsd-amd64-low.cc \
@@ -262,6 +263,7 @@ OBS = \
 	dll.o \
 	hostio.o \
 	inferiors.o \
+	locexpr.o \
 	mem-break.o \
 	notif.o \
 	regcache.o \
diff --git a/gdbserver/config.in b/gdbserver/config.in
index 65f9ff6e6470..25aefbecae4f 100644
--- a/gdbserver/config.in
+++ b/gdbserver/config.in
@@ -104,6 +104,9 @@
 /* Define to 1 if you have the <dlfcn.h> header file. */
 #undef HAVE_DLFCN_H
 
+/* Define if the target provides DWARF register to regnum mapping. */
+#undef HAVE_DWARF_REG_TO_REGNUM
+
 /* Define to 1 if the system has the type `Elf32_auxv_t'. */
 #undef HAVE_ELF32_AUXV_T
 
diff --git a/gdbserver/configure b/gdbserver/configure
index 09cb3c5bf434..c310d522ef10 100755
--- a/gdbserver/configure
+++ b/gdbserver/configure
@@ -14743,6 +14743,12 @@ if $want_ipa ; then
    fi
 fi
 
+if test "${srv_dwarf_reg_to_regnum}" = "yes"; then
+
+$as_echo "#define HAVE_DWARF_REG_TO_REGNUM 1" >>confdefs.h
+
+fi
+
 
 
 
diff --git a/gdbserver/configure.ac b/gdbserver/configure.ac
index ee0de9decbde..35ec08da06e4 100644
--- a/gdbserver/configure.ac
+++ b/gdbserver/configure.ac
@@ -441,6 +441,11 @@ if $want_ipa ; then
    fi
 fi
 
+if test "${srv_dwarf_reg_to_regnum}" = "yes"; then
+  AC_DEFINE(HAVE_DWARF_REG_TO_REGNUM, 1,
+	    [Define if the target provides DWARF register to regnum mapping.])
+fi
+
 AC_SUBST(GDBSERVER_DEPFILES)
 AC_SUBST(GDBSERVER_LIBS)
 AC_SUBST(srv_xmlbuiltin)
diff --git a/gdbserver/locexpr.cc b/gdbserver/locexpr.cc
new file mode 100644
index 000000000000..25d614c3a2bb
--- /dev/null
+++ b/gdbserver/locexpr.cc
@@ -0,0 +1,107 @@
+/* Simplified DWARF location expression evaluator for gdbserver.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#include "server.h"
+#include "leb128.h"
+#include "locexpr.h"
+#include "regcache.h"
+#include "arch/aarch64.h"
+
+#define DW_OP_lit0 0x30
+#define DW_OP_lit2 0x32
+#define DW_OP_lit4 0x34
+#define DW_OP_lit8 0x38
+#define DW_OP_bregx 0x92
+#define DW_OP_div 0x1b
+#define DW_OP_mul 0x1e
+
+/* See gdbserver/locexpr.h.  */
+
+long
+evaluate_locexpr (const gdb::array_view<const gdb_byte> locexpr,
+		  const struct regcache *regcache)
+{
+  std::vector<long> stack;
+
+  for (auto it = locexpr.begin (); it != locexpr.end ();)
+    {
+      gdb_byte op = *it;
+      long result;
+
+      it++;
+      switch (op)
+	{
+	case DW_OP_div:
+	  {
+	    long divisor = stack.back ();
+	    stack.pop_back ();
+	    long dividend = stack.back ();
+	    stack.pop_back ();
+	    result = dividend / divisor;
+	    break;
+	  }
+	case DW_OP_mul:
+	  {
+	    long multiplicand = stack.back ();
+	    stack.pop_back ();
+	    long multiplier = stack.back ();
+	    stack.pop_back ();
+	    result = multiplier * multiplicand;
+	    break;
+	  }
+	case DW_OP_lit0:
+	case DW_OP_lit2:
+	case DW_OP_lit4:
+	case DW_OP_lit8:
+	  result = op - DW_OP_lit0;
+	  break;
+	case DW_OP_bregx:
+	  {
+	    uint64_t reg;
+	    int read = read_uleb128_to_uint64 (it, locexpr.end (), &reg);
+	    gdb_assert (read != 0);
+
+	    it += read;
+
+	    int64_t offset;
+	    read = read_sleb128_to_int64 (it, locexpr.end (), &offset);
+	    gdb_assert (read != 0);
+
+	    it += read;
+
+	    const char *arch = tdesc_architecture_name (regcache->tdesc);
+	    reg = tdesc_dwarf_reg_to_regnum (arch, reg);
+	    gdb_assert (reg != -1);
+
+	    register_status reg_status = regcache->get_register_status (reg);
+	    gdb_assert (reg_status == REG_VALID);
+	    regcache->raw_collect (reg,
+				   gdb::make_array_view ((gdb_byte *) &result,
+							 sizeof (result)));
+	    result += offset;
+	    break;
+	  }
+	default:
+	  gdb_assert_not_reached ("Unsupported locexpr operation: %c\n", op);
+	}
+
+      stack.push_back (result);
+    }
+
+  return stack.back ();
+}
diff --git a/gdbserver/locexpr.h b/gdbserver/locexpr.h
new file mode 100644
index 000000000000..7b768058697d
--- /dev/null
+++ b/gdbserver/locexpr.h
@@ -0,0 +1,31 @@
+/* Simplified DWARF location expression evaluator for gdbserver.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef GDBSERVER_LOCEXPR_H
+#define GDBSERVER_LOCEXPR_H
+
+#include "gdbsupport/array-view.h"
+#include "gdbsupport/common-types.h"
+#include "regcache.h"
+
+/* Evaluate the given DWARF LOCEXPR, using REGCACHE to get register values.  */
+
+long evaluate_locexpr (const gdb::array_view<const gdb_byte> locexpr,
+		       const struct regcache *regcache);
+
+#endif /* GDBSERVER_LOCEXPR_H */
diff --git a/gdbserver/tdesc.cc b/gdbserver/tdesc.cc
index 6f7ebb7c5c76..fbaf756f5050 100644
--- a/gdbserver/tdesc.cc
+++ b/gdbserver/tdesc.cc
@@ -129,6 +129,9 @@ copy_target_description (struct target_desc *dest,
   dest->expedite_regs = src->expedite_regs;
   dest->registers_size = src->registers_size;
   dest->xmltarget = src->xmltarget;
+
+  if (src->arch)
+    set_tdesc_architecture (dest, src->arch.get ());
 }
 
 const struct target_desc *
@@ -242,3 +245,18 @@ tdesc_contains_feature (const target_desc *tdesc, const std::string &feature)
 
   return false;
 }
+
+#ifndef HAVE_DWARF_REG_TO_REGNUM
+
+/* See gdbsupport/tdesc.h.
+
+   This is a dummy implementation.  It's not used if the target description
+   doesn't have variable-length registers.  */
+
+int
+tdesc_dwarf_reg_to_regnum (const char *arch, int dwarf_reg)
+{
+  return -1;
+}
+
+#endif /* HAVE_DWARF_REG_TO_REGNUM */
diff --git a/gdbsupport/tdesc.cc b/gdbsupport/tdesc.cc
index a99119274f44..05b348fd8077 100644
--- a/gdbsupport/tdesc.cc
+++ b/gdbsupport/tdesc.cc
@@ -18,6 +18,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "gdbsupport/tdesc.h"
+#include "dwarf2.h"
 
 tdesc_reg::tdesc_reg (struct tdesc_feature *feature, const std::string &name_,
 		      int regnum, int save_restore_, const char *group_,
@@ -160,6 +161,21 @@ tdesc_create_vector (struct tdesc_feature *feature, const char *name,
 
 /* See gdbsupport/tdesc.h.  */
 
+struct tdesc_type *
+tdesc_create_vector (struct tdesc_feature *feature, const char *name,
+		     struct tdesc_type *field_type,
+		     const gdb::array_view<const gdb_byte> locexpr,
+		     std::optional<std::vector<const char *>> locexpr_str)
+{
+  tdesc_type_vector *type = new tdesc_type_vector (name, field_type, locexpr,
+						   locexpr_str);
+  feature->types.emplace_back (type);
+
+  return type;
+}
+
+/* See gdbsupport/tdesc.h.  */
+
 tdesc_type_with_fields *
 tdesc_create_struct (struct tdesc_feature *feature, const char *name)
 {
@@ -312,8 +328,113 @@ void print_xml_feature::visit (const tdesc_type_builtin *t)
 
 void print_xml_feature::visit (const tdesc_type_vector *t)
 {
-  add_line ("<vector id=\"%s\" type=\"%s\" count=\"%d\"/>",
-	    t->name.c_str (), t->element_type->name.c_str (), t->count);
+  if (t->locexpr.empty ())
+    add_line ("<vector id=\"%s\" type=\"%s\" count=\"%d\"/>",
+	      t->name.c_str (), t->element_type->name.c_str (), t->count);
+  else
+    {
+      add_line ("<vector id=\"%s\" type=\"%s\">",
+		t->name.c_str (), t->element_type->name.c_str ());
+
+      if (t->locexpr_idref.has_value ())
+	add_line ("  <count idref=\"%s\"/>", t->locexpr_idref->c_str ());
+      else
+	{
+	  if (t->locexpr_id.has_value ())
+	    add_line ("  <count id=\"%s\">", t->locexpr_id->c_str ());
+	  else
+	    add_line ("  <count>");
+
+	  add_line ("    <math>");
+
+	  std::vector<std::string> stack;
+
+	  /* Convert DWARF location expression to MathML.  */
+	  for (int i = 0; i < t->locexpr.size (); i++)
+	    {
+	      switch (t->locexpr[i])
+		{
+		case DW_OP_bregx:
+		  {
+		    gdb_byte arg1 = t->locexpr[i + 1];
+		    gdb_byte arg2 = t->locexpr[i + 2];
+		    gdb_byte dwarf_reg;
+
+		    if (arg1 == 0)
+		      dwarf_reg = arg2;
+		    else if (arg2 == 0)
+		      dwarf_reg = arg1;
+		    else
+		      error (_("In a vector count locexpr one of the "
+			       "operands of DW_OP_bregx must be 0."));
+
+		    if (m_arch == nullptr)
+		      error (_("Feature references a register in locexpr but "
+			       "the architecture is unknown."));
+
+		    int regnum = tdesc_dwarf_reg_to_regnum (m_arch, dwarf_reg);
+		    if (regnum == -1)
+		      error (_("Unknown DWARF register %d."), dwarf_reg);
+
+		    stack.push_back (string_printf ("<ci>%d</ci>", regnum));
+
+		    i += 2;
+		  }
+		  break;
+		case DW_OP_div:
+		  {
+		    std::string divisor = stack.back ();
+		    stack.pop_back ();
+		    std::string dividend = stack.back ();
+		    stack.pop_back ();
+
+		    stack.push_back ("</apply>");
+		    stack.push_back ("  " + divisor);
+		    stack.push_back ("  " + dividend);
+		    stack.push_back ("  <divide/>");
+		    stack.push_back ("<apply>");
+		  }
+		  break;
+		case DW_OP_mul:
+		  {
+		    std::string multiplicand = stack.back ();
+		    stack.pop_back ();
+		    std::string multiplier = stack.back ();
+		    stack.pop_back ();
+
+		    stack.push_back ("</apply>");
+		    stack.push_back ("  " + multiplicand);
+		    stack.push_back ("  " + multiplier);
+		    stack.push_back ("  <times/>");
+		    stack.push_back ("<apply>");
+		  }
+		  break;
+		default:
+		  {
+		    gdb_byte val = t->locexpr[i];
+
+		    if (val > DW_OP_lit0 && val <= DW_OP_lit31)
+		      stack.push_back (string_printf ("<cn>%d</cn>",
+						      val - DW_OP_lit0));
+		    else
+		      error (_ ("Unsupported DWARF operator %d."), val);
+		  }
+		  break;
+		}
+	    }
+
+	  while (!stack.empty ())
+	    {
+	      add_line ("      " + stack.back ());
+	      stack.pop_back ();
+	    }
+
+	  add_line ("    </math>");
+	  add_line ("  </count>");
+	}
+
+      add_line ("</vector>");
+    }
 }
 
 void print_xml_feature::visit (const tdesc_type_with_fields *t)
@@ -405,9 +526,12 @@ void print_xml_feature::visit_pre (const target_desc *e)
   add_line ("<!DOCTYPE target SYSTEM \"gdb-target.dtd\">");
   add_line ("<target>");
   indent (1);
-  if (tdesc_architecture_name (e))
-    add_line ("<architecture>%s</architecture>",
-	      tdesc_architecture_name (e));
+  const char *arch = tdesc_architecture_name (e);
+  if (arch != nullptr)
+    {
+      add_line ("<architecture>%s</architecture>", arch);
+      m_arch = arch;
+    }
 
   const char *osabi = tdesc_osabi_name (e);
   if (osabi != nullptr)
diff --git a/gdbsupport/tdesc.h b/gdbsupport/tdesc.h
index 7e483486139b..5f20e6ab8627 100644
--- a/gdbsupport/tdesc.h
+++ b/gdbsupport/tdesc.h
@@ -64,6 +64,11 @@ class tdesc_element
   virtual void accept (tdesc_element_visitor &v) const = 0;
 };
 
+/* Used in vector type element count or register bitsize to indicate that
+   the corresponding value is given by a DWARF expression.  */
+
+#define TDESC_REG_VARIABLE_SIZE -1
+
 /* An individual register from a target description.  */
 
 struct tdesc_reg : tdesc_element
@@ -242,13 +247,40 @@ struct tdesc_type_vector : tdesc_type
     element_type (element_type_), count (count_)
   {}
 
+  tdesc_type_vector (const std::string &name, tdesc_type *element_type_,
+		     const gdb::array_view<const gdb_byte> locexpr_,
+		     std::optional<std::vector<const char *>> locexpr_str_)
+      : tdesc_type (name, TDESC_TYPE_VECTOR), element_type (element_type_),
+	count (TDESC_REG_VARIABLE_SIZE), locexpr (locexpr_.begin (),
+						  locexpr_.end()),
+	locexpr_str (locexpr_str_)
+  {
+    if (locexpr_str.has_value () && locexpr.size () != locexpr_str->size ())
+      error (_ ("Vector locexpr and its string representation must have the same size."));
+  }
+
   void accept (tdesc_element_visitor &v) const override
   {
     v.visit (this);
   }
 
   struct tdesc_type *element_type;
+
+  /* Ignored if LOCEXPR isn't empty. */
   int count;
+
+  /* DWARF location expression providing number of elements.  */
+  gdb::byte_vector locexpr;
+
+  /* Vector with strings to use instead of the corresponding raw bytecode
+     in C code that creates the vector type.  */
+  std::optional<std::vector<const char *>> locexpr_str;
+
+  /* XML id used to reference this location expression.  */
+  std::optional<std::string> locexpr_id;
+
+  /* XML id of location expression to be used for number of elements.  */
+  std::optional<std::string> locexpr_idref;
 };
 
 /* A named type from a target description.  */
@@ -368,6 +400,14 @@ struct tdesc_type *tdesc_create_vector (struct tdesc_feature *feature,
 					struct tdesc_type *field_type,
 					int count);
 
+/* Return the created vector tdesc_type named NAME in FEATURE,
+   with number of elements given by DWARF LOCEXPR.  */
+struct tdesc_type * tdesc_create_vector (struct tdesc_feature *feature,
+					 const char *name,
+					 struct tdesc_type *field_type,
+					 const gdb::array_view<const gdb_byte> locexpr,
+					 std::optional<std::vector<const char *>> locexpr_str = {});
+
 /* Return the created struct tdesc_type named NAME in FEATURE.  */
 tdesc_type_with_fields *tdesc_create_struct (struct tdesc_feature *feature,
 					     const char *name);
@@ -469,6 +509,13 @@ class print_xml_feature : public tdesc_element_visitor
 
   /* The current indentation depth.  */
   int m_depth;
+
+  /* The current feature's architecture, if any.  */
+  const char *m_arch = nullptr;
 };
 
+/* Convert ARCH's DWARF register number DWARF_REG to GDB's register number.
+   Returns -1 if ARCH or DWARF_REG are unknown.  */
+int tdesc_dwarf_reg_to_regnum (const char *arch, int dwarf_reg);
+
 #endif /* COMMON_TDESC_H */
