public inbox for gdb-cvs@sourceware.org
help / color / mirror / Atom feed
* [binutils-gdb] Add new variant part code
@ 2020-04-24 20:22 Tom Tromey
0 siblings, 0 replies; only message in thread
From: Tom Tromey @ 2020-04-24 20:22 UTC (permalink / raw)
To: gdb-cvs
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=ef83a141a291474f1364d6c64ee7a207b96b8e19
commit ef83a141a291474f1364d6c64ee7a207b96b8e19
Author: Tom Tromey <tromey@adacore.com>
Date: Fri Apr 24 13:40:31 2020 -0600
Add new variant part code
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-24 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.
Diff:
---
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(-)
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 9645eadc9e6..b5128802cd2 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,23 @@
+2020-04-24 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.
+
2020-04-24 Tom Tromey <tromey@adacore.com>
* rs6000-tdep.c (struct ppc_variant): Rename from "variant".
diff --git a/gdb/gdbtypes.c b/gdb/gdbtypes.c
index 157b3c5e614..c2a1d221377 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 6e2a9ba4fcc..5ae0b32f4fc 100644
--- a/gdb/value.c
+++ b/gdb/value.c
@@ -3090,23 +3090,9 @@ value_fn_field (struct value **arg1p, struct fn_field *f,
\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);
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2020-04-24 20:22 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-24 20:22 [binutils-gdb] Add new variant part code Tom Tromey
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).