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.
This commit is contained in:
Tom Tromey 2020-04-24 13:40:31 -06:00
parent 675127ec64
commit ef83a141a2
5 changed files with 337 additions and 27 deletions

View File

@ -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".

View File

@ -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

View File

@ -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. */

View File

@ -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.
/* See value.h. */
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
LONGEST
unpack_bits_as_long (struct type *field_type, const gdb_byte *valaddr,
LONGEST bitpos, LONGEST bitsize)
{

View File

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