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:
parent
675127ec64
commit
ef83a141a2
|
@ -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".
|
||||
|
|
200
gdb/gdbtypes.c
200
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
|
||||
|
|
105
gdb/gdbtypes.h
105
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. */
|
||||
|
|
18
gdb/value.c
18
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.
|
||||
/* 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)
|
||||
{
|
||||
|
|
21
gdb/value.h
21
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);
|
||||
|
|
Loading…
Reference in New Issue