On 7/17/25 05:16, Richard Biener wrote:
> On Tue, Jul 15, 2025 at 12:03 AM 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.
>
> Thanks - this looks a lot better, but see below.
I am also happier with it thanks to your help :)
>
>> 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, maybe_gen_btf_decl_tag_dies): New
>> functions.
>> (modified_type_die): Add new argument to pass btf_type_tag attribute.
>> Handle btf_type_tag, and update recursive calls.
>> (base_type_for_mode): Add new arg for modified_type_die call.
>> (add_type_attribute): Likewise.
>> (gen_array_type_die): Call maybe_gen_btf_type_tag_dies for the type.
>> (gen_formal_parameter_die): Call maybe_gen_btf_decl_tag_dies for the
>> parameter.
>> (override_type_for_decl_p): Add new arg for modified_type_die call.
>> (force_type_die): Likewise.
>> (gen_tagged_type_die): Call maybe_gen_btf_type_tag_dies for the type.
>> (gen_decl_die): Call maybe_gen_btf_decl_tag_dies for the decl.
>> (dwarf2out_finish): Empty btf_tag_htab.
>> (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 | 337 ++++++++++++++++--
>> .../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 | 25 ++
>> include/dwarf2.def | 4 +
>> 12 files changed, 527 insertions(+), 23 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..20007f439a2 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
>> @@ -3840,7 +3867,7 @@ static void output_file_names (void);
>> static bool is_base_type (tree);
>> static dw_die_ref subrange_type_die (tree, tree, tree, tree, dw_die_ref);
>> static int decl_quals (const_tree);
>> -static dw_die_ref modified_type_die (tree, int, bool, dw_die_ref);
>> +static dw_die_ref modified_type_die (tree, int, bool, dw_die_ref, tree);
>> static dw_die_ref generic_parameter_die (tree, tree, bool, dw_die_ref);
>> static dw_die_ref template_parameter_pack_die (tree, tree, dw_die_ref);
>> static unsigned int debugger_reg_number (const_rtx);
>> @@ -13659,6 +13686,206 @@ 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;
>
> This should be always comp_unit_die (). But, seeing this I wonder
> how this interacts with -fdebug-types aka type units? I would expect
> us to duplicate the btf-tag DIEs into the type units, so it should work.
> But can you double-check?
Thanks, to be honest I did not consider -fdebug-types-section.
Fortunately, looks like it does indeed "just work" and the btf-tag DIEs
are duplicated into the type units as necessary.
Regarding "should be always comp_unit_die ()": I thought so too, but I
wasn't sure whether I forgot something (like -fdebug-types...)
In that case I wonder whether we need to pass context_die at all here.
>From a quick check, just using comp_unit_die () gives the same output,
also with -fdebug-types, and the intended design is that the tag dies
are added as children of the top-level CU (or type unit) die.
>
>> +
>> + 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_GNU_annotation referring to the annotation DIE.
>> + It may have already been added, some global declarations are
>> processed
>> + twice, but if so it must be the same or we have a bug. */
>> + dw_die_ref existing = get_AT_ref (die, DW_AT_GNU_annotation);
>> + if (existing)
>> + gcc_checking_assert (existing == tag_die);
>> + else
>> + 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 tags, dw_die_ref target, dw_die_ref context_die)
>> +{
>> + if (!target)
>> + return;
>> +
>> + gen_btf_tag_dies (tags, 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 target, dw_die_ref
>> context_die)
>> +{
>> + if (t == NULL_TREE || !TYPE_P (t) || !target)
>> + return;
>> +
>> + tree attr = lookup_attribute ("btf_type_tag", TYPE_ATTRIBUTES (t));
>> + if (attr == NULL_TREE)
>> + return;
>> +
>> + gen_btf_type_tag_dies (attr, target, context_die);
>> +}
>> +
>> +/* Generate (or reuse) annotation DIEs representing any decl_tags in ATTR
>> that
>> + apply to TARGET. */
>> +
>> +static void
>> +maybe_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);
>> +}
>> +
>> /* 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
>> @@ -13666,7 +13893,7 @@ long_double_as_float128 (tree type)
>>
>> static dw_die_ref
>> modified_type_die (tree type, int cv_quals, bool reverse,
>> - dw_die_ref context_die)
>> + dw_die_ref context_die, tree tag_attrs)
>
> The new param lacks documentation in the function-level comment. I
> see below that
> you only ever pass "btf-tag" attributes here but I hoped this could be
> the vehicle
> to handle more attributes in the future. So I wonder if you can instead make
> the argument carry the full attribute list from TYPE_ATTRIBUTES?
Thanks, will add the doc commment.
Yes, passing the full TYPE_ATTRIBUTES makes sense and is straightforward
from initial testing. It gives a nice parallel to cv-quals with many callers
passing TYPE_QUALS and now TYPE_ATTRIBUTES. Nice :)
In that case WDYT about inserting the new arg as type_attrs like so:
modified_type_die (tree type, int cv_quals, tree type_attrs,
bool reverse, dw_die_ref context_die)
> I'm also not
> sure whether your implementation handles the case of two attributes on the
> same type (it might be your updated attribute handler forces multiple
> type variants
> then?).
Yes, it still works with the change in the attribute handler.
>
> From a quick look there's no test coverage for
>
> typedef int myint __attribute__((btf_tag("A"),btf_tag("B")));
>
> and what's expected from that.
Test dwarf2/btf-type-tag-2.c has a couple of multi-tag types, but not in
the same __attribute__ (()). I will add another test for this specifically,
and also to stress more cases of having several tags on the same type.
Thank you,
David
>
> Thanks,
> Richard.
>
>> {
>> enum tree_code code = TREE_CODE (type);
>> dw_die_ref mod_type_die;
>> @@ -13693,7 +13920,8 @@ modified_type_die (tree type, int cv_quals, bool
>> reverse,
>> tree debug_type = lang_hooks.types.get_debug_type (type);
>>
>> if (debug_type != NULL_TREE && debug_type != type)
>> - return modified_type_die (debug_type, cv_quals, reverse,
>> context_die);
>> + return modified_type_die (debug_type, cv_quals, reverse, context_die,
>> + tag_attrs);
>> }
>>
>> cv_quals &= cv_qual_mask;
>> @@ -13771,7 +13999,7 @@ modified_type_die (tree type, int cv_quals, bool
>> reverse,
>> abstract origin instead. */
>> if (origin != NULL && origin != name)
>> return modified_type_die (TREE_TYPE (origin), cv_quals, reverse,
>> - context_die);
>> + context_die, tag_attrs);
>>
>> /* For a named type, use the typedef. */
>> gen_type_die (qualified_type, context_die);
>> @@ -13783,10 +14011,35 @@ 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 (tag_attrs && !attribute_list_equal (tag_attrs, 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,
>> + NULL_TREE);
>> +
>> + 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);
>> +
>> + /* 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 (tag_attrs, 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, tag_attrs);
>> + }
>> /* Else cv-qualified version of named type; fall through. */
>> }
>> }
>> @@ -13820,7 +14073,8 @@ modified_type_die (tree type, int cv_quals, bool
>> reverse,
>> break;
>> }
>> }
>> - mod_type_die = modified_type_die (type, sub_quals, reverse,
>> context_die);
>> + mod_type_die = modified_type_die (type, sub_quals, reverse,
>> + context_die, tag_attrs);
>> if (mod_scope && mod_type_die && mod_type_die->die_parent ==
>> mod_scope)
>> {
>> /* As not all intermediate qualified DIEs have corresponding
>> @@ -13887,6 +14141,15 @@ modified_type_die (tree type, int cv_quals, bool
>> reverse,
>> first_quals |= dwarf_qual_info[i].q;
>> }
>> }
>> + else if (tag_attrs)
>> + {
>> + /* First create a DIE for the type without any type_tag attribute.
>> + Then generate TAG_GNU_annotation DIEs for the type_tags. */
>> + dw_die_ref mod_die = modified_type_die (type, cv_quals, reverse,
>> + context_die, NULL_TREE);
>> + gen_btf_type_tag_dies (tag_attrs, mod_die, context_die);
>> + return mod_die;
>> + }
>> else if (code == POINTER_TYPE || code == REFERENCE_TYPE)
>> {
>> dwarf_tag tag = DW_TAG_pointer_type;
>> @@ -13951,9 +14214,13 @@ modified_type_die (tree type, int cv_quals, bool
>> reverse,
>> {
>> dw_die_ref other_die;
>> if (TYPE_NAME (other_type))
>> - other_die
>> - = modified_type_die (other_type, TYPE_UNQUALIFIED, reverse,
>> - context_die);
>> + {
>> + tree other_attr = lookup_attribute ("btf_type_tag",
>> + TYPE_ATTRIBUTES
>> (other_type));
>> + other_die
>> + = modified_type_die (other_type, TYPE_UNQUALIFIED, reverse,
>> + context_die, other_attr);
>> + }
>> else
>> {
>> other_die = base_type_die (type, reverse);
>> @@ -13972,7 +14239,7 @@ modified_type_die (tree type, int cv_quals, bool
>> reverse,
>> if (reverse_type)
>> {
>> dw_die_ref after_die
>> - = modified_type_die (type, cv_quals, false, context_die);
>> + = modified_type_die (type, cv_quals, false, context_die,
>> tag_attrs);
>> add_child_die_after (mod_scope, mod_type_die, after_die);
>> }
>> else
>> @@ -13986,7 +14253,7 @@ modified_type_die (tree type, int cv_quals, bool
>> reverse,
>> if (reverse_type)
>> {
>> dw_die_ref after_die
>> - = modified_type_die (type, cv_quals, false, context_die);
>> + = modified_type_die (type, cv_quals, false, context_die,
>> tag_attrs);
>> gen_type_die (type, context_die, true);
>> gcc_assert (after_die->die_sib
>> && get_AT_unsigned (after_die->die_sib,
>> DW_AT_endianity));
>> @@ -14076,8 +14343,9 @@ modified_type_die (tree type, int cv_quals, bool
>> reverse,
>> types are possible in Ada. */
>> sub_die = modified_type_die (item_type,
>> TYPE_QUALS_NO_ADDR_SPACE (item_type),
>> - reverse,
>> - context_die);
>> + reverse, context_die,
>> + lookup_attribute ("btf_type_tag",
>> + TYPE_ATTRIBUTES
>> (item_type)));
>>
>> if (sub_die != NULL)
>> add_AT_die_ref (mod_type_die, DW_AT_type, sub_die);
>> @@ -15222,7 +15490,7 @@ base_type_for_mode (machine_mode mode, bool
>> unsignedp)
>> type_die = lookup_type_die (type);
>> if (!type_die)
>> type_die = modified_type_die (type, TYPE_UNQUALIFIED, false,
>> - comp_unit_die ());
>> + comp_unit_die (), NULL_TREE);
>> if (type_die == NULL || type_die->die_tag != DW_TAG_base_type)
>> return NULL;
>> return type_die;
>> @@ -22493,7 +22761,9 @@ add_type_attribute (dw_die_ref object_die, tree
>> type, int cv_quals,
>> type_die = modified_type_die (type,
>> cv_quals | TYPE_QUALS (type),
>> reverse,
>> - context_die);
>> + context_die,
>> + lookup_attribute ("btf_type_tag",
>> + TYPE_ATTRIBUTES (type)));
>>
>> if (type_die != NULL)
>> add_AT_die_ref (object_die, DW_AT_type, type_die);
>> @@ -22770,6 +23040,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 +23411,8 @@ gen_formal_parameter_die (tree node, tree origin,
>> bool emit_name_p,
>> else
>> {
>> add_child_die (context_die, parm_die);
>> + maybe_gen_btf_decl_tag_dies (node_or_origin, parm_die,
>> + context_die);
>> return parm_die;
>> }
>> }
>> @@ -23208,6 +23481,8 @@ gen_formal_parameter_die (tree node, tree origin,
>> bool emit_name_p,
>> gcc_unreachable ();
>> }
>>
>> + maybe_gen_btf_decl_tag_dies (node_or_origin, parm_die, context_die);
>> +
>> return parm_die;
>> }
>>
>> @@ -24561,10 +24836,13 @@ override_type_for_decl_p (tree decl, dw_die_ref
>> old_die,
>> else
>> cv_quals = decl_quals (decl);
>>
>> - dw_die_ref type_die = modified_type_die (type,
>> - cv_quals | TYPE_QUALS (type),
>> - false,
>> - context_die);
>> + dw_die_ref type_die
>> + = modified_type_die (type,
>> + cv_quals | TYPE_QUALS (type),
>> + false,
>> + context_die,
>> + lookup_attribute ("btf_type_tag",
>> + TYPE_ATTRIBUTES (type)));
>>
>> dw_die_ref old_type_die = get_AT_ref (old_die, DW_AT_type);
>>
>> @@ -26432,6 +26710,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. */
>> @@ -27065,7 +27347,9 @@ force_type_die (tree type)
>> dw_die_ref context_die = get_context_die (TYPE_CONTEXT (type));
>>
>> type_die = modified_type_die (type, TYPE_QUALS_NO_ADDR_SPACE (type),
>> - false, context_die);
>> + false, context_die,
>> + lookup_attribute ("btf_type_tag",
>> + TYPE_ATTRIBUTES
>> (type)));
>> gcc_assert (type_die);
>> }
>> return type_die;
>> @@ -27442,6 +27726,9 @@ gen_decl_die (tree decl, tree origin, struct
>> vlr_context *ctx,
>> break;
>> }
>>
>> + maybe_gen_btf_decl_tag_dies (decl_or_origin, lookup_decl_die
>> (decl_or_origin),
>> + context_die);
>> +
>> return NULL;
>> }
>>
>> @@ -32488,6 +32775,9 @@ dwarf2out_finish (const char *filename)
>> /* Flush out any latecomers to the limbo party. */
>> flush_limbo_die_list ();
>>
>> + if (btf_tag_htab)
>> + btf_tag_htab->empty ();
>> +
>> if (inline_entry_data_table)
>> gcc_assert (inline_entry_data_table->is_empty ());
>>
>> @@ -33558,6 +33848,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..5b3d45d5e7a
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-7.c
>> @@ -0,0 +1,25 @@
>> +/* 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 } }
>> */
>> +
>> +/* Due to an ambiguity in the tree attribute list, it is not currently
>> possible
>> + to distinguish with certaianty whether "tag1" appears to the left or
>> right
>> + of "foo" in the declaration of "b" above. This means that the DIE for
>> + "tag1" is also referred in the AT_GNU_annotation chain of the duplicate
>> + typedef DIE annotated wtih "tag2", for a total of 3 AT_GNU_annotations.
>> */
>> +/* { dg-final { scan-assembler-times " DW_AT_GNU_annotation" 3 } } */
>> +
>> +/* 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
>>