[02/11] Add new variant part code

Message ID 20200408175452.30637-3-tromey@adacore.com
State New
Headers show
Series
  • Variant part support, plus more
Related show

Commit Message

Tom Tromey April 8, 2020, 5:54 p.m.
This patch adds the infrastructure for the new variant part code.  At
this point, nothing uses this code.  This is done in a separate patch
to make it simpler to review.

I examined a few possible approaches to handling variant parts.  In
particular, I considered having a DWARF variant part be a union
(similar to how the Rust code works now); and I considered having type
fields have a flag indicating that they are variants.

Having separate types seemed bad conceptually, because these variants
aren't truly separate -- they rely on the "parent" type.  And,
changing how fields worked seemed excessively invasive.

So, in the end I thought the approach taken in this patch was both
simple to implement and understand, without losing generality.  The
idea in this patch is that all the fields of a type with variant parts
will be stored in a single field array, just as if they'd all been
listed directly.  Then, the variants are attached as a dynamic
property.  These control which fields end up in the type that's
constructed during dynamic type resolution.

gdb/ChangeLog
2020-04-08  Tom Tromey  <tromey@adacore.com>

	* gdbtypes.c (is_dynamic_type_internal): Check for variant parts.
	(variant::matches, compute_variant_fields_recurse)
	(compute_variant_fields_inner, compute_variant_fields): New
	functions.
	(resolve_dynamic_struct): Check for DYN_PROP_VARIANT_PARTS.
	Use resolved_type after type is made.
	(operator==): Add new cases.
	* gdbtypes.h (TYPE_HAS_VARIANT_PARTS): New macro.
	(struct discriminant_range, struct variant, struct variant_part):
	New.
	(union dynamic_prop_data) <variant_parts, original_type>: New
	members.
	(enum dynamic_prop_node_kind) <DYN_PROP_VARIANT_PARTS>: New constant.
	(enum dynamic_prop_kind) <PROP_TYPE, PROP_VARIANT_PARTS>: New
	constants.
	* value.c (unpack_bits_as_long): Now public.
	* value.h (unpack_bits_as_long): Declare.
---
 gdb/ChangeLog  |  20 +++++
 gdb/gdbtypes.c | 200 ++++++++++++++++++++++++++++++++++++++++++++++---
 gdb/gdbtypes.h | 105 +++++++++++++++++++++++++-
 gdb/value.c    |  20 +----
 gdb/value.h    |  21 ++++++
 5 files changed, 338 insertions(+), 28 deletions(-)

-- 
2.21.1

Patch

diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index f23def1ff71..6b32fc9a8ff 100644
--- a/gdb/gdbtypes.c
+++ b/gdb/gdbtypes.c
@@ -39,6 +39,7 @@ 
 #include "dwarf2/loc.h"
 #include "gdbcore.h"
 #include "floatformat.h"
+#include <algorithm>
 
 /* Initialize BADNESS constants.  */
 
@@ -886,6 +887,10 @@  operator== (const dynamic_prop &l, const dynamic_prop &r)
     case PROP_LOCEXPR:
     case PROP_LOCLIST:
       return l.data.baton == r.data.baton;
+    case PROP_VARIANT_PARTS:
+      return l.data.variant_parts == r.data.variant_parts;
+    case PROP_TYPE:
+      return l.data.original_type == r.data.original_type;
     }
 
   gdb_assert_not_reached ("unhandled dynamic_prop kind");
@@ -1966,6 +1971,10 @@  is_dynamic_type_internal (struct type *type, int top_level)
   if (TYPE_ALLOCATED_PROP (type))
     return 1;
 
+  struct dynamic_prop *prop = get_dyn_prop (DYN_PROP_VARIANT_PARTS, type);
+  if (prop != nullptr && prop->kind != PROP_TYPE)
+    return 1;
+
   switch (TYPE_CODE (type))
     {
     case TYPE_CODE_RANGE:
@@ -2215,6 +2224,161 @@  resolve_dynamic_union (struct type *type,
   return resolved_type;
 }
 
+/* See gdbtypes.h.  */
+
+bool
+variant::matches (ULONGEST value, bool is_unsigned) const
+{
+  for (const discriminant_range &range : discriminants)
+    if (range.contains (value, is_unsigned))
+      return true;
+  return false;
+}
+
+static void
+compute_variant_fields_inner (struct type *type,
+			      struct property_addr_info *addr_stack,
+			      const variant_part &part,
+			      std::vector<bool> &flags);
+
+/* A helper function to determine which variant fields will be active.
+   This handles both the variant's direct fields, and any variant
+   parts embedded in this variant.  TYPE is the type we're examining.
+   ADDR_STACK holds information about the concrete object.  VARIANT is
+   the current variant to be handled.  FLAGS is where the results are
+   stored -- this function sets the Nth element in FLAGS if the
+   corresponding field is enabled.  ENABLED is whether this variant is
+   enabled or not.  */
+
+static void
+compute_variant_fields_recurse (struct type *type,
+				struct property_addr_info *addr_stack,
+				const variant &variant,
+				std::vector<bool> &flags,
+				bool enabled)
+{
+  for (int field = variant.first_field; field < variant.last_field; ++field)
+    flags[field] = enabled;
+
+  for (const variant_part &new_part : variant.parts)
+    {
+      if (enabled)
+	compute_variant_fields_inner (type, addr_stack, new_part, flags);
+      else
+	{
+	  for (const auto &sub_variant : new_part.variants)
+	    compute_variant_fields_recurse (type, addr_stack, sub_variant,
+					    flags, enabled);
+	}
+    }
+}
+
+/* A helper function to determine which variant fields will be active.
+   This evaluates the discriminant, decides which variant (if any) is
+   active, and then updates FLAGS to reflect which fields should be
+   available.  TYPE is the type we're examining.  ADDR_STACK holds
+   information about the concrete object.  VARIANT is the current
+   variant to be handled.  FLAGS is where the results are stored --
+   this function sets the Nth element in FLAGS if the corresponding
+   field is enabled.  */
+
+static void
+compute_variant_fields_inner (struct type *type,
+			      struct property_addr_info *addr_stack,
+			      const variant_part &part,
+			      std::vector<bool> &flags)
+{
+  /* Evaluate the discriminant.  */
+  gdb::optional<ULONGEST> discr_value;
+  if (part.discriminant_index != -1)
+    {
+      int idx = part.discriminant_index;
+
+      if (TYPE_FIELD_LOC_KIND (type, idx) != FIELD_LOC_KIND_BITPOS)
+	error (_("Cannot determine struct field location"
+		 " (invalid location kind)"));
+
+      if (addr_stack->valaddr != NULL)
+	discr_value = unpack_field_as_long (type, addr_stack->valaddr, idx);
+      else
+	{
+	  CORE_ADDR addr = (addr_stack->addr
+			    + (TYPE_FIELD_BITPOS (type, idx)
+			       / TARGET_CHAR_BIT));
+
+	  LONGEST bitsize = TYPE_FIELD_BITSIZE (type, idx);
+	  LONGEST size = bitsize / 8;
+	  if (size == 0)
+	    size = TYPE_LENGTH (TYPE_FIELD_TYPE (type, idx));
+
+	  gdb_byte bits[sizeof (ULONGEST)];
+	  read_memory (addr, bits, size);
+
+	  LONGEST bitpos = (TYPE_FIELD_BITPOS (type, idx)
+			    % TARGET_CHAR_BIT);
+
+	  discr_value = unpack_bits_as_long (TYPE_FIELD_TYPE (type, idx),
+					     bits, bitpos, bitsize);
+	}
+    }
+
+  /* Go through each variant and see which applies.  */
+  const variant *default_variant = nullptr;
+  const variant *applied_variant = nullptr;
+  for (const auto &variant : part.variants)
+    {
+      if (variant.is_default ())
+	default_variant = &variant;
+      else if (discr_value.has_value ()
+	       && variant.matches (*discr_value, part.is_unsigned))
+	{
+	  applied_variant = &variant;
+	  break;
+	}
+    }
+  if (applied_variant == nullptr)
+    applied_variant = default_variant;
+
+  for (const auto &variant : part.variants)
+    compute_variant_fields_recurse (type, addr_stack, variant,
+				    flags, applied_variant == &variant);
+}  
+
+/* Determine which variant fields are available in TYPE.  The enabled
+   fields are stored in RESOLVED_TYPE.  ADDR_STACK holds information
+   about the concrete object.  PARTS describes the top-level variant
+   parts for this type.  */
+
+static void
+compute_variant_fields (struct type *type,
+			struct type *resolved_type,
+			struct property_addr_info *addr_stack,
+			const gdb::array_view<variant_part> &parts)
+{
+  /* Assume all fields are included by default.  */
+  std::vector<bool> flags (TYPE_NFIELDS (resolved_type), true);
+
+  /* Now disable fields based on the variants that control them.  */
+  for (const auto &part : parts)
+    compute_variant_fields_inner (type, addr_stack, part, flags);
+
+  TYPE_NFIELDS (resolved_type) = std::count (flags.begin (), flags.end (),
+					     true);
+  TYPE_FIELDS (resolved_type)
+    = (struct field *) TYPE_ALLOC (resolved_type,
+				   TYPE_NFIELDS (resolved_type)
+				   * sizeof (struct field));
+  int out = 0;
+  for (int i = 0; i < TYPE_NFIELDS (type); ++i)
+    {
+      if (!flags[i])
+	continue;
+
+      TYPE_FIELD (resolved_type, out) = TYPE_FIELD (type, i);
+      ++out;
+    }
+}
+
 /* Resolve dynamic bounds of members of the struct TYPE to static
    bounds.  ADDR_STACK is a stack of struct property_addr_info to
    be used if needed during the dynamic resolution.  */
@@ -2231,19 +2395,35 @@  resolve_dynamic_struct (struct type *type,
   gdb_assert (TYPE_NFIELDS (type) > 0);
 
   resolved_type = copy_type (type);
-  TYPE_FIELDS (resolved_type)
-    = (struct field *) TYPE_ALLOC (resolved_type,
-				   TYPE_NFIELDS (resolved_type)
-				   * sizeof (struct field));
-  memcpy (TYPE_FIELDS (resolved_type),
-	  TYPE_FIELDS (type),
-	  TYPE_NFIELDS (resolved_type) * sizeof (struct field));
+
+  struct dynamic_prop *variant_prop = get_dyn_prop (DYN_PROP_VARIANT_PARTS,
+						    resolved_type);
+  if (variant_prop != nullptr && variant_prop->kind == PROP_VARIANT_PARTS)
+    {
+      compute_variant_fields (type, resolved_type, addr_stack,
+			      *variant_prop->data.variant_parts);
+      /* We want to leave the property attached, so that the Rust code
+	 can tell whether the type was originally an enum.  */
+      variant_prop->kind = PROP_TYPE;
+      variant_prop->data.original_type = type;
+    }
+  else
+    {
+      TYPE_FIELDS (resolved_type)
+	= (struct field *) TYPE_ALLOC (resolved_type,
+				       TYPE_NFIELDS (resolved_type)
+				       * sizeof (struct field));
+      memcpy (TYPE_FIELDS (resolved_type),
+	      TYPE_FIELDS (type),
+	      TYPE_NFIELDS (resolved_type) * sizeof (struct field));
+    }
+
   for (i = 0; i < TYPE_NFIELDS (resolved_type); ++i)
     {
       unsigned new_bit_length;
       struct property_addr_info pinfo;
 
-      if (field_is_static (&TYPE_FIELD (type, i)))
+      if (field_is_static (&TYPE_FIELD (resolved_type, i)))
 	continue;
 
       /* As we know this field is not a static field, the field's
@@ -2253,11 +2433,11 @@  resolve_dynamic_struct (struct type *type,
 	 that verification indicates a bug in our code, the error
 	 is not severe enough to suggest to the user he stops
 	 his debugging session because of it.  */
-      if (TYPE_FIELD_LOC_KIND (type, i) != FIELD_LOC_KIND_BITPOS)
+      if (TYPE_FIELD_LOC_KIND (resolved_type, i) != FIELD_LOC_KIND_BITPOS)
 	error (_("Cannot determine struct field location"
 		 " (invalid location kind)"));
 
-      pinfo.type = check_typedef (TYPE_FIELD_TYPE (type, i));
+      pinfo.type = check_typedef (TYPE_FIELD_TYPE (resolved_type, i));
       pinfo.valaddr = addr_stack->valaddr;
       pinfo.addr
 	= (addr_stack->addr
diff --git a/gdb/gdbtypes.h b/gdb/gdbtypes.h
index 77cc92e419d..8b4da6e3f9f 100644
--- a/gdb/gdbtypes.h
+++ b/gdb/gdbtypes.h
@@ -51,6 +51,7 @@ 
 #include "gdbsupport/underlying.h"
 #include "gdbsupport/print-utils.h"
 #include "dwarf2.h"
+#include "gdb_obstack.h"
 
 /* Forward declarations for prototypes.  */
 struct field;
@@ -358,6 +359,10 @@  DEF_ENUM_FLAGS_TYPE (enum type_instance_flag_value, type_instance_flags);
 #define TYPE_IS_ALLOCATABLE(t) \
   (get_dyn_prop (DYN_PROP_ALLOCATED, t) != NULL)
 
+/* * True if this type has variant parts.  */
+#define TYPE_HAS_VARIANT_PARTS(t) \
+  (get_dyn_prop (DYN_PROP_VARIANT_PARTS, t) != nullptr)
+
 /* * Instruction-space delimited type.  This is for Harvard architectures
    which have separate instruction and data address spaces (and perhaps
    others).
@@ -399,6 +404,84 @@  DEF_ENUM_FLAGS_TYPE (enum type_instance_flag_value, type_instance_flags);
 #define TYPE_ADDRESS_CLASS_ALL(t) (TYPE_INSTANCE_FLAGS(t) \
 				   & TYPE_INSTANCE_FLAG_ADDRESS_CLASS_ALL)
 
+/* * Information about a single discriminant.  */
+
+struct discriminant_range
+{
+  /* * The range of values for the variant.  This is an inclusive
+     range.  */
+  ULONGEST low, high;
+
+  /* * Return true if VALUE is contained in this range.  IS_UNSIGNED
+     is true if this should be an unsigned comparison; false for
+     signed.  */
+  bool contains (ULONGEST value, bool is_unsigned) const
+  {
+    if (is_unsigned)
+      return value >= low && value <= high;
+    LONGEST valuel = (LONGEST) value;
+    return valuel >= (LONGEST) low && valuel <= (LONGEST) high;
+  }
+};
+
+struct variant_part;
+
+/* * A single variant.  A variant has a list of discriminant values.
+   When the discriminator matches one of these, the variant is
+   enabled.  Each variant controls zero or more fields; and may also
+   control other variant parts as well.  This struct corresponds to
+   DW_TAG_variant in DWARF.  */
+
+struct variant : allocate_on_obstack
+{
+  /* * The discriminant ranges for this variant.  */
+  gdb::array_view<discriminant_range> discriminants;
+
+  /* * The fields controlled by this variant.  This is inclusive on
+     the low end and exclusive on the high end.  A variant may not
+     control any fields, in which case the two values will be equal.
+     These are indexes into the type's array of fields.  */
+  int first_field;
+  int last_field;
+
+  /* * Variant parts controlled by this variant.  */
+  gdb::array_view<variant_part> parts;
+
+  /* * Return true if this is the default variant.  The default
+     variant can be recognized because it has no associated
+     discriminants.  */
+  bool is_default () const
+  {
+    return discriminants.empty ();
+  }
+
+  /* * Return true if this variant matches VALUE.  IS_UNSIGNED is true
+     if this should be an unsigned comparison; false for signed.  */
+  bool matches (ULONGEST value, bool is_unsigned) const;
+};
+
+/* * A variant part.  Each variant part has an optional discriminant
+   and holds an array of variants.  This struct corresponds to
+   DW_TAG_variant_part in DWARF.  */
+
+struct variant_part : allocate_on_obstack
+{
+  /* * The index of the discriminant field in the outer type.  This is
+     an index into the type's array of fields.  If this is -1, there
+     is no discriminant, and only the default variant can be
+     considered to be selected.  */
+  int discriminant_index;
+
+  /* * True if this discriminant is unsigned; false if signed.  This
+     comes from the type of the discriminant.  */
+  bool is_unsigned;
+
+  /* * The variants that are controlled by this variant part.  Note
+     that these will always be sorted by field number.  */
+  gdb::array_view<variant> variants;
+};
+
+
 /* * Information needed for a discriminated union.  A discriminated
    union is handled somewhat differently from an ordinary union.
 
@@ -438,7 +521,9 @@  enum dynamic_prop_kind
   PROP_CONST,     /* Constant.  */
   PROP_ADDR_OFFSET, /* Address offset.  */
   PROP_LOCEXPR,   /* Location expression.  */
-  PROP_LOCLIST    /* Location list.  */
+  PROP_LOCLIST,    /* Location list.  */
+  PROP_VARIANT_PARTS, /* Variant parts.  */
+  PROP_TYPE,	   /* Type.  */
 };
 
 union dynamic_prop_data
@@ -450,6 +535,21 @@  union dynamic_prop_data
   /* Storage for dynamic property.  */
 
   void *baton;
+
+  /* Storage of variant parts for a type.  A type with variant parts
+     has all its fields "linearized" -- stored in a single field
+     array, just as if they had all been declared that way.  The
+     variant parts are attached via a dynamic property, and then are
+     used to control which fields end up in the final type during
+     dynamic type resolution.  */
+
+  const gdb::array_view<variant_part> *variant_parts;
+
+  /* Once a variant type is resolved, we may want to be able to go
+     from the resolved type to the original type.  In this case we
+     rewrite the property's kind and set this field.  */
+
+  struct type *original_type;
 };
 
 /* * Used to store a dynamic property.  */
@@ -493,6 +593,9 @@  enum dynamic_prop_node_kind
 
   /* A property holding information about a discriminated union.  */
   DYN_PROP_DISCRIMINATED,
+
+  /* A property holding variant parts.  */
+  DYN_PROP_VARIANT_PARTS,
 };
 
 /* * List for dynamic type attributes.  */
diff --git a/gdb/value.c b/gdb/value.c
index f722c272d8b..641c09a0624 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -3090,23 +3090,9 @@  value_fn_field (struct value **arg1p, struct fn_field *f,
 
 
 
-/* Unpack a bitfield of the specified FIELD_TYPE, from the object at
-   VALADDR, and store the result in *RESULT.
-   The bitfield starts at BITPOS bits and contains BITSIZE bits; if
-   BITSIZE is zero, then the length is taken from FIELD_TYPE.
-
-   Extracting bits depends on endianness of the machine.  Compute the
-   number of least significant bits to discard.  For big endian machines,
-   we compute the total number of bits in the anonymous object, subtract
-   off the bit count from the MSB of the object to the MSB of the
-   bitfield, then the size of the bitfield, which leaves the LSB discard
-   count.  For little endian machines, the discard count is simply the
-   number of bits from the LSB of the anonymous object to the LSB of the
-   bitfield.
-
-   If the field is signed, we also do sign extension.  */
-
-static LONGEST
+/* See value.h.  */
+
+LONGEST
 unpack_bits_as_long (struct type *field_type, const gdb_byte *valaddr,
 		     LONGEST bitpos, LONGEST bitsize)
 {
diff --git a/gdb/value.h b/gdb/value.h
index 247be13a0d9..dfe3e8e3ed3 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -651,6 +651,27 @@  extern CORE_ADDR unpack_pointer (struct type *type, const gdb_byte *valaddr);
 extern LONGEST unpack_field_as_long (struct type *type,
 				     const gdb_byte *valaddr,
 				     int fieldno);
+
+/* Unpack a bitfield of the specified FIELD_TYPE, from the object at
+   VALADDR, and store the result in *RESULT.
+   The bitfield starts at BITPOS bits and contains BITSIZE bits; if
+   BITSIZE is zero, then the length is taken from FIELD_TYPE.
+
+   Extracting bits depends on endianness of the machine.  Compute the
+   number of least significant bits to discard.  For big endian machines,
+   we compute the total number of bits in the anonymous object, subtract
+   off the bit count from the MSB of the object to the MSB of the
+   bitfield, then the size of the bitfield, which leaves the LSB discard
+   count.  For little endian machines, the discard count is simply the
+   number of bits from the LSB of the anonymous object to the LSB of the
+   bitfield.
+
+   If the field is signed, we also do sign extension.  */
+
+extern LONGEST unpack_bits_as_long (struct type *field_type,
+				    const gdb_byte *valaddr,
+				    LONGEST bitpos, LONGEST bitsize);
+
 extern int unpack_value_field_as_long (struct type *type, const gdb_byte *valaddr,
 				LONGEST embedded_offset, int fieldno,
 				const struct value *val, LONGEST *result);