On Tue, May 6, 2025 at 9:34 PM David Faust <david.fa...@oracle.com> wrote: > > > > On 5/6/25 01:24, Richard Biener wrote: > > On Mon, May 5, 2025 at 10:40 PM David Faust <david.fa...@oracle.com> wrote: > >> > >> > >> > >> On 5/2/25 01:26, Richard Biener wrote: > >>> On Wed, Apr 30, 2025 at 7:26 PM David Faust <david.fa...@oracle.com> > >>> wrote: > >>>> > >>>> The btf_decl_tag and btf_type_tag attributes provide a means to annotate > >>>> declarations and types respectively with arbitrary user provided > >>>> strings. These strings are recorded in debug information for > >>>> post-compilation uses, and despite the name they are meant to be > >>>> recorded in DWARF as well as BTF. New DWARF extensions > >>>> DW_TAG_GNU_annotation and DW_AT_GNU_annotation are used to represent > >>>> these user annotations in DWARF. > >>>> > >>>> This patch introduces the new DWARF extension DIE and attribute, and > >>>> generates them as necessary to represent user annotations from > >>>> btf_decl_tag and btf_type_tag. > >>>> > >>>> The format of the new DIE is as follows: > >>>> > >>>> DW_TAG_GNU_annotation > >>>> DW_AT_name: "btf_decl_tag" or "btf_type_tag" > >>>> DW_AT_const_value: <arbitrary user-supplied string> > >>>> DW_AT_GNU_annotation: <reference to another TAG_GNU_annotation > >>>> DIE> > >>>> > >>>> DW_AT_GNU_annotation is a new attribute extension used to refer to these > >>>> new annotation DIEs. If non-null in any given declaration or type DIE, > >>>> it is a reference to a DW_TAG_GNU_annotation DIE holding an annotation > >>>> for that declaration or type. In addition, the DW_TAG_GNU_annotation > >>>> DIEs may also have a non-null DW_AT_GNU_annotation, referring to another > >>>> annotation DIE. This allows chains of annotation DIEs to be formed, > >>>> such as in the case where a single declaration has multiple instances of > >>>> btf_decl_tag with different string annotations. > >>>> > >>>> gcc/ > >>>> * dwarf2out.cc (struct annotation_node, struct > >>>> annotation_node_hasher) > >>>> (btf_tag_htab): New ancillary structures and hash table. > >>>> (annotation_node_hasher::hash, annotation_node_hasher::equal): > >>>> New. > >>>> (hash_btf_tag, gen_btf_tag_dies, gen_btf_type_tag_dies) > >>>> (maybe_gen_btf_type_tag_dies, gen_btf_decl_tag_dies): New > >>>> functions. > >>>> (modified_type_die): Handle btf_type_tag attribute. > >>>> (gen_array_type_die): Call maybe_gen_btf_type_tags for the type. > >>>> (gen_formal_parameter_die): Call gen_btf_decl_tags for the > >>>> parameter. > >>>> (gen_decl_die): Call gen_btf_decl_tags for the decl. > >>>> (gen_tagged_type_die): Call maybe_gen_btf_type_tag_dies for the > >>>> type. > >>>> (dwarf2out_early_finish): Empty btf_tag_htab hash table. > >>>> (dwarf2out_cc_finalize): Delete btf_tag_htab hash table. > >>>> > >>>> include/ > >>>> * dwarf2.def (DW_TAG_GNU_annotation): New DWARF extension. > >>>> (DW_AT_GNU_annotation): Likewise. > >>>> > >>>> gcc/testsuite/ > >>>> * gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-1.c: New test. > >>>> * gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-2.c: New test. > >>>> * gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-3.c: New test. > >>>> * gcc.dg/debug/dwarf2/dwarf-btf-type-tag-1.c: New test. > >>>> * gcc.dg/debug/dwarf2/dwarf-btf-type-tag-2.c: New test. > >>>> * gcc.dg/debug/dwarf2/dwarf-btf-type-tag-3.c: New test. > >>>> * gcc.dg/debug/dwarf2/dwarf-btf-type-tag-4.c: New test. > >>>> * gcc.dg/debug/dwarf2/dwarf-btf-type-tag-5.c: New test. > >>>> --- > >>>> gcc/dwarf2out.cc | 270 +++++++++++++++++- > >>>> .../debug/dwarf2/dwarf-btf-decl-tag-1.c | 11 + > >>>> .../debug/dwarf2/dwarf-btf-decl-tag-2.c | 25 ++ > >>>> .../debug/dwarf2/dwarf-btf-decl-tag-3.c | 21 ++ > >>>> .../debug/dwarf2/dwarf-btf-type-tag-1.c | 10 + > >>>> .../debug/dwarf2/dwarf-btf-type-tag-2.c | 31 ++ > >>>> .../debug/dwarf2/dwarf-btf-type-tag-3.c | 15 + > >>>> .../debug/dwarf2/dwarf-btf-type-tag-4.c | 33 +++ > >>>> .../debug/dwarf2/dwarf-btf-type-tag-5.c | 10 + > >>>> include/dwarf2.def | 4 + > >>>> 10 files changed, 426 insertions(+), 4 deletions(-) > >>>> create mode 100644 > >>>> gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-1.c > >>>> create mode 100644 > >>>> gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-2.c > >>>> create mode 100644 > >>>> gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-3.c > >>>> create mode 100644 > >>>> gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-1.c > >>>> create mode 100644 > >>>> gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-2.c > >>>> create mode 100644 > >>>> gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-3.c > >>>> create mode 100644 > >>>> gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-4.c > >>>> create mode 100644 > >>>> gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-5.c > >>>> > >>>> diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc > >>>> index 34ffeed86ff..1ec3b24e773 100644 > >>>> --- a/gcc/dwarf2out.cc > >>>> +++ b/gcc/dwarf2out.cc > >>>> @@ -3696,6 +3696,32 @@ static bool frame_pointer_fb_offset_valid; > >>>> > >>>> static vec<dw_die_ref> base_types; > >>>> > >>>> +/* A cached btf_type_tag or btf_decl_tag user annotation. */ > >>>> +struct GTY ((for_user)) annotation_node > >>>> +{ > >>>> + const char *name; > >>>> + const char *value; > >>>> + hashval_t hash; > >>>> + dw_die_ref die; > >>>> + struct annotation_node *next; > >>>> +}; > >>>> + > >>>> +struct annotation_node_hasher : ggc_ptr_hash<annotation_node> > >>>> +{ > >>>> + typedef const struct annotation_node *compare_type; > >>>> + > >>>> + static hashval_t hash (struct annotation_node *); > >>>> + static bool equal (const struct annotation_node *, > >>>> + const struct annotation_node *); > >>>> +}; > >>>> + > >>>> +/* A hash table of tag annotation nodes for btf_type_tag and > >>>> btf_decl_tag C > >>>> + attributes. DIEs for these user annotations may be reused if they > >>>> are > >>>> + structurally equivalent; this hash table is used to ensure the DIEs > >>>> are > >>>> + reused wherever possible. */ > >>>> +static GTY (()) hash_table<annotation_node_hasher> *btf_tag_htab; > >>>> + > >>>> + > >>>> /* Flags to represent a set of attribute classes for attributes that > >>>> represent > >>>> a scalar value (bounds, pointers, ...). */ > >>>> enum dw_scalar_form > >>>> @@ -13659,6 +13685,180 @@ long_double_as_float128 (tree type) > >>>> return NULL_TREE; > >>>> } > >>>> > >>>> + > >>>> +hashval_t > >>>> +annotation_node_hasher::hash (struct annotation_node *node) > >>>> +{ > >>>> + return node->hash; > >>>> +} > >>>> + > >>>> +bool > >>>> +annotation_node_hasher::equal (const struct annotation_node *node1, > >>>> + const struct annotation_node *node2) > >>>> +{ > >>>> + return (node1->hash == node2->hash); > >>> > >>> never any hash collision? I'd have expected name/value to be compared > >>> here, > >>> and also 'next' (the "chain")? > >> > >> Thanks, you're right. name/value/chain go into the hash but I need to > >> check for collisions here. > >> > >>> > >>> Are we ever able to share partial chains this way? LTO streaming does > >>> for example "share" list tails. I think attribute list generation > >>> does not perform > >>> any attempt to share. > >> > >> Yes, we are able to share common list tails of the attribute. But it does > >> depend on the ordering of the attribute list that we get. > >> > >> For example, if __tagN are all btf_type_tag("tagN") attributes: > >> > >> int * __tag1 __tag2 __tag3 foo; > >> char * __tag1 __tag2 __tag4 bar; > >> > >> In this case the sub-chain for (__tag1 __tag2) is shared, because the > >> attribute lists for the two types have them in the same order > >> (the actual tree_list nodes are not shared despite same contents). > >> Then __tag3 or __tag4 can be tacked on to the head of that common tail, > >> and the result is only one TAG_annotation DIE for each tag is produced. > >> > >> But if we add: > >> > >> float __tag2 __tag1 __tag3 baz; > >> > >> Currently this will not share the above sub-chain, because the attribute > >> list for this type has a different ordering than above. > >> > >> This could be improved by enforcing our own ordering when processing the > >> attribute list for tags, or maybe better by enforcing an ordering when > >> the attribute list is constructed. That would require looking also at > >> the attribute value, which I think we would need to do anyway as a step > >> to making attribute lists sharable. I may look into that in the future, > >> but IMO a little out of scope of this series. > >> > >>> > >>>> +} > >>>> + > >>>> +/* Return an appropriate entry in the btf tag hash table for a given > >>>> btf tag. > >>>> + If a structurally equivalent tag (one with the same name, value, and > >>>> + subsequent chain of further tags) has already been processed, then > >>>> the > >>>> + existing entry for that tag is returned and should be reused. > >>>> + Otherwise, a new entry is added to the hash table and returned. */ > >>>> + > >>>> +static struct annotation_node * > >>>> +hash_btf_tag (tree attr) > >>>> +{ > >>>> + if (attr == NULL_TREE || TREE_CODE (attr) != TREE_LIST) > >>>> + return NULL; > >>>> + > >>>> + if (!btf_tag_htab) > >>>> + btf_tag_htab = hash_table<annotation_node_hasher>::create_ggc (10); > >>>> + > >>>> + const char * name = IDENTIFIER_POINTER (get_attribute_name (attr)); > >>>> + const char * value = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE > >>>> (attr))); > >>>> + tree chain = lookup_attribute (name, TREE_CHAIN (attr)); > >>>> + > >>>> + /* Hash for one tag depends on hash of next tag in the chain, because > >>>> + the chain is part of structural equivalence. */ > >>>> + struct annotation_node *chain_node = hash_btf_tag (chain); > >>>> + gcc_checking_assert (chain == NULL_TREE || chain_node != NULL); > >>>> + > >>>> + /* Skip any non-btf-tag attributes that might be in the chain. */ > >>>> + if (strcmp (name, "btf_type_tag") != 0 && strcmp (name, > >>>> "btf_decl_tag") != 0) > >>>> + return chain_node; > >>>> + > >>>> + /* Hash for a given tag is determined by the name, value, and chain of > >>>> + further tags. */ > >>>> + inchash::hash h; > >>>> + h.merge_hash (htab_hash_string (name)); > >>>> + h.merge_hash (htab_hash_string (value)); > >>>> + h.merge_hash (chain_node ? chain_node->hash : 0); > >>>> + > >>>> + struct annotation_node node; > >>>> + node.name = name; > >>>> + node.value = value; > >>>> + node.hash = h.end (); > >>>> + node.next = chain_node; > >>>> + > >>>> + struct annotation_node **slot = btf_tag_htab->find_slot (&node, > >>>> INSERT); > >>>> + if (*slot == NULL) > >>>> + { > >>>> + /* Create new htab entry for this annotation. */ > >>>> + struct annotation_node *new_slot > >>>> + = ggc_cleared_alloc<struct annotation_node> (); > >>>> + new_slot->name = name; > >>>> + new_slot->value = value; > >>>> + new_slot->hash = node.hash; > >>>> + new_slot->next = chain_node; > >>>> + > >>>> + *slot = new_slot; > >>>> + return new_slot; > >>>> + } > >>>> + else > >>>> + { > >>>> + /* This node is already in the hash table. */ > >>>> + return *slot; > >>>> + } > >>>> +} > >>>> + > >>>> +/* Generate (or reuse) DW_TAG_annotation DIEs representing the > >>>> btf_type_tag or > >>>> + btf_decl_tag user annotations in ATTR, and update DIE to refer to > >>>> them > >>>> + via DW_AT_annotation. If there are multiple type_tag or decl_tag > >>>> + annotations in ATTR, they are all processed recursively by this > >>>> function > >>>> + to build a chain of annotation DIEs. > >>>> + Return the first annotation DIE in the created (or reused) chain. */ > >>>> + > >>>> +static dw_die_ref > >>>> +gen_btf_tag_dies (tree attr, dw_die_ref die, dw_die_ref context_die) > >>>> +{ > >>>> + if (attr == NULL_TREE) > >>>> + return die; > >>>> + > >>>> + while (dw_get_die_tag (context_die) != DW_TAG_compile_unit) > >>>> + context_die = context_die->die_parent; > >>>> + > >>>> + const char * name = IDENTIFIER_POINTER (get_attribute_name (attr)); > >>>> + const char * value = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE > >>>> (attr))); > >>>> + > >>>> + dw_die_ref tag_die, prev = NULL; > >>>> + > >>>> + /* Multiple annotations on the same item form a singly-linked list of > >>>> + annotation DIEs; generate recursively backward from the end so we > >>>> can > >>>> + chain each created DIE to the next, which has already been > >>>> created. */ > >>>> + tree rest = lookup_attribute (name, TREE_CHAIN (attr)); > >>>> + if (rest) > >>>> + prev = gen_btf_tag_dies (rest, NULL, context_die); > >>>> + > >>>> + /* Calculate a hash value for the tag based on its structure, find the > >>>> + existing entry for it (if any) in the hash table, or create a new > >>>> entry > >>>> + which can be reused by structurally-equivalent tags. */ > >>>> + struct annotation_node *entry = hash_btf_tag (attr); > >>>> + if (!entry) > >>>> + return die; > >>>> + > >>>> + /* If the node already has an associated DIE, reuse it. > >>>> + Otherwise, create the new annotation DIE, and associate it with > >>>> + the hash table entry for future reuse. Any structurally-equivalent > >>>> + tag we process later will find and share the same DIE. */ > >>>> + if (entry->die) > >>>> + tag_die = entry->die; > >>>> + else > >>>> + { > >>>> + tag_die = new_die (DW_TAG_GNU_annotation, context_die, NULL); > >>>> + add_name_attribute (tag_die, name); > >>>> + add_AT_string (tag_die, DW_AT_const_value, value); > >>>> + if (prev) > >>>> + add_AT_die_ref (tag_die, DW_AT_GNU_annotation, prev); > >>> > >>> So I can see this saves DWARF space compared to adding multiple > >>> DW_AT_GNU_annotation to the annotated object. It does feel a bit > >>> un-DWARFish, I suppose using DIE children (with the same tag?) > >>> would have been more DWARFish here. That would of course > >>> remove the ability to share sub-lists (but as said, I'm unsure this > >>> works now). So for a three-item list you'd have > >>> > >>> DW_TAG_GNU_annotation > >>> name > >>> value > >>> DW_TAG_GNU_annotation (first child) > >>> name > >>> value > >>> DW_TAG_GNU_annotation (second child) > >>> name > >>> value > >>> > >>> or alternatively, make the outer one "empty" (a container) > >>> and add a third child with the above outers name/value. > >>> > >>> It's a bit of bike-shedding of course. > >>> > >> > >> Hmm, interesting. This is a little similar to what clang does now, > >> except that they attach the annotation DIEs as children directly to > >> the type DIE, which eliminates sharing altogether and was the main > >> driver to look into a different format. > >> > >> So iiuc this is a sort of hybrid, to use the new AT_annotation > >> along with the simpler child-DIE approach. That way we can still > >> share since all types with the same set of tags can use AT_annotation > >> to point to the same TAG_annotation DIE. > >> > >> I agree this is probably a little more simple and a little more > >> DWARFish. Not sure whether or not the tradeoff with sharing sub-chains > >> is worth it. > >> > >> The way type_tag is currently used in the kernel, the common case is > >> that there is a small set of distinct values (e.g. "user", "kernel", > >> "rcu", a few others) and there are many occurrences types which have > >> one or maybe two of these tags. I do think type_tags are an interesting > >> concept which have several potential applications beyond the kernel > >> use case though. > >> > >>>> + > >>>> + entry->die = tag_die; > >>>> + } > >>>> + > >>>> + if (die) > >>>> + add_AT_die_ref (die, DW_AT_GNU_annotation, tag_die); > >>>> + > >>>> + return tag_die; > >>>> +} > >>>> + > >>>> +static void > >>>> +gen_btf_type_tag_dies (tree t, tree attr, dw_die_ref target, > >>>> + dw_die_ref context_die) > >>>> +{ > >>>> + if (t == NULL_TREE || !TYPE_P (t) || !target) > >>>> + return; > >>>> + > >>>> + gen_btf_tag_dies (attr, target, context_die); > >>>> +} > >>>> + > >>>> +static void > >>>> +maybe_gen_btf_type_tag_dies (tree t, dw_die_ref die, dw_die_ref > >>>> context_die) > >>>> +{ > >>>> + tree tags = lookup_attribute ("btf_type_tag", TYPE_ATTRIBUTES (t)); > >>>> + if (tags) > >>>> + { > >>>> + TYPE_ATTRIBUTES (t) > >>>> + = remove_attribute ("btf_type_tag", TYPE_ATTRIBUTES (t)); > >>>> + gen_btf_type_tag_dies (t, tags, die, context_die); > >>>> + } > >>>> +} > >>>> + > >>> > >>> Most of the functions miss a function-level comment. > >> > >> Oops, thanks. > >> > >>> > >>>> +static void > >>>> +gen_btf_decl_tag_dies (tree t, dw_die_ref target, dw_die_ref > >>>> context_die) > >>>> +{ > >>>> + if (t == NULL_TREE || !DECL_P (t) || !target) > >>>> + return; > >>>> + > >>>> + tree attr = lookup_attribute ("btf_decl_tag", DECL_ATTRIBUTES (t)); > >>>> + if (attr == NULL_TREE) > >>>> + return; > >>>> + > >>>> + gen_btf_tag_dies (attr, target, context_die); > >>>> + > >>>> + /* Strip the decl tag attribute once we have created the annotation > >>>> DIEs > >>>> + to avoid attempting process it multiple times. */ > >>>> + DECL_ATTRIBUTES (t) > >>>> + = remove_attribute ("btf_decl_tag", DECL_ATTRIBUTES (t)); > >>> > >>> Hmm, you should get a type/decl DIE created only once, so why would you > >>> run into this multiple times? > >> > >> For decls, I see that we go through gen_decl_die twice for global > >> variables; > >> first from c_parser_declaration_or_fndef via finish_decl, and then again > >> later from c_write_global_declarations_1. > >> > >> For types, it is due to the recursion in modified_type_die. > >> > >>> > >>>> +} > >>>> + > >>>> /* Given a pointer to an arbitrary ..._TYPE tree node, return a > >>>> debugging > >>>> entry that chains the modifiers specified by CV_QUALS in front of the > >>>> given type. REVERSE is true if the type is to be interpreted in the > >>>> @@ -13783,10 +13983,44 @@ modified_type_die (tree type, int cv_quals, > >>>> bool reverse, > >>>> dquals &= cv_qual_mask; > >>>> if ((dquals & ~cv_quals) != TYPE_UNQUALIFIED > >>>> || (cv_quals == dquals && DECL_ORIGINAL_TYPE (name) != > >>>> type)) > >>>> - /* cv-unqualified version of named type. Just use > >>>> - the unnamed type to which it refers. */ > >>>> - return modified_type_die (DECL_ORIGINAL_TYPE (name), > >>>> cv_quals, > >>>> - reverse, context_die); > >>>> + { > >>>> + if (lookup_attribute ("btf_type_tag", TYPE_ATTRIBUTES > >>>> (type))) > >>>> + { > >>>> + /* Use of a typedef with additional btf_type_tags. > >>>> Create > >>>> + a distinct typedef DIE for this version of the > >>>> named type > >>>> + so that the btf_type_tag annotations may be > >>>> attached to > >>>> + it without affecting other users of the plain > >>>> typedef. */ > >>>> + tree tags = lookup_attribute ("btf_type_tag", > >>>> + TYPE_ATTRIBUTES (type)); > >>> > >>> you did this in the if () check already. Note I think this is only > >>> executed > >>> when the type was qualified or typedef-ed? > >> > >> OK, will fix/reuse the lookup_attribute in the if() here and elsewhere. > >> > >> Yes, this block is for dealing with type_tag on typedefs, since it is > >> possible to add a type_tag at the use of a typedef'd type like > >> > >> typedef const int __tag1 my_int; > >> my_int __tag2 a; > >> my_int __tag3 b; > >> my_int c; > >> > >> We need to construct a few distinct typedef DIEs here so that the different > >> tags can be attached to their respective versions without disrupting other > >> users of the underlying typedef. > >> > >>> > >>>> + tree dtags = lookup_attribute ("btf_type_tag", > >>>> + TYPE_ATTRIBUTES > >>>> (dtype)); > >>>> + > >>>> + /* Remove type tags before the recursive call to avoid > >>>> + processing the same attribute multiple times. */ > >>>> + TYPE_ATTRIBUTES (type) > >>>> + = remove_attribute ("btf_type_tag", TYPE_ATTRIBUTES > >>>> (type)); > >>>> + > >>>> + dw_die_ref mod_die = modified_type_die (dtype, > >>>> cv_quals, > >>>> + reverse, > >>>> context_die); > >>>> + > >>>> + /* Create a new typedef DIE since the btf_type_tag-ed > >>>> use of > >>>> + the typedef has really created a distinct type. */ > >>>> + if (!attribute_list_equal (tags, dtags)) > >>>> + { > >>>> + mod_die = clone_die (mod_die); > >>>> + add_child_die (comp_unit_die (), mod_die); > >>>> + if (!lookup_type_die (type)) > >>>> + equate_type_number_to_die (type, mod_die); > >>>> + } > >>>> + > >>>> + gen_btf_type_tag_dies (type, tags, mod_die, > >>>> context_die); > >>>> + return mod_die; > >>>> + } > >>>> + /* cv-unqualified version of named type. Just use > >>>> + the unnamed type to which it refers. */ > >>>> + return modified_type_die (DECL_ORIGINAL_TYPE (name), > >>>> cv_quals, > >>>> + reverse, context_die); > >>>> + } > >>>> /* Else cv-qualified version of named type; fall through. */ > >>>> } > >>>> } > >>>> @@ -13887,6 +14121,18 @@ modified_type_die (tree type, int cv_quals, > >>>> bool reverse, > >>>> first_quals |= dwarf_qual_info[i].q; > >>>> } > >>>> } > >>>> + else if (lookup_attribute ("btf_type_tag", TYPE_ATTRIBUTES (type))) > >>>> + { > >>>> + /* Remove type tags before the recursive call to avoid processing > >>>> the > >>>> + same attribute multiple times. */ > >>>> + tree attr = lookup_attribute ("btf_type_tag", TYPE_ATTRIBUTES > >>>> (type)); > >>> > >>> twice again - if (tree attr = lookup_...) is a thing. > >>> > >>>> + TYPE_ATTRIBUTES (type) = remove_attribute ("btf_type_tag", > >>>> + TYPE_ATTRIBUTES (type)); > >>>> + dw_die_ref mod_die = modified_type_die (type, cv_quals, reverse, > >>>> + context_die); > >>> > >>> While recusing might look "convenient" here I think given > >>> modified_type_die's > >>> complexity a more obvious handling should be prefered ;) > >> > >> Yes.. I have had quite some trouble with modified_type_die and doing the > >> right > >> thing in all various constructs with typedefs, cv-quals, and now type_tags > >> as > >> another axis of complexity. > >> > >> Did you a particular approach in mind? > >> I have tried a few non-recursive alternatives, but I didn't land on > >> anything > >> that was actually easier to reason about than the recursive call, without > >> duplicating a lot of logic already written elsewhere. > >> > >> It seems to me that in general we need the recursion, following the same > >> approach as is done for cv-qual'd types above: process the qualifier(s) > >> (here type_tags) and then recurse with a less-qualified type. > >> > >>> > >>> I think you want the tags to work like qualifiers, but I'm not sure > >>> how this works > >>> with your patch. Consider > >>> > >>> struct S { int i ; }; > >>> > >>> struct S __attribute__((btf_type_tag("foo"))) a; > >>> const struct S b; > >>> struct S c; > >>> const struct S __attribute__((btf_type_tag("foo"))) d; > >>> > >>> how does the DWARF for 'a' look like? In particular the iffy thing is > >>> dwarf_qual_info and the way we check we already have a qualified > >>> type. > >> > >> This is iffy for me as well. > >> > >> You are right that basically the desire is to have these tags work like > >> qualifiers. But of course, attributes are not qualifiers... > > > > Note TYPE_ADDR_SPACE has the very same issue (and bugs). There is > > currently no nice way to stuff in extra "dwarf" qualifiers that are > > not qualifiers > > on tree (or the other way around, like we have for TYPE_ADDR_SPACE). > > Oh, interesting. I'm not that familiar with TYPE_ADDR_SPACE. > Will look into it. > > > > >> Currently, the DWARF for 'a' above does not have any annotation DIE at all. > >> Because the attribute is ignored during parsing (also for 'd'): > >> > >> warning: ignoring attributes applied to 'struct S' after definition > >> > >> (This happens also if we try to "hide" the struct behind a typedef). > >> > >> I am undecided so far whether this is really what we want long term. > >> Probably not? For now it seems this is not a concern for kernel users; > >> what they are really concerned about is being able to tag pointer types. > >> But I do think these type tags are useful beyond the kernel needs, so I'd > >> like to figure out a better answer long term. > >> > >> I have experimented some with the attribute parsing and creating a variant > >> type of struct S for the attributed version, but not to a working impl. > >> It's not totally clear to me whether that is something that would be "OK" > >> to do or would cause breakage elsewhere. > >> > >>> > >>> How do you represent having both the type S and the attributed type S? > >>> And how do you go between having a const qualified variant of both? > >>> > >> > >> We would need to have two DIEs for the 'struct S'; one with AT_annotation > >> and one without, as we do for base types like int. Obviously this is not > >> good if S has many members and/or there are many uses of S with different > >> tags. > >> > >> As for const (or otherwise qualified) versions, it is relatively > >> straightforward: the DW_TAG_const refers via AT_type either to the > >> type die for S with AT_annotation or to the one without AT_annotation, > >> as appropriate. > > > > I think what would be good to have is a set of testcases with those > > corner cases and the expectation what happens. > > Yep, makes sense. I will add some tests. > > > > > modified_type_die is unfortunately a twisted maze and I hate making > > it even more mazier. But I honestly have no energy to try refactoring > > it into something more maintainable... > > I can take a stab at it. I know it will not be easy, but maybe we can > get some step in the right direction at least. If it's ok with you, I'll > work on the refactor as a separate/followup patch(es) to this series. > > The thing I will need to study more is C++ and other languages' > interactions with modified_type_die and dwarf2out more generally. > Some changes I tried for this patch and a while back when originally > looking at PR110439 (which I still intend to find a solution for, > maybe as a part of refactoring) seemed to work ok for C but cause > explosions in C++ tests. > > If you have already any ideas or suggestions about the refactor, > I'm all ears :)
No idea as to how to achieve it but the refactoring goal would be to separate "typedef-of-X" and "qualified type" handling from a central entry-point you feed in a tree type to get back a DIE for it (which currently is what modified_type_die is). Richard. > Thanks, > David > > > > > Richard. > > > >> > >> Thanks for all your comments :) > >> David > >> > >>>> + gen_btf_type_tag_dies (type, attr, mod_die, context_die); > >>>> + return mod_die; > >>>> + } > >>>> else if (code == POINTER_TYPE || code == REFERENCE_TYPE) > >>>> { > >>>> dwarf_tag tag = DW_TAG_pointer_type; > >>>> @@ -22729,6 +22975,7 @@ gen_array_type_die (tree type, dw_die_ref > >>>> context_die) > >>>> add_pubtype (type, array_die); > >>>> > >>>> add_alignment_attribute (array_die, type); > >>>> + maybe_gen_btf_type_tag_dies (type, array_die, context_die); > >>>> } > >>>> > >>>> /* This routine generates DIE for array with hidden descriptor, details > >>>> @@ -23099,6 +23346,8 @@ gen_formal_parameter_die (tree node, tree > >>>> origin, bool emit_name_p, > >>>> else > >>>> { > >>>> add_child_die (context_die, parm_die); > >>>> + > >>>> + gen_btf_decl_tag_dies (node_or_origin, parm_die, > >>>> context_die); > >>>> return parm_die; > >>>> } > >>>> } > >>>> @@ -23167,6 +23416,8 @@ gen_formal_parameter_die (tree node, tree > >>>> origin, bool emit_name_p, > >>>> gcc_unreachable (); > >>>> } > >>>> > >>>> + gen_btf_decl_tag_dies (node_or_origin, parm_die, context_die); > >>>> + > >>>> return parm_die; > >>>> } > >>>> > >>>> @@ -26391,6 +26642,10 @@ gen_tagged_type_die (tree type, > >>>> else > >>>> gen_struct_or_union_type_die (type, context_die, usage); > >>>> > >>>> + dw_die_ref die = lookup_type_die (type); > >>>> + if (die) > >>>> + maybe_gen_btf_type_tag_dies (type, die, context_die); > >>>> + > >>>> /* Don't set TREE_ASM_WRITTEN on an incomplete struct; we want to fix > >>>> it up if it is ever completed. gen_*_type_die will set it for us > >>>> when appropriate. */ > >>>> @@ -27401,6 +27656,9 @@ gen_decl_die (tree decl, tree origin, struct > >>>> vlr_context *ctx, > >>>> break; > >>>> } > >>>> > >>>> + gen_btf_decl_tag_dies (decl_or_origin, lookup_decl_die > >>>> (decl_or_origin), > >>>> + context_die); > >>>> + > >>>> return NULL; > >>>> } > >>>> > >>>> @@ -33321,6 +33579,9 @@ dwarf2out_early_finish (const char *filename) > >>>> print_die (comp_unit_die (), dump_file); > >>>> } > >>>> > >>>> + if (btf_tag_htab) > >>>> + btf_tag_htab->empty (); > >>>> + > >>>> /* Generate CTF/BTF debug info. */ > >>>> if ((ctf_debug_info_level > CTFINFO_LEVEL_NONE > >>>> || btf_debuginfo_p ()) && lang_GNU_C ()) > >>>> @@ -33516,6 +33777,7 @@ dwarf2out_cc_finalize (void) > >>>> switch_text_ranges = NULL; > >>>> switch_cold_ranges = NULL; > >>>> current_unit_personality = NULL; > >>>> + btf_tag_htab = NULL; > >>>> > >>>> early_dwarf = false; > >>>> early_dwarf_finished = false; > >>>> diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-1.c > >>>> b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-1.c > >>>> new file mode 100644 > >>>> index 00000000000..a1c1676a7ba > >>>> --- /dev/null > >>>> +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-1.c > >>>> @@ -0,0 +1,11 @@ > >>>> +/* Test simple generation of DW_TAG_GNU_annotation DIE for > >>>> + btf_decl_tag attribute. */ > >>>> +/* { dg-do compile } */ > >>>> +/* { dg-options "-gdwarf -dA" } */ > >>>> + > >>>> +int *foo __attribute__((btf_decl_tag ("my_foo"))); > >>>> + > >>>> +/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) > >>>> DW_TAG_GNU_annotation" 1 } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_name: \"btf_decl_tag\"" 1 > >>>> } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_const_value: \"my_foo\"" 1 > >>>> } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_GNU_annotation" 1 } } */ > >>>> diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-2.c > >>>> b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-2.c > >>>> new file mode 100644 > >>>> index 00000000000..76583840439 > >>>> --- /dev/null > >>>> +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-2.c > >>>> @@ -0,0 +1,25 @@ > >>>> +/* Test dwarf generation for btf_decl_tag on struct and union members. > >>>> */ > >>>> +/* { dg-do compile } */ > >>>> +/* { dg-options "-gdwarf -dA" } */ > >>>> + > >>>> +#define __tag1 __attribute__((btf_decl_tag ("decl1"))) > >>>> +#define __tag2 __attribute__((btf_decl_tag ("decl2"))) > >>>> + > >>>> +union U { > >>>> + int i __tag1; > >>>> + unsigned char ub[4]; > >>>> +}; > >>>> + > >>>> +struct S { > >>>> + union U u; > >>>> + int b __tag2; > >>>> + char *z __tag1; > >>>> +}; > >>>> + > >>>> +struct S my_s __tag1 __tag2; > >>>> + > >>>> +/* We must have two occurrances of one of the two annotation DIEs due to > >>>> + the different attribute sets between declarations above. */ > >>>> +/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) > >>>> DW_TAG_GNU_annotation" 3 } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_name: \"btf_decl_tag\"" 3 > >>>> } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_GNU_annotation" 5 } } */ > >>>> diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-3.c > >>>> b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-3.c > >>>> new file mode 100644 > >>>> index 00000000000..f3fad8fe3d2 > >>>> --- /dev/null > >>>> +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-3.c > >>>> @@ -0,0 +1,21 @@ > >>>> +/* Test dwarf generation for btf_decl_tag on functions and function > >>>> args. */ > >>>> +/* { dg-do compile } */ > >>>> +/* { dg-options "-gdwarf -dA" } */ > >>>> + > >>>> +#define __tag1 __attribute__((btf_decl_tag ("decl1"))) > >>>> +#define __tag2 __attribute__((btf_decl_tag ("decl2"))) > >>>> + > >>>> +int __tag1 __tag2 func (int arg_a __tag1, int arg_b __tag2) > >>>> +{ > >>>> + return arg_a * arg_b; > >>>> +} > >>>> + > >>>> +int foo (int x) { > >>>> + return func (x, x + 1); > >>>> +} > >>>> + > >>>> +/* In this case one of the decl tag DIEs must be duplicated due to > >>>> differing > >>>> + DW_AT_GNU_annotation chain between the three uses. */ > >>>> +/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) > >>>> DW_TAG_GNU_annotation" 3 } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_name: \"btf_decl_tag\"" 3 > >>>> } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_GNU_annotation" 4 } } */ > >>>> diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-1.c > >>>> b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-1.c > >>>> new file mode 100644 > >>>> index 00000000000..772aab09cfb > >>>> --- /dev/null > >>>> +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-1.c > >>>> @@ -0,0 +1,10 @@ > >>>> +/* Test simple generation for btf_type_tag attribute. */ > >>>> +/* { dg-do compile } */ > >>>> +/* { dg-options "-gdwarf -dA" } */ > >>>> + > >>>> +int * __attribute__((btf_type_tag("__user"))) ptr; > >>>> + > >>>> +/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) > >>>> DW_TAG_GNU_annotation" 1 } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_name: \"btf_type_tag\"" 1 > >>>> } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_const_value: \"__user\"" 1 > >>>> } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_GNU_annotation" 1 } } */ > >>>> diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-2.c > >>>> b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-2.c > >>>> new file mode 100644 > >>>> index 00000000000..9c44e0ee0b9 > >>>> --- /dev/null > >>>> +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-2.c > >>>> @@ -0,0 +1,31 @@ > >>>> +/* Test that DW_TAG_GNU_annotation DIEs for attribute btf_type_tag are > >>>> shared > >>>> + where possible. */ > >>>> +/* { dg-do compile } */ > >>>> +/* { dg-options "-gdwarf -dA" } */ > >>>> + > >>>> +#define __tag1 __attribute__((btf_type_tag("tag1"))) > >>>> +#define __tag2 __attribute__((btf_type_tag("tag2"))) > >>>> + > >>>> +int __tag1 foo; > >>>> +char * __tag1 __tag2 bar; > >>>> + > >>>> +struct S > >>>> +{ > >>>> + unsigned char bytes[8]; > >>>> + unsigned long __tag1 t; > >>>> + void *ptr; > >>>> +}; > >>>> + > >>>> +struct S * __tag1 __tag2 my_S; > >>>> + > >>>> +/* Only 2 DW_TAG_GNU_annotation DIEs should be generated, one each for > >>>> "tag1" > >>>> + and "tag2", and they should be reused. */ > >>>> +/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) > >>>> DW_TAG_GNU_annotation" 2 } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_name: \"btf_type_tag\"" 2 > >>>> } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_const_value: \"tag1\"" 1 } > >>>> } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_const_value: \"tag2\"" 1 } > >>>> } */ > >>>> + > >>>> +/* Each attribute-ed type shall refer via DW_AT_GNU_annotation to the > >>>> + appropriate annotation DIE, including the annotation DIE for "tag2" > >>>> which > >>>> + is always chained to the DIE for "tag1" in this construction. */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_GNU_annotation" 5 } } */ > >>>> diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-3.c > >>>> b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-3.c > >>>> new file mode 100644 > >>>> index 00000000000..d02144c8004 > >>>> --- /dev/null > >>>> +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-3.c > >>>> @@ -0,0 +1,15 @@ > >>>> +/* Test dwarf generation for btf_type_tag with cv-quals and typedefs. > >>>> */ > >>>> +/* { dg-do compile } */ > >>>> +/* { dg-options "-gdwarf -dA" } */ > >>>> + > >>>> +#define __tag1 __attribute__((btf_type_tag ("tag1"))) > >>>> +#define __tag2 __attribute__((btf_type_tag ("tag2"))) > >>>> + > >>>> +typedef const int foo; > >>>> +typedef int __tag1 bar; > >>>> + > >>>> +foo __tag2 x; > >>>> +const bar y; > >>>> + > >>>> +/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) > >>>> DW_TAG_GNU_annotation" 2 } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_name: \"btf_type_tag\"" 2 > >>>> } } */ > >>>> diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-4.c > >>>> b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-4.c > >>>> new file mode 100644 > >>>> index 00000000000..2e916e8b443 > >>>> --- /dev/null > >>>> +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-4.c > >>>> @@ -0,0 +1,33 @@ > >>>> +/* Test generating annotation DIEs for struct/union/enum types. */ > >>>> +/* { dg-do compile } */ > >>>> +/* { dg-options "-gdwarf -dA" } */ > >>>> + > >>>> +enum E > >>>> +{ > >>>> + ONE, > >>>> + TWO > >>>> +} __attribute__((btf_type_tag("foo"))); > >>>> + > >>>> +enum E some_e; > >>>> + > >>>> +struct S > >>>> +{ > >>>> + int i; > >>>> + char c; > >>>> +} __attribute__((btf_type_tag("foo"))); > >>>> + > >>>> +typedef struct S S1; > >>>> +const S1 some_s1; > >>>> + > >>>> +union U > >>>> +{ > >>>> + int x; > >>>> + char y; > >>>> +} __attribute__((btf_type_tag("foo"))); > >>>> + > >>>> +volatile union U volatile_u; > >>>> + > >>>> +/* One annotation DIE may be shared by all three annotated types. */ > >>>> +/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) > >>>> DW_TAG_GNU_annotation" 1 } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_name: \"btf_type_tag\"" 1 > >>>> } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_GNU_annotation" 3 } } */ > >>>> diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-5.c > >>>> b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-5.c > >>>> new file mode 100644 > >>>> index 00000000000..1a6b29f99a1 > >>>> --- /dev/null > >>>> +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-5.c > >>>> @@ -0,0 +1,10 @@ > >>>> +/* Test generation for btf_type_tag attribute on array type. */ > >>>> +/* { dg-do compile } */ > >>>> +/* { dg-options "-gdwarf -dA" } */ > >>>> + > >>>> +int arr[8] __attribute__((btf_type_tag("tagged_arr"))); > >>>> + > >>>> +/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) > >>>> DW_TAG_GNU_annotation" 1 } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_name: \"btf_type_tag\"" 1 > >>>> } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_const_value: > >>>> \"tagged_arr\"" 1 } } */ > >>>> +/* { dg-final { scan-assembler-times " DW_AT_GNU_annotation" 1 } } */ > >>>> diff --git a/include/dwarf2.def b/include/dwarf2.def > >>>> index 989f078041d..37b8d6b99d0 100644 > >>>> --- a/include/dwarf2.def > >>>> +++ b/include/dwarf2.def > >>>> @@ -174,6 +174,9 @@ DW_TAG (DW_TAG_GNU_formal_parameter_pack, 0x4108) > >>>> are properly part of DWARF 5. */ > >>>> DW_TAG (DW_TAG_GNU_call_site, 0x4109) > >>>> DW_TAG (DW_TAG_GNU_call_site_parameter, 0x410a) > >>>> + > >>>> +DW_TAG (DW_TAG_GNU_annotation, 0x6001) > >>>> + > >>>> /* Extensions for UPC. See: http://dwarfstd.org/doc/DWARF4.pdf. */ > >>>> DW_TAG (DW_TAG_upc_shared_type, 0x8765) > >>>> DW_TAG (DW_TAG_upc_strict_type, 0x8766) > >>>> @@ -456,6 +459,7 @@ DW_AT (DW_AT_GNU_pubtypes, 0x2135) > >>>> DW_AT (DW_AT_GNU_discriminator, 0x2136) > >>>> DW_AT (DW_AT_GNU_locviews, 0x2137) > >>>> DW_AT (DW_AT_GNU_entry_view, 0x2138) > >>>> +DW_AT (DW_AT_GNU_annotation, 0x2139) > >>>> /* VMS extensions. */ > >>>> DW_AT (DW_AT_VMS_rtnbeg_pd_address, 0x2201) > >>>> /* GNAT extensions. */ > >>>> -- > >>>> 2.47.2 > >>>> > >> >