On Tue, Jun 10, 2025 at 11:41 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.
Mostly looks good to me, I'd like to see a 2nd ack on the design. Also see below for an issue I still have with this. > 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.dg/debug/dwarf2/dwarf-btf-type-tag-6.c: New test. > * gcc.dg/debug/dwarf2/dwarf-btf-type-tag-7.c: New test. > --- > gcc/dwarf2out.cc | 308 +++++++++++++++++- > .../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 | 34 ++ > .../debug/dwarf2/dwarf-btf-type-tag-5.c | 10 + > .../debug/dwarf2/dwarf-btf-type-tag-6.c | 27 ++ > .../debug/dwarf2/dwarf-btf-type-tag-7.c | 19 ++ > include/dwarf2.def | 4 + > 12 files changed, 511 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 > create mode 100644 gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-6.c > create mode 100644 gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-7.c > > diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc > index d1a55dbcbcb..5b8ed47044f 100644 > --- a/gcc/dwarf2out.cc > +++ b/gcc/dwarf2out.cc > @@ -3696,6 +3696,33 @@ 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; > +}; > + > +/* Hasher for btf_type_tag and btf_decl_tag annotation nodes. */ > +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 +13686,199 @@ long_double_as_float128 (tree type) > return NULL_TREE; > } > > +/* Hash function for struct annotation_node. The hash value is computed when > + the annotation node is created based on the name, value and chain of any > + further annotations on the same entity. */ > + > +hashval_t > +annotation_node_hasher::hash (struct annotation_node *node) > +{ > + return node->hash; > +} > + > +/* Return whether two annotation nodes represent the same annotation and > + can therefore share a DIE. Beware of hash value collisions. */ > + > +bool > +annotation_node_hasher::equal (const struct annotation_node *node1, > + const struct annotation_node *node2) > +{ > + return (node1->hash == node2->hash > + && (node1->name == node2->name > + || !strcmp (node1->name, node2->name)) > + && (node1->value == node2->value > + || !strcmp (node1->value, node2->value)) > + && node1->next == node2->next); > +} > + > +/* 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_GNU_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_GNU_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. > + A single chain of annotation DIEs can be shared among all occurrences of > + equivalent sets of attributes appearing on different types or > declarations. > + 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); > + > + entry->die = tag_die; > + } > + > + if (die) > + add_AT_die_ref (die, DW_AT_GNU_annotation, tag_die); > + > + return tag_die; > +} > + > +/* Generate (or reuse) annotation DIEs representing any type_tags in ATTR, > and > + update TARGET to refer to them via DW_AT_GNU_annotation. */ > + > +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); > +} > + > +/* Generate (or reuse) annotation DIEs representing the type_tags on T, if > + any, and update DIE to refer to them as appropriate. */ > + > +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) > + gen_btf_type_tag_dies (t, tags, die, context_die); > +} > + > +/* Generate (or reuse) annotation DIEs representing any decl_tags in ATTR > that > + apply to TARGET. */ > + > +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. Global variable > + declarations may reach this function more than once. */ > + DECL_ATTRIBUTES (t) > + = remove_attribute ("btf_decl_tag", DECL_ATTRIBUTES (t)); I do not like modifying trees as part of dwarf2out. You should be able to see whether a DIE already has the respective attribute applied? > +} > + > /* 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 > @@ -13674,6 +13894,7 @@ modified_type_die (tree type, int cv_quals, bool > reverse, > tree item_type = NULL; > tree qualified_type; > tree name, low, high; > + tree tags; > dw_die_ref mod_scope; > struct array_descr_info info; > /* Only these cv-qualifiers are currently handled. */ > @@ -13783,10 +14004,62 @@ 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); > + { > + tree dtags = lookup_attribute ("btf_type_tag", > + TYPE_ATTRIBUTES (dtype)); > + if ((tags = lookup_attribute ("btf_type_tag", > + TYPE_ATTRIBUTES (type))) > + && !attribute_list_equal (tags, dtags)) > + { > + /* Use of a typedef with additional btf_type_tags. > + Create a new typedef DIE to which we can attach the > + additional type_tag DIEs without disturbing other users > of > + the underlying typedef. */ > + dw_die_ref mod_die = modified_type_die (dtype, cv_quals, > + reverse, > context_die); > + 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); > + > + /* 'tags' is an accumulated list of type_tag attributes > + for the typedef'd type on both sides of the typedef. > + 'dtags' is the set of type_tag attributes only appearing > + in the typedef itself. > + Find the set of type_tags only on the _use_ of the > + typedef, i.e. (tags - dtags). By construction these > + additional type_tags have been chained onto the head of > + the attribute list of the original typedef. */ > + tree t = tags; > + bool altered_chain = false; > + while (t) > + { > + if (TREE_CHAIN (t) == dtags) > + { > + TREE_CHAIN (t) = NULL_TREE; > + altered_chain = true; > + break; > + } > + t = TREE_CHAIN (t); > + } And this might be part of that and should thus split out, or be part of gen_btf_type_tag_dies itself? I expected the hashtable to cover the DIEs we have for existing chains of tags? > + > + /* Now generate the type_tag DIEs only for the new > + type_tags appearing in the use of the typedef, and > + attach them to the cloned typedef DIE. */ > + gen_btf_type_tag_dies (type, tags, mod_die, context_die); > + > + /* Restore the list of type_tags. In principle, the same > + attribute list could be shared by unrelated types. */ > + if (altered_chain) > + TREE_CHAIN (t) = dtags; > + > + 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 +14160,17 @@ modified_type_die (tree type, int cv_quals, bool > reverse, > first_quals |= dwarf_qual_info[i].q; > } > } > + else if ((tags = lookup_attribute ("btf_type_tag", TYPE_ATTRIBUTES > (type)))) > + { > + /* 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)); See above. > + dw_die_ref mod_die = modified_type_die (type, cv_quals, reverse, > + context_die); > + gen_btf_type_tag_dies (type, tags, mod_die, context_die); > + return mod_die; > + } > else if (code == POINTER_TYPE || code == REFERENCE_TYPE) > { > dwarf_tag tag = DW_TAG_pointer_type; > @@ -22770,6 +23054,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 > @@ -23140,6 +23425,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; > } > } > @@ -23208,6 +23495,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; > } > > @@ -26432,6 +26721,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. */ > @@ -27442,6 +27735,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; > } > > @@ -33363,6 +33659,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 ()) > @@ -33558,6 +33857,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..00485c000b5 > --- /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 occurrences 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..7205ef2c9a3 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-4.c > @@ -0,0 +1,34 @@ > +/* 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; > +S1 plain_s1; > +const S1 const_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/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-6.c > b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-6.c > new file mode 100644 > index 00000000000..11fc9036b8a > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-6.c > @@ -0,0 +1,27 @@ > +/* Test generation for btf_type_tag attribute when applied to struct/union > + types after definition. Attributes applied after definition will be > + ignored, so DW_TAG_GNU_annotations shall be generated. */ > +/* { dg-do compile } */ > +/* { dg-options "-gdwarf -dA" } */ > + > +struct foo > +{ > + int a; > + char c; > +}; > + > +struct foo __attribute__((btf_type_tag ("tag1"))) x; /* { dg-warning > "ignoring attribute" } */ > +typedef const struct foo c_foo; > +c_foo __attribute__((btf_type_tag ("tag2"))) y; /* { dg-warning "ignoring > attribute" } */ > + > +union bar > +{ > + int s; > + unsigned int u; > +}; > + > +typedef union bar __attribute__((btf_type_tag("tag3"))) tag_bar; /* { > dg-warning "ignoring attribute" } */ > +const tag_bar z; > + > +/* { dg-final { scan-assembler-not "DIE \\(\[^\n\]*\\) > DW_TAG_GNU_annotation" } } */ > +/* { dg-final { scan-assembler-not " DW_AT_GNU_annotation" } } */ > diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-7.c > b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-7.c > new file mode 100644 > index 00000000000..9b2926bef56 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-7.c > @@ -0,0 +1,19 @@ > +/* Test generation for btf_type_tag attribute for pointer typedef with > + tags appearing on both the typedef and the usage of the tyepdef. */ > +/* { dg-do compile } */ > +/* { dg-options "-gdwarf -dA" } */ > + > +#define __tag1 __attribute__((btf_type_tag ("tag1"))) > +#define __tag2 __attribute__((btf_type_tag ("tag2"))) > + > +typedef int __tag1 foo; > + > +foo a; > +foo __tag2 b; > + > +/* { 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_GNU_annotation" 2 } } */ > + > +/* A duplicate typedef die must be created for the tagged use in 'b'. */ > +/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_typedef" 2 } > } */ > 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 >