From 4ea48cc1cd4875e570047f5aff70387018a56645 Mon Sep 17 00:00:00 2001 From: Daniel Jacobowitz Date: Tue, 21 Jul 2009 18:15:32 +0000 Subject: [PATCH] gdb/ * valops.c (value_fetch_lazy): Handle bitfields explicitly. (value_assign): Remove unnecessary FIXME. Honor the container type of bitfields if possible. * value.c (struct value): Add parent field. (value_parent): New function. (value_free): Free the parent also. (value_copy): Copy the parent also. (value_primitive_field): Do not read the contents of a lazy value to create a child bitfield value. Set bitpos and offset according to the container type if possible. (unpack_bits_as_long): Rename from unpack_field_as_long. Take field_type, bitpos, and bitsize instead of type and fieldno. (unpack_field_as_long): Use unpack_bits_as_long. * value.h (value_parent, unpack_bits_as_long): New prototypes. --- gdb/ChangeLog | 18 ++++++++++++ gdb/valops.c | 31 +++++++++++++++++++-- gdb/value.c | 77 +++++++++++++++++++++++++++++++++++++++------------ gdb/value.h | 8 ++++++ 4 files changed, 114 insertions(+), 20 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 37555d13574..161d275c83b 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,21 @@ +2009-07-21 Daniel Jacobowitz + Vladimir Prus + + * valops.c (value_fetch_lazy): Handle bitfields explicitly. + (value_assign): Remove unnecessary FIXME. Honor the container + type of bitfields if possible. + * value.c (struct value): Add parent field. + (value_parent): New function. + (value_free): Free the parent also. + (value_copy): Copy the parent also. + (value_primitive_field): Do not read the contents of a lazy + value to create a child bitfield value. Set bitpos and offset + according to the container type if possible. + (unpack_bits_as_long): Rename from unpack_field_as_long. Take + field_type, bitpos, and bitsize instead of type and fieldno. + (unpack_field_as_long): Use unpack_bits_as_long. + * value.h (value_parent, unpack_bits_as_long): New prototypes. + 2009-07-21 Daniel Jacobowitz * value.c (struct value): Add reference_count field. diff --git a/gdb/valops.c b/gdb/valops.c index 2d20b4153a1..5e5c4edeb30 100644 --- a/gdb/valops.c +++ b/gdb/valops.c @@ -632,7 +632,25 @@ value_fetch_lazy (struct value *val) { gdb_assert (value_lazy (val)); allocate_value_contents (val); - if (VALUE_LVAL (val) == lval_memory) + if (value_bitsize (val)) + { + /* To read a lazy bitfield, read the entire enclosing value. This + prevents reading the same block of (possibly volatile) memory once + per bitfield. It would be even better to read only the containing + word, but we have no way to record that just specific bits of a + value have been fetched. */ + struct type *type = check_typedef (value_type (val)); + enum bfd_endian byte_order = gdbarch_byte_order (get_type_arch (type)); + struct value *parent = value_parent (val); + LONGEST offset = value_offset (val); + LONGEST num = unpack_bits_as_long (value_type (val), + value_contents (parent) + offset, + value_bitpos (val), + value_bitsize (val)); + int length = TYPE_LENGTH (type); + store_signed_integer (value_contents_raw (val), length, byte_order, num); + } + else if (VALUE_LVAL (val) == lval_memory) { CORE_ADDR addr = value_address (val); int length = TYPE_LENGTH (check_typedef (value_enclosing_type (val))); @@ -800,13 +818,20 @@ value_assign (struct value *toval, struct value *fromval) if (value_bitsize (toval)) { - /* We assume that the argument to read_memory is in units - of host chars. FIXME: Is that correct? */ changed_len = (value_bitpos (toval) + value_bitsize (toval) + HOST_CHAR_BIT - 1) / HOST_CHAR_BIT; + /* If we can read-modify-write exactly the size of the + containing type (e.g. short or int) then do so. This + is safer for volatile bitfields mapped to hardware + registers. */ + if (changed_len < TYPE_LENGTH (type) + && TYPE_LENGTH (type) <= (int) sizeof (LONGEST) + && ((LONGEST) value_address (toval) % TYPE_LENGTH (type)) == 0) + changed_len = TYPE_LENGTH (type); + if (changed_len > (int) sizeof (LONGEST)) error (_("Can't handle bitfields which don't fit in a %d bit word."), (int) sizeof (LONGEST) * HOST_CHAR_BIT); diff --git a/gdb/value.c b/gdb/value.c index 904bd5ea9e8..65a5aa93745 100644 --- a/gdb/value.c +++ b/gdb/value.c @@ -108,6 +108,11 @@ struct value gdbarch_bits_big_endian=1 targets, it is the position of the MSB. */ int bitpos; + /* Only used for bitfields; the containing value. This allows a + single read from the target when displaying multiple + bitfields. */ + struct value *parent; + /* Frame register value is relative to. This will be described in the lval enum above as "lval_register". */ struct frame_id frame_id; @@ -398,6 +403,12 @@ set_value_bitsize (struct value *value, int bit) value->bitsize = bit; } +struct value * +value_parent (struct value *value) +{ + return value->parent; +} + gdb_byte * value_contents_raw (struct value *value) { @@ -617,6 +628,11 @@ value_free (struct value *val) if (val->reference_count > 0) return; + /* If there's an associated parent value, drop our reference to + it. */ + if (val->parent != NULL) + value_free (val->parent); + if (VALUE_LVAL (val) == lval_computed) { struct lval_funcs *funcs = val->location.computed.funcs; @@ -739,6 +755,9 @@ value_copy (struct value *arg) TYPE_LENGTH (value_enclosing_type (arg))); } + val->parent = arg->parent; + if (val->parent) + value_incref (val->parent); if (VALUE_LVAL (val) == lval_computed) { struct lval_funcs *funcs = val->location.computed.funcs; @@ -1861,15 +1880,28 @@ value_primitive_field (struct value *arg1, int offset, if (TYPE_FIELD_BITSIZE (arg_type, fieldno)) { - v = value_from_longest (type, - unpack_field_as_long (arg_type, - value_contents (arg1) - + offset, - fieldno)); - v->bitpos = TYPE_FIELD_BITPOS (arg_type, fieldno) % 8; + /* Create a new value for the bitfield, with bitpos and bitsize + set. If possible, arrange offset and bitpos so that we can + do a single aligned read of the size of the containing type. + Otherwise, adjust offset to the byte containing the first + bit. Assume that the address, offset, and embedded offset + are sufficiently aligned. */ + int bitpos = TYPE_FIELD_BITPOS (arg_type, fieldno); + int container_bitsize = TYPE_LENGTH (type) * 8; + + v = allocate_value_lazy (type); v->bitsize = TYPE_FIELD_BITSIZE (arg_type, fieldno); - v->offset = value_offset (arg1) + offset - + TYPE_FIELD_BITPOS (arg_type, fieldno) / 8; + if ((bitpos % container_bitsize) + v->bitsize <= container_bitsize + && TYPE_LENGTH (type) <= (int) sizeof (LONGEST)) + v->bitpos = bitpos % container_bitsize; + else + v->bitpos = bitpos % 8; + v->offset = value_offset (arg1) + value_embedded_offset (arg1) + + (bitpos - v->bitpos) / 8; + v->parent = arg1; + value_incref (v->parent); + if (!value_lazy (arg1)) + value_fetch_lazy (v); } else if (fieldno < TYPE_N_BASECLASSES (arg_type)) { @@ -1994,8 +2026,9 @@ value_fn_field (struct value **arg1p, struct fn_field *f, int j, struct type *ty } -/* Unpack a field FIELDNO of the specified TYPE, from the anonymous object at - VALADDR. +/* Unpack a bitfield of the specified FIELD_TYPE, from the anonymous + object at VALADDR. The bitfield starts at BITPOS bits and contains + BITSIZE bits. Extracting bits depends on endianness of the machine. Compute the number of least significant bits to discard. For big endian machines, @@ -2009,24 +2042,21 @@ value_fn_field (struct value **arg1p, struct fn_field *f, int j, struct type *ty If the field is signed, we also do sign extension. */ LONGEST -unpack_field_as_long (struct type *type, const gdb_byte *valaddr, int fieldno) +unpack_bits_as_long (struct type *field_type, const gdb_byte *valaddr, + int bitpos, int bitsize) { - enum bfd_endian byte_order = gdbarch_byte_order (get_type_arch (type)); + enum bfd_endian byte_order = gdbarch_byte_order (get_type_arch (field_type)); ULONGEST val; ULONGEST valmask; - int bitpos = TYPE_FIELD_BITPOS (type, fieldno); - int bitsize = TYPE_FIELD_BITSIZE (type, fieldno); int lsbcount; - struct type *field_type; val = extract_unsigned_integer (valaddr + bitpos / 8, sizeof (val), byte_order); - field_type = TYPE_FIELD_TYPE (type, fieldno); CHECK_TYPEDEF (field_type); /* Extract bits. See comment above. */ - if (gdbarch_bits_big_endian (get_type_arch (type))) + if (gdbarch_bits_big_endian (get_type_arch (field_type))) lsbcount = (sizeof val * 8 - bitpos % 8 - bitsize); else lsbcount = (bitpos % 8); @@ -2050,6 +2080,19 @@ unpack_field_as_long (struct type *type, const gdb_byte *valaddr, int fieldno) return (val); } +/* Unpack a field FIELDNO of the specified TYPE, from the anonymous object at + VALADDR. See unpack_bits_as_long for more details. */ + +LONGEST +unpack_field_as_long (struct type *type, const gdb_byte *valaddr, int fieldno) +{ + int bitpos = TYPE_FIELD_BITPOS (type, fieldno); + int bitsize = TYPE_FIELD_BITSIZE (type, fieldno); + struct type *field_type = TYPE_FIELD_TYPE (type, fieldno); + + return unpack_bits_as_long (field_type, valaddr, bitpos, bitsize); +} + /* Modify the value of a bitfield. ADDR points to a block of memory in target byte order; the bitfield starts in the byte pointed to. FIELDVAL is the desired value of the field, in host byte order. BITPOS and BITSIZE diff --git a/gdb/value.h b/gdb/value.h index 039e160bfe7..29ad783d429 100644 --- a/gdb/value.h +++ b/gdb/value.h @@ -76,6 +76,12 @@ extern void set_value_bitsize (struct value *, int bit); extern int value_bitpos (struct value *); extern void set_value_bitpos (struct value *, int bit); +/* Only used for bitfields; the containing value. This allows a + single read from the target when displaying multiple + bitfields. */ + +struct value *value_parent (struct value *); + /* Describes offset of a value within lval of a structure in bytes. If lval == lval_memory, this is an offset to the address. If lval == lval_register, this is a further offset from location.address @@ -329,6 +335,8 @@ extern LONGEST unpack_long (struct type *type, const gdb_byte *valaddr); extern DOUBLEST unpack_double (struct type *type, const gdb_byte *valaddr, int *invp); extern CORE_ADDR unpack_pointer (struct type *type, const gdb_byte *valaddr); +LONGEST unpack_bits_as_long (struct type *field_type, const gdb_byte *valaddr, + int bitpos, int bitsize); extern LONGEST unpack_field_as_long (struct type *type, const gdb_byte *valaddr, int fieldno); -- 2.30.2