On Tue, Jul 22, 2025 at 1:00 AM David Faust <[email protected]> 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.
This LGTM now, please leave others time to comment (and sorry for the delay).
The c-family part still needs review from a FE maintainer, I suppose you can
ack the CTF parts within your group.
Richard.
> 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, maybe_gen_btf_type_tag_dies)
> (maybe_gen_btf_decl_tag_dies): New functions.
> (modified_type_die): Add new argument to pass type attributes.
> 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.dg/debug/dwarf2/dwarf-btf-type-tag-8.c: New test.
> * gcc.dg/debug/dwarf2/dwarf-btf-type-tag-9.c: New test.
> * gcc.dg/debug/dwarf2/dwarf-btf-type-tag-10.c: New test.
> ---
> gcc/dwarf2out.cc | 328 ++++++++++++++++--
> .../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-10.c | 17 +
> .../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 ++
> .../debug/dwarf2/dwarf-btf-type-tag-8.c | 23 ++
> .../debug/dwarf2/dwarf-btf-type-tag-9.c | 41 +++
> include/dwarf2.def | 4 +
> 15 files changed, 595 insertions(+), 27 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-10.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
> create mode 100644 gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-8.c
> create mode 100644 gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-9.c
>
> diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
> index d1a55dbcbcb..e6c33e61c75 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, tree, bool, dw_die_ref);
> 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,13 +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)
> +{
> + if (attr == NULL_TREE)
> + return 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);
> +
> + /* 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, comp_unit_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 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)
> +{
> + 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_tag_dies (attr, target);
> +}
> +
> +/* 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)
> +{
> + 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);
> +}
> +
> /* 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
> - reverse storage order wrt the target order. */
> + given type. Also handle any type attributes in TYPE_ATTRS which have
> + a representation in DWARF. REVERSE is true if the type is to be
> interpreted
> + in the reverse storage order wrt the target order. */
>
> static dw_die_ref
> -modified_type_die (tree type, int cv_quals, bool reverse,
> +modified_type_die (tree type, int cv_quals, tree type_attrs, bool reverse,
> dw_die_ref context_die)
> {
> enum tree_code code = TREE_CODE (type);
> @@ -13674,6 +13887,7 @@ modified_type_die (tree type, int cv_quals, bool
> reverse,
> tree item_type = NULL;
> tree qualified_type;
> tree name, low, high;
> + tree btf_tags;
> dw_die_ref mod_scope;
> struct array_descr_info info;
> /* Only these cv-qualifiers are currently handled. */
> @@ -13693,7 +13907,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, type_attrs, reverse,
> + context_die);
> }
>
> cv_quals &= cv_qual_mask;
> @@ -13770,8 +13985,8 @@ modified_type_die (tree type, int cv_quals, bool
> reverse,
> type DIE (see gen_typedef_die), so fall back on the ultimate
> abstract origin instead. */
> if (origin != NULL && origin != name)
> - return modified_type_die (TREE_TYPE (origin), cv_quals, reverse,
> - context_die);
> + return modified_type_die (TREE_TYPE (origin), cv_quals,
> type_attrs,
> + reverse, context_die);
>
> /* For a named type, use the typedef. */
> gen_type_die (qualified_type, context_die);
> @@ -13783,10 +13998,36 @@ 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 tags = lookup_attribute ("btf_type_tag", type_attrs);
> + tree dtags = lookup_attribute ("btf_type_tag",
> + TYPE_ATTRIBUTES (dtype));
> + if (tags && !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, NULL_TREE, 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);
> +
> + /* 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_tag_dies (tags, mod_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,
> + type_attrs, reverse, context_die);
> + }
> /* Else cv-qualified version of named type; fall through. */
> }
> }
> @@ -13820,7 +14061,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, type_attrs,
> + reverse, context_die);
> if (mod_scope && mod_type_die && mod_type_die->die_parent == mod_scope)
> {
> /* As not all intermediate qualified DIEs have corresponding
> @@ -13887,6 +14129,16 @@ modified_type_die (tree type, int cv_quals, bool
> reverse,
> first_quals |= dwarf_qual_info[i].q;
> }
> }
> + else if (type_attrs
> + && (btf_tags = lookup_attribute ("btf_type_tag", type_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, NULL_TREE,
> + reverse, context_die);
> + gen_btf_tag_dies (btf_tags, mod_die);
> + return mod_die;
> + }
> else if (code == POINTER_TYPE || code == REFERENCE_TYPE)
> {
> dwarf_tag tag = DW_TAG_pointer_type;
> @@ -13951,9 +14203,12 @@ 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);
> + {
> + other_die
> + = modified_type_die (other_type, TYPE_UNQUALIFIED,
> + TYPE_ATTRIBUTES (other_type),
> + reverse, context_die);
> + }
> else
> {
> other_die = base_type_die (type, reverse);
> @@ -13971,8 +14226,8 @@ modified_type_die (tree type, int cv_quals, bool
> reverse,
> /* The DIE with DW_AT_endianity is placed right after the naked DIE.
> */
> if (reverse_type)
> {
> - dw_die_ref after_die
> - = modified_type_die (type, cv_quals, false, context_die);
> + dw_die_ref after_die = modified_type_die (type, cv_quals,
> type_attrs,
> + false, context_die);
> add_child_die_after (mod_scope, mod_type_die, after_die);
> }
> else
> @@ -13985,8 +14240,8 @@ modified_type_die (tree type, int cv_quals, bool
> reverse,
> /* The DIE with DW_AT_endianity is placed right after the naked DIE.
> */
> if (reverse_type)
> {
> - dw_die_ref after_die
> - = modified_type_die (type, cv_quals, false, context_die);
> + dw_die_ref after_die = modified_type_die (type, cv_quals,
> type_attrs,
> + false, context_die);
> 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 +14331,8 @@ 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);
> + TYPE_ATTRIBUTES (item_type),
> + reverse, context_die);
>
> if (sub_die != NULL)
> add_AT_die_ref (mod_type_die, DW_AT_type, sub_die);
> @@ -15221,8 +15476,8 @@ 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 ());
> + type_die = modified_type_die (type, TYPE_UNQUALIFIED, NULL_TREE,
> + false, comp_unit_die ());
> if (type_die == NULL || type_die->die_tag != DW_TAG_base_type)
> return NULL;
> return type_die;
> @@ -22492,6 +22747,7 @@ add_type_attribute (dw_die_ref object_die, tree type,
> int cv_quals,
>
> type_die = modified_type_die (type,
> cv_quals | TYPE_QUALS (type),
> + TYPE_ATTRIBUTES (type),
> reverse,
> context_die);
>
> @@ -22770,6 +23026,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);
> }
>
> /* This routine generates DIE for array with hidden descriptor, details
> @@ -23140,6 +23397,7 @@ 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);
> return parm_die;
> }
> }
> @@ -23208,6 +23466,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);
> +
> return parm_die;
> }
>
> @@ -24561,10 +24821,12 @@ 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),
> + TYPE_ATTRIBUTES (type),
> + false,
> + context_die);
>
> dw_die_ref old_type_die = get_AT_ref (old_die, DW_AT_type);
>
> @@ -26432,6 +26694,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);
> +
> /* 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,6 +27331,7 @@ 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),
> + TYPE_ATTRIBUTES (type),
> false, context_die);
> gcc_assert (type_die);
> }
> @@ -27442,6 +27709,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));
> +
> return NULL;
> }
>
> @@ -32488,6 +32758,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 +33831,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-10.c
> b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-10.c
> new file mode 100644
> index 00000000000..28b6b04e795
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-10.c
> @@ -0,0 +1,17 @@
> +/* Test btf_type_tag and btf_decl_tag on a function. In this case
> + btf_type_tag is treated as applying to the return type. */
> +/* { dg-do compile } */
> +/* { dg-options "-gdwarf -dA" } */
> +
> +int __attribute__((btf_type_tag ("A")))
> +__attribute__((btf_decl_tag ("decl")))
> +foo (int x, int y)
> +{
> + return x + y;
> +}
> +
> +/* Ideally, verify that AT_GNU_annotation in the subprogram DIE refers to
> + the decl_tag annotation DIE, and the AT_GNU_annotation in the return
> + int type refers to the type_tag... */
> +/* { dg-final { scan-assembler-times " DW_AT_name: \"btf_type_tag\"" 1 } } */
> +/* { dg-final { scan-assembler-times " DW_AT_name: \"btf_decl_tag\"" 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/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-8.c
> b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-8.c
> new file mode 100644
> index 00000000000..607956adbf6
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-8.c
> @@ -0,0 +1,23 @@
> +/* Test that annotation DIEs are copied as needed to the
> + debug_types section (DW_UT_type for dwarf 5). */
> +/* { dg-do compile } */
> +/* { dg-options "-gdwarf-5 -dA -fdebug-types-section" } */
> +
> +int __attribute__((btf_type_tag ("A"))) foo;
> +
> +struct S
> +{
> + int x;
> + char * __attribute__((btf_type_tag ("B"))) c;
> +} __attribute__((btf_type_tag ("A"))) some_struct;
> +
> +/* The struct type is placed in DW_UT_type, and the types of both members and
> + both tags are copied there too. Since the tags and base types also exist
> in
> + the main compile unit, we have 4 annotation DIEs total. But since they
> + are only referenced by 'foo' and by the struct members, there are only
> + 3 AT_GNU_annotation. The DIE for tag "B" in the main compile unit is not
> + referred to by anything. */
> +
> +/* { dg-final { scan-assembler-times " DW_UT_type" 1 } } */
> +/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\)
> DW_TAG_GNU_annotation" 4 } } */
> +/* { dg-final { scan-assembler-times " DW_AT_GNU_annotation" 3 } } */
> diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-9.c
> b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-9.c
> new file mode 100644
> index 00000000000..14325bc760f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-9.c
> @@ -0,0 +1,41 @@
> +/* Stress test for several btf_type_tag on the same type and specified
> + in various ways. */
> +/* { dg-do compile } */
> +/* { dg-options "-gdwarf -dA" } */
> +
> +typedef int __attribute__((btf_type_tag ("A"), btf_type_tag ("B"))) myint;
> +
> +myint x;
> +
> +struct S
> +{
> + myint * __attribute__((btf_type_tag ("A"))) p;
> + unsigned long __attribute__((btf_type_tag ("A")))
> + __attribute__((btf_type_tag ("B")))
> + __attribute__((btf_type_tag ("C"))) l;
> +};
> +
> +char * [[gnu::btf_type_tag ("B"), gnu::btf_type_tag ("C")]] str;
> +
> +unsigned long [[gnu::btf_type_tag ("B")]]
> +do_thing (struct S * __attribute__((btf_type_tag ("C"),
> + btf_type_tag ("B"),
> + btf_type_tag ("A"))) arg)
> +{
> + return arg->l * 2;
> +}
> +
> +/* Expect the following chains of annotations off of the types:
> + 1. int |-> b -> a -> *
> + 2. myint* |-> a -> *
> + 3. unsigned long |-> c -> b -> a -> *
> + 4. char* |-> c -> b -> *
> + 4. unsigned long |-> b -> *
> + 5. struct S* |-> a -> b -> c -> *
> +
> + a and b are reused in 1-3
> + new c,b created in 4, reused in 5
> + new a,b,c created in 5 (not yet deduplicated). */
> +
> +/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\)
> DW_TAG_GNU_annotation" 8 } } */
> +/* { dg-final { scan-assembler-times " DW_AT_GNU_annotation" 11 } } */
> 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
>