From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 2126) id 2575C389365C; Fri, 24 Apr 2020 20:22:05 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2575C389365C Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: Tom Tromey To: gdb-cvs@sourceware.org Subject: [binutils-gdb] Add new variant part code X-Act-Checkin: binutils-gdb X-Git-Author: Tom Tromey X-Git-Refname: refs/heads/master X-Git-Oldrev: 675127ec647e08ceabc66ec7d3ad560d91deacad X-Git-Newrev: ef83a141a291474f1364d6c64ee7a207b96b8e19 Message-Id: <20200424202205.2575C389365C@sourceware.org> Date: Fri, 24 Apr 2020 20:22:05 +0000 (GMT) X-BeenThere: gdb-cvs@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 24 Apr 2020 20:22:05 -0000 https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=ef83a141a291474f1364d6c64ee7a207b96b8e19 commit ef83a141a291474f1364d6c64ee7a207b96b8e19 Author: Tom Tromey 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 * 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) : New members. (enum dynamic_prop_node_kind) : New constant. (enum dynamic_prop_kind) : 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 + + * 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) : New + members. + (enum dynamic_prop_node_kind) : New constant. + (enum dynamic_prop_kind) : New + constants. + * value.c (unpack_bits_as_long): Now public. + * value.h (unpack_bits_as_long): Declare. + 2020-04-24 Tom Tromey * 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 /* 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 &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 &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 &flags) +{ + /* Evaluate the discriminant. */ + gdb::optional 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 &parts) +{ + /* Assume all fields are included by default. */ + std::vector 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 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 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 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_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, -/* 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);