how to support C type qualifiers applied to arrays?

Gary Funck
2006-11-27T22:42:10Z

Consider the following:

[config: Fedora Core 5, x86_64, gdb 6.3 and GCC 4.1.]

% cat -n const_array.c
1 const volatile int array[5] = {1,2,3,4,5};
2 volatile int s;
3
4 int
5 main()
6 {
7 int i;
8 for (i = 0, s = 0; i < 5; ++i) s += array[i];
9 }

Above, what we'd like to see is that the qualifiers
"const" and "volatile" on references to the array named
'array' are known to gdb.

Not so:

(gdb) ptype s
type = volatile int
(gdb) ptype array
type = int [5]
(gdb) ptype array[4]
type = int

The lack of c-v qualifiers on 'array' above, probably doesn't
impact GDB or its users very much at the moment. However,
it does impact the development of GDB support for
an experimental dialect of "C" called UPC (see http://upc.gwu.edu),
which is something that I'm working on at the moment.

In UPC a new qualifier, "shared", is added to indicate that a variable
is shared across all UPC threads. "shared" can be applied to
scalars, arrays, structures and other "C" language objects.
Thus, if 'array' above is to be shared, it would be declared:

shared int array[5];

Accesses to UPC shared objects cannot generally be implemented
as direct loads and stores. The UPC compiler also needs to
know that an array indexing operation is being performed
on a shared array, because UPC's indexing operation propagates
over every thread (A[i+1] may index the data on the next thread
in sequence from the thread with affinity to A[i], for example).
GDB will need to know about the UPC "shared" qualifier
as well so that GDB can properly access UPC shared objects.

Extending C-V qualifiers to include other qualifiers within
GDB isn't difficult. However, GDB's lack of support for C-V
qualifiers on array types is a major obstacle to properly supporting
accesses to UPC shared arrays.

I'm wondering if anyone has looked at how GDB handles qualifiers
on array types and array accesses, or if you can suggest how
that might best be supported?

-----

The discussion below adds detail, and explores a few
alternatives.

Running "objdump -W const_array", we can print out the
DWARF information.

Here is the declaration of "array":

<1><c6>: Abbrev Number: 7 (DW_TAG_variable)
DW_AT_name : array
DW_AT_decl_file : 1
DW_AT_decl_line : 1
DW_AT_type : <de>
DW_AT_external : 1
DW_AT_location : 9 byte block: 3 60 8 50 0 0 0 0 0 (DW_OP_addr:
500860)

The type definition is at die offset <de>. Following
that reference, we see the chain of const and volatile:

<1><de>: Abbrev Number: 8 (DW_TAG_const_type)
DW_AT_type : <e3>
<1><e3>: Abbrev Number: 9 (DW_TAG_volatile_type)
DW_AT_type : <a1>

Where the underlying array type is given by <a1>:

<1><a1>: Abbrev Number: 5 (DW_TAG_array_type)
DW_AT_sibling : <b1>
DW_AT_type : <9a>

<1><9a>: Abbrev Number: 4 (DW_TAG_base_type)
DW_AT_name : int
DW_AT_byte_size : 4
DW_AT_encoding : 5 (signed)

There are two ways of looking at the debugging info.
generated above:

1. The DWARF 2 info. accurately reflects the original
declaration.

2. The "const" and "volatile" attributes should be
propagated to the element type. Thus above, we should
have seen the type of "array" (schematically) as:

(array_type (domain int 5)
(element_type
(const (volatile (base_type int)))))

-----

A few questions that arise:

Q: Is GCC generating an inaccurate DWARF 2 representation
to describe a qualified array type, or is GDB not
interpreting the DWARF 2 information correctly?

Q: Which representation (associating qualifiers with the
array or with the array's ultimate element type) most
accurately reflects "C" semantics?

Due to the large installed GCC installed base (as well as that of
other compilers which generate DWARF 2 information in a similar
fashion to GCC), we maximize interoperability
if we localize the representation issue to GDB, rather
than going back to GCC to generate a different
Dwarf 2 representation.

Taking a look at read_tag_const_type() in dwarf2read.c:

static void
read_tag_const_type (struct die_info *die, struct dwarf2_cu *cu)
{
struct type *base_type;

if (die->type)
{
return;
}

base_type = die_type (die, cu);
set_die_type (die, make_cv_type (1, TYPE_VOLATILE (base_type), base_type,
0),
cu);
}

We see that make_cv_type() is called to qualify 'base_type' as "const".
Make_cv_type() does this by setting a flag bit in the 'instance_flags'
field of base_type. In our example above, 'base_type' is an array type.

Thus, gdb applies the C-V qualifiers encoded in the DWARF 2 representation
to the containing array type, but does not propagate these qualifiers to
the element type.

-----

In order to make GDB aware that an array type, or an indexed array
element is qualified, two approaches come to mind:

1) Encode the fact that the array's elements are C-V qualified
by propagating the C-V qualifiers down to the element type.
If we know that base_type above isn't otherwise aliased (i.e.,
linked into some other type chain), we could try something
like the following:

base_type = die_type (die, cu);
if (TYPE_CODE (base_type) == TYPE_CODE_ARRAY)
{
struct type *final_array_type = type;
struct type *final_elem_type TYPE_TARGET_TYPE (final_array_type);
/* Propagate qualifiers down to the element type of an array. */
while (TYPE_CODE (final_elem_type) == TYPE_CODE_ARRAY)
{
final_array_type = final_elem_type;
final_elem_type = TYPE_TARGET_TYPE (final_array_type);
}
final_elem_type = make_cv_type (1, TYPE_VOLATILE (final_elem_type),
final_elem_type, 0);
TYPE_TARGET_TYPE (final_array_type) = final_elem_type;
}
else
{
base_type = make_cv_type (1, TYPE_VOLATILE (base_type), base_type, 0);
}
set_die_type (die, base_type, cu);

If make_cv_type() were generalized to accept various qualifier flags, we
could place the logic that propagates the C-V qualifiers into this
new implementation of make_cv_type(), thus simplifying the callers like
read_tag_const_type() above, so that they are unaware of the propagation.

2) Dynamically compute whether and array type or array indexing
operation will result in a qualified type. This moves
the calculation shown above into GDB's expression evaluation logic.
Performing the propagation of type qualifiers in the expression
evaluator is further complicated by the fact that the
qualifier information will have to be propagated up and down the tree.

For example:

typedef const int c5_t[5];
typedef volatile c5_t v10_c5_t[10];
v10_c5_t A[20];

if we ask:

) ptype A[10]

we'd probably expect to see:

volatile const int [10][5]

which traverses down the tree, to the element type in order to
calculate the final type of the array elements. Thus, we might
view the "const" and "volatile" qualifiers as being propagated
up the tree to any referencing type or value expression.

Similarly,

) ptype A[10][9]

should yield

volatile const int [5]

which propagates the volatile attribute of A[10][9] down the expression
tree and combines it with the const qualifier of the c5_t type.

-----

I haven't looked at GDB's expression evaluator to determine if the dynamic
calculation of the qualifiers is feasible or not. The dynamic calculation
method has the advantage that it leaves the symbol readers alone and can
also properly handle cross object file references (which are handled via
symbolic tag references).

I'd appreciate hearing any comments/ideas that you have on this
topic, including whether I've properly characterized the problem
and the proposed solutions.