[2/5] dwarf: create annotation DIEs for btf tags

Message ID 20241030183155.2038501-3-david.faust@oracle.com
State New
Headers
Series Add btf_decl_tag and btf_type_tag C attributes |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Test passed

Commit Message

David Faust Oct. 30, 2024, 6:31 p.m. UTC
  The btf_decl_tag and btf_type_tag attributes provide a means to annotate
declarations and types respectively with arbitrary user provided
strings.  These strings are recorded in debug information for
post-compilation uses, and despite the name they are meant to be
recorded in DWARF as well as BTF.  New DWARF extensions
DW_TAG_GNU_annotation and DW_AT_GNU_annotation are used to represent
these user annotations in DWARF.

This patch introduces the new DWARF extension DIE and attribute, and
generates them as necessary to represent user annotations from
btf_decl_tag and btf_type_tag.

The format of the new DIE is as follows:

  DW_TAG_GNU_annotation
    DW_AT_name: "btf_decl_tag" or "btf_type_tag"
	DW_AT_const_value: <arbitrary user-supplied string>
	DW_AT_GNU_annotation: <reference to another TAG_GNU_annotation DIE>

DW_AT_GNU_annotation is a new attribute extension used to refer to these
new annotation DIEs.  If non-null in any given declaration or type DIE,
it is a reference to a DW_TAG_GNU_annotation DIE holding an annotation
for that declaration or type.  In addition, the DW_TAG_GNU_annotation
DIEs may also have a non-null DW_AT_GNU_annotation, referring to another
annotation DIE.  This allows chains of annotation DIEs to be formed,
such as in the case where a single declaration has multiple instances of
btf_decl_tag with different string annotations.

gcc/

	* dwarf2out.cc (struct annotation_node, struct annotation_node_hasher)
	(btf_tag_htab): New ancillary structures and hash table.
	(annotation_node_hasher::hash, annotation_node_hasher::equal): New.
	(hash_btf_tag, gen_btf_tag_dies, gen_btf_type_tag_dies)
	(gen_btf_decl_tag_dies): New functions.
	(modified_type_die): Handle btf_type_tag attribute.
	(gen_formal_parameter_die): Call gen_btf_decl_tags for the parameter.
	(gen_decl_die): Call gen_btf_decl_tags for the decl.
	(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/dwarf2out.cc                              | 253 +++++++++++++++++-
 .../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 ++
 include/dwarf2.def                            |   4 +
 8 files changed, 366 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
  

Patch

diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
index 38aedb64470..9f95539062c 100644
--- a/gcc/dwarf2out.cc
+++ b/gcc/dwarf2out.cc
@@ -3696,6 +3696,32 @@  static bool frame_pointer_fb_offset_valid;
 
 static vec<dw_die_ref> base_types;
 
+/* A cached btf_type_tag or btf_decl_tag user annotation.  */
+struct GTY ((for_user)) annotation_node
+{
+  const char *name;
+  const char *value;
+  hashval_t hash;
+  dw_die_ref die;
+  struct annotation_node *next;
+};
+
+struct annotation_node_hasher : ggc_ptr_hash<annotation_node>
+{
+  typedef const struct annotation_node *compare_type;
+
+  static hashval_t hash (struct annotation_node *);
+  static bool equal (const struct annotation_node *,
+		     const struct annotation_node *);
+};
+
+/* A hash table of tag annotation nodes for btf_type_tag and btf_decl_tag C
+   attributes.  DIEs for these user annotations may be reused if they are
+   structurally equivalent; this hash table is used to ensure the DIEs are
+   reused wherever possible.  */
+static GTY (()) hash_table<annotation_node_hasher> *btf_tag_htab;
+
+
 /* Flags to represent a set of attribute classes for attributes that represent
    a scalar value (bounds, pointers, ...).  */
 enum dw_scalar_form
@@ -13649,6 +13675,168 @@  long_double_as_float128 (tree type)
   return NULL_TREE;
 }
 
+
+hashval_t
+annotation_node_hasher::hash (struct annotation_node *node)
+{
+  return node->hash;
+}
+
+bool
+annotation_node_hasher::equal (const struct annotation_node *node1,
+			       const struct annotation_node *node2)
+{
+  return (node1->hash == node2->hash);
+}
+
+/* Return an appropriate entry in the btf tag hash table for a given btf tag.
+   If a structurally equivalent tag (one with the same name, value, and
+   subsequent chain of further tags) has already been processed, then the
+   existing entry for that tag is returned and should be reused.
+   Otherwise, a new entry is added to the hash table and returned.  */
+
+static struct annotation_node *
+hash_btf_tag (tree attr)
+{
+  if (attr == NULL_TREE || TREE_CODE (attr) != TREE_LIST)
+    return NULL;
+
+  if (!btf_tag_htab)
+    btf_tag_htab = hash_table<annotation_node_hasher>::create_ggc (10);
+
+  const char * name = IDENTIFIER_POINTER (get_attribute_name (attr));
+  const char * value = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)));
+  tree chain = lookup_attribute (name, TREE_CHAIN (attr));
+
+  /* Hash for one tag depends on hash of next tag in the chain, because
+     the chain is part of structural equivalence.  */
+  struct annotation_node *chain_node = hash_btf_tag (chain);
+  gcc_checking_assert (chain == NULL_TREE || chain_node != NULL);
+
+  /* Skip any non-btf-tag attributes that might be in the chain.  */
+  if (strcmp (name, "btf_type_tag") != 0 && strcmp (name, "btf_decl_tag") != 0)
+    return chain_node;
+
+  /* Hash for a given tag is determined by the name, value, and chain of
+     further tags.  */
+  inchash::hash h;
+  h.merge_hash (htab_hash_string (name));
+  h.merge_hash (htab_hash_string (value));
+  h.merge_hash (chain_node ? chain_node->hash : 0);
+
+  struct annotation_node node;
+  node.name = name;
+  node.value = value;
+  node.hash = h.end ();
+  node.next = chain_node;
+
+  struct annotation_node **slot = btf_tag_htab->find_slot (&node, INSERT);
+  if (*slot == NULL)
+    {
+      /* Create new htab entry for this annotation.  */
+      struct annotation_node *new_slot
+	= ggc_cleared_alloc<struct annotation_node> ();
+      new_slot->name = name;
+      new_slot->value = value;
+      new_slot->hash = node.hash;
+      new_slot->next = chain_node;
+
+      *slot = new_slot;
+      return new_slot;
+    }
+  else
+    {
+      /* This node is already in the hash table.  */
+      return *slot;
+    }
+}
+
+/* Generate (or reuse) DW_TAG_annotation DIEs representing the btf_type_tag or
+   btf_decl_tag user annotations in ATTR, and update DIE to refer to them
+   via DW_AT_annotation.  If there are multiple type_tag or decl_tag
+   annotations in ATTR, they are all processed recursively by this function
+   to build a chain of annotation DIEs.
+   Return the first annotation DIE in the created (or reused) chain.  */
+
+static dw_die_ref
+gen_btf_tag_dies (tree attr, dw_die_ref die, dw_die_ref context_die)
+{
+  if (attr == NULL_TREE)
+    return die;
+
+  while (dw_get_die_tag (context_die) != DW_TAG_compile_unit)
+    context_die = context_die->die_parent;
+
+  const char * name = IDENTIFIER_POINTER (get_attribute_name (attr));
+  const char * value = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)));
+
+  dw_die_ref tag_die, prev = NULL;
+
+  /* Multiple annotations on the same item form a singly-linked list of
+     annotation DIEs; generate recursively backward from the end so we can
+     chain each created DIE to the next, which has already been created.  */
+  tree rest = lookup_attribute (name, TREE_CHAIN (attr));
+  if (rest)
+    prev = gen_btf_tag_dies (rest, NULL, context_die);
+
+  /* Calculate a hash value for the tag based on its structure, find the
+     existing entry for it (if any) in the hash table, or create a new entry
+     which can be reused by structurally-equivalent tags.  */
+  struct annotation_node *entry = hash_btf_tag (attr);
+  if (!entry)
+    return die;
+
+  /* If the node already has an associated DIE, reuse it.
+     Otherwise, create the new annotation DIE, and associate it with
+     the hash table entry for future reuse.  Any structurally-equivalent
+     tag we process later will find and share the same DIE.  */
+  if (entry->die)
+    tag_die = entry->die;
+  else
+    {
+      tag_die = new_die (DW_TAG_GNU_annotation, context_die, NULL);
+      add_name_attribute (tag_die, name);
+      add_AT_string (tag_die, DW_AT_const_value, value);
+      if (prev)
+	add_AT_die_ref (tag_die, DW_AT_GNU_annotation, prev);
+
+      entry->die = tag_die;
+    }
+
+  if (die)
+    add_AT_die_ref (die, DW_AT_GNU_annotation, tag_die);
+
+  return tag_die;
+}
+
+static void
+gen_btf_type_tag_dies (tree t, tree attr, dw_die_ref target,
+		       dw_die_ref context_die)
+{
+  if (t == NULL_TREE || !TYPE_P (t) || !target)
+    return;
+
+  gen_btf_tag_dies (attr, target, context_die);
+}
+
+static void
+gen_btf_decl_tag_dies (tree t, dw_die_ref target, dw_die_ref context_die)
+{
+  if (t == NULL_TREE || !DECL_P (t) || !target)
+    return;
+
+  tree attr = lookup_attribute ("btf_decl_tag", DECL_ATTRIBUTES (t));
+  if (attr == NULL_TREE)
+    return;
+
+  gen_btf_tag_dies (attr, target, context_die);
+
+  /* Strip the decl tag attribute once we have created the annotation DIEs
+     to avoid attempting process it multiple times.  */
+  DECL_ATTRIBUTES (t)
+    = remove_attribute ("btf_decl_tag", DECL_ATTRIBUTES (t));
+}
+
 /* 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
@@ -13774,10 +13962,44 @@  modified_type_die (tree type, int cv_quals, bool reverse,
 	  dquals &= cv_qual_mask;
 	  if ((dquals & ~cv_quals) != TYPE_UNQUALIFIED
 	      || (cv_quals == dquals && DECL_ORIGINAL_TYPE (name) != type))
-	    /* cv-unqualified version of named type.  Just use
-	       the unnamed type to which it refers.  */
-	    return modified_type_die (DECL_ORIGINAL_TYPE (name), cv_quals,
-				      reverse, context_die);
+	    {
+	      if (lookup_attribute ("btf_type_tag", TYPE_ATTRIBUTES (type)))
+		{
+		  /* Use of a typedef with additional btf_type_tags.  Create
+		     a distinct typedef DIE for this version of the named type
+		     so that the btf_type_tag annotations may be attached to
+		     it without affecting other users of the plain typedef.  */
+		  tree tags = lookup_attribute ("btf_type_tag",
+						TYPE_ATTRIBUTES (type));
+		  tree dtags = lookup_attribute ("btf_type_tag",
+						 TYPE_ATTRIBUTES (dtype));
+
+		  /* Remove type tags before the recursive call to avoid
+		     processing the same attribute multiple times.  */
+		  TYPE_ATTRIBUTES (type)
+		    = remove_attribute ("btf_type_tag", TYPE_ATTRIBUTES (type));
+
+		  dw_die_ref mod_die = modified_type_die (dtype, cv_quals,
+							  reverse, context_die);
+
+		  /* Create a new typedef DIE since the btf_type_tag-ed use of
+		     the typedef has really created a distinct type.  */
+		  if (!attribute_list_equal (tags, dtags))
+		    {
+		      mod_die = clone_die (mod_die);
+		      add_child_die (comp_unit_die (), mod_die);
+		      if (!lookup_type_die (type))
+			equate_type_number_to_die (type, mod_die);
+		    }
+
+		  gen_btf_type_tag_dies (type, tags, mod_die, context_die);
+		  return mod_die;
+		}
+	      /* cv-unqualified version of named type.  Just use
+		 the unnamed type to which it refers.  */
+	      return modified_type_die (DECL_ORIGINAL_TYPE (name), cv_quals,
+					reverse, context_die);
+	    }
 	  /* Else cv-qualified version of named type; fall through.  */
 	}
     }
@@ -13878,6 +14100,18 @@  modified_type_die (tree type, int cv_quals, bool reverse,
 	    first_quals |= dwarf_qual_info[i].q;
 	  }
     }
+  else if (lookup_attribute ("btf_type_tag", TYPE_ATTRIBUTES (type)))
+    {
+      /* Remove type tags before the recursive call to avoid processing the
+	 same attribute multiple times.  */
+      tree attr = lookup_attribute ("btf_type_tag", TYPE_ATTRIBUTES (type));
+      TYPE_ATTRIBUTES (type) = remove_attribute ("btf_type_tag",
+						 TYPE_ATTRIBUTES (type));
+      dw_die_ref mod_die = modified_type_die (type, cv_quals, reverse,
+					      context_die);
+      gen_btf_type_tag_dies (type, attr, mod_die, context_die);
+      return mod_die;
+    }
   else if (code == POINTER_TYPE || code == REFERENCE_TYPE)
     {
       dwarf_tag tag = DW_TAG_pointer_type;
@@ -23090,6 +23324,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;
 	    }
 	}
@@ -23158,6 +23394,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;
 }
 
@@ -27342,6 +27580,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;
 }
 
@@ -33245,6 +33486,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 ())
@@ -33440,6 +33684,7 @@  dwarf2out_cc_finalize (void)
   switch_text_ranges = NULL;
   switch_cold_ranges = NULL;
   current_unit_personality = NULL;
+  btf_tag_htab = NULL;
 
   early_dwarf = false;
   early_dwarf_finished = false;
diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-1.c b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-1.c
new file mode 100644
index 00000000000..a1c1676a7ba
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-1.c
@@ -0,0 +1,11 @@ 
+/* Test simple generation of DW_TAG_GNU_annotation DIE for
+   btf_decl_tag attribute.  */
+/* { dg-do compile } */
+/* { dg-options "-gdwarf -dA" } */
+
+int *foo __attribute__((btf_decl_tag ("my_foo")));
+
+/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_GNU_annotation" 1 } } */
+/* { dg-final { scan-assembler-times " DW_AT_name: \"btf_decl_tag\"" 1 } } */
+/* { dg-final { scan-assembler-times " DW_AT_const_value: \"my_foo\"" 1 } } */
+/* { dg-final { scan-assembler-times " DW_AT_GNU_annotation" 1 } } */
diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-2.c b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-2.c
new file mode 100644
index 00000000000..76583840439
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-2.c
@@ -0,0 +1,25 @@ 
+/* Test dwarf generation for btf_decl_tag on struct and union members.  */
+/* { dg-do compile } */
+/* { dg-options "-gdwarf -dA" } */
+
+#define __tag1 __attribute__((btf_decl_tag ("decl1")))
+#define __tag2 __attribute__((btf_decl_tag ("decl2")))
+
+union U {
+  int i __tag1;
+  unsigned char ub[4];
+};
+
+struct S {
+  union U u;
+  int b __tag2;
+  char *z __tag1;
+};
+
+struct S my_s __tag1 __tag2;
+
+/* We must have two occurrances of one of the two annotation DIEs due to
+   the different attribute sets between declarations above.  */
+/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_GNU_annotation" 3 } } */
+/* { dg-final { scan-assembler-times " DW_AT_name: \"btf_decl_tag\"" 3 } } */
+/* { dg-final { scan-assembler-times " DW_AT_GNU_annotation" 5 } } */
diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-3.c b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-3.c
new file mode 100644
index 00000000000..f3fad8fe3d2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-decl-tag-3.c
@@ -0,0 +1,21 @@ 
+/* Test dwarf generation for btf_decl_tag on functions and function args. */
+/* { dg-do compile } */
+/* { dg-options "-gdwarf -dA" } */
+
+#define __tag1 __attribute__((btf_decl_tag ("decl1")))
+#define __tag2 __attribute__((btf_decl_tag ("decl2")))
+
+int __tag1 __tag2 func (int arg_a __tag1, int arg_b __tag2)
+{
+  return arg_a * arg_b;
+}
+
+int foo (int x) {
+  return func (x, x + 1);
+}
+
+/* In this case one of the decl tag DIEs must be duplicated due to differing
+   DW_AT_GNU_annotation chain between the three uses.  */
+/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_GNU_annotation" 3 } } */
+/* { dg-final { scan-assembler-times " DW_AT_name: \"btf_decl_tag\"" 3 } } */
+/* { dg-final { scan-assembler-times " DW_AT_GNU_annotation" 4 } } */
diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-1.c b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-1.c
new file mode 100644
index 00000000000..772aab09cfb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-1.c
@@ -0,0 +1,10 @@ 
+/* Test simple generation for btf_type_tag attribute.  */
+/* { dg-do compile } */
+/* { dg-options "-gdwarf -dA" } */
+
+int * __attribute__((btf_type_tag("__user"))) ptr;
+
+/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_GNU_annotation" 1 } } */
+/* { dg-final { scan-assembler-times " DW_AT_name: \"btf_type_tag\"" 1 } } */
+/* { dg-final { scan-assembler-times " DW_AT_const_value: \"__user\"" 1 } } */
+/* { dg-final { scan-assembler-times " DW_AT_GNU_annotation" 1 } } */
diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-2.c b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-2.c
new file mode 100644
index 00000000000..9c44e0ee0b9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-2.c
@@ -0,0 +1,31 @@ 
+/* Test that DW_TAG_GNU_annotation DIEs for attribute btf_type_tag are shared
+   where possible.  */
+/* { dg-do compile } */
+/* { dg-options "-gdwarf -dA" } */
+
+#define __tag1 __attribute__((btf_type_tag("tag1")))
+#define __tag2 __attribute__((btf_type_tag("tag2")))
+
+int __tag1 foo;
+char * __tag1 __tag2 bar;
+
+struct S
+{
+  unsigned char bytes[8];
+  unsigned long __tag1 t;
+  void *ptr;
+};
+
+struct S * __tag1 __tag2 my_S;
+
+/* Only 2 DW_TAG_GNU_annotation DIEs should be generated, one each for "tag1"
+   and "tag2", and they should be reused.  */
+/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_GNU_annotation" 2 } } */
+/* { dg-final { scan-assembler-times " DW_AT_name: \"btf_type_tag\"" 2 } } */
+/* { dg-final { scan-assembler-times " DW_AT_const_value: \"tag1\"" 1 } } */
+/* { dg-final { scan-assembler-times " DW_AT_const_value: \"tag2\"" 1 } } */
+
+/* Each attribute-ed type shall refer via DW_AT_GNU_annotation to the
+   appropriate annotation DIE, including the annotation DIE for "tag2" which
+   is always chained to the DIE for "tag1" in this construction.  */
+/* { dg-final { scan-assembler-times " DW_AT_GNU_annotation" 5 } } */
diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-3.c b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-3.c
new file mode 100644
index 00000000000..d02144c8004
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-3.c
@@ -0,0 +1,15 @@ 
+/* Test dwarf generation for btf_type_tag with cv-quals and typedefs.  */
+/* { dg-do compile } */
+/* { dg-options "-gdwarf -dA" } */
+
+#define __tag1 __attribute__((btf_type_tag ("tag1")))
+#define __tag2 __attribute__((btf_type_tag ("tag2")))
+
+typedef const int foo;
+typedef int __tag1 bar;
+
+foo __tag2 x;
+const bar y;
+
+/* { dg-final { scan-assembler-times "DIE \\(\[^\n\]*\\) DW_TAG_GNU_annotation" 2 } } */
+/* { dg-final { scan-assembler-times " DW_AT_name: \"btf_type_tag\"" 2 } } */
diff --git a/include/dwarf2.def b/include/dwarf2.def
index 66c7fa1220f..6a3a986cf70 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)
@@ -453,6 +456,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.  */