On 2/6/25 11:54 AM, David Faust wrote:
Support the btf_decl_tag and btf_type_tag attributes in BTF by creating
and emitting BTF_KIND_DECL_TAG and BTF_KIND_TYPE_TAG records,
respectively, for them.

Some care is required when -gprune-btf is in effect to avoid emitting
decl or type tags for declarations or types which have been pruned and
will not be emitted in BTF.


The patch itself looks OK to me.  Two comments below.

gcc/
        * btfout.cc (get_btf_kind): Handle DECL_TAG and TYPE_TAG kinds.
        (btf_calc_num_vbytes): Likewise.
        (btf_asm_type): Likewise.
        (output_asm_btf_vlen_bytes): Likewise.
        (output_btf_tags): New.
        (btf_output): Call it here.
        (btf_add_used_type): Replace with simple wrapper around...
        (btf_add_used_type_1): ...the implementation.  Handle
        BTF_KIND_DECL_TAG and BTF_KIND_TYPE_TAG.
        (btf_add_vars): Update btf_add_used_type call.
        (btf_assign_tag_ids): New.
        (btf_mark_type_used): Update btf_add_used_type call.
        (btf_collect_pruned_types): Likewise.  Handle type and decl tags.
        (btf_finish): Call btf_assign_tag_ids.

gcc/testsuite/
        * gcc.dg/debug/btf/btf-decl-tag-1.c: New test.
        * gcc.dg/debug/btf/btf-decl-tag-2.c: New test.
        * gcc.dg/debug/btf/btf-decl-tag-3.c: New test.
        * gcc.dg/debug/btf/btf-decl-tag-4.c: New test.
        * gcc.dg/debug/btf/btf-type-tag-1.c: New test.
        * gcc.dg/debug/btf/btf-type-tag-2.c: New test.
        * gcc.dg/debug/btf/btf-type-tag-3.c: New test.
        * gcc.dg/debug/btf/btf-type-tag-4.c: New test.
        * gcc.dg/debug/btf/btf-type-tag-5.c: New test.
        * gcc.dg/debug/btf/btf-type-tag-6.c: New test.
        * gcc.dg/debug/btf/btf-type-tag-c2x-1.c: New test.


I think its worth adding a testcase in the BPF backend tests for testing (no) interaction between CO-RE relocs with tags. E.g, when __attribute__((preserve_access_index)) is used on a struct with type tags, the type id in the CO-RE relocation records is the type and not the type tag. Or other tests as you see fit.

WDYT ?

include/
        * btf.h (BTF_KIND_DECL_TAG, BTF_KIND_TYPE_TAG) New defines.
        (struct btf_decl_tag): New.
---
  gcc/btfout.cc                                 | 171 +++++++++++++++---
  .../gcc.dg/debug/btf/btf-decl-tag-1.c         |  14 ++
  .../gcc.dg/debug/btf/btf-decl-tag-2.c         |  22 +++
  .../gcc.dg/debug/btf/btf-decl-tag-3.c         |  22 +++
  .../gcc.dg/debug/btf/btf-decl-tag-4.c         |  34 ++++
  .../gcc.dg/debug/btf/btf-type-tag-1.c         |  27 +++
  .../gcc.dg/debug/btf/btf-type-tag-2.c         |  15 ++
  .../gcc.dg/debug/btf/btf-type-tag-3.c         |  21 +++
  .../gcc.dg/debug/btf/btf-type-tag-4.c         |  25 +++
  .../gcc.dg/debug/btf/btf-type-tag-5.c         |  35 ++++
  .../gcc.dg/debug/btf/btf-type-tag-6.c         |  15 ++
  .../gcc.dg/debug/btf/btf-type-tag-c2x-1.c     |  23 +++
  include/btf.h                                 |  14 ++
  13 files changed, 414 insertions(+), 24 deletions(-)
  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-decl-tag-1.c
  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-decl-tag-2.c
  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-decl-tag-3.c
  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-decl-tag-4.c
  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-1.c
  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-2.c
  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-3.c
  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-4.c
  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-5.c
  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-6.c
  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-c2x-1.c

diff --git a/gcc/btfout.cc b/gcc/btfout.cc
index ff7ea42a961..c00e0c98015 100644
--- a/gcc/btfout.cc
+++ b/gcc/btfout.cc
@@ -141,6 +141,8 @@ get_btf_kind (uint32_t ctf_kind)
      case CTF_K_VOLATILE: return BTF_KIND_VOLATILE;
      case CTF_K_CONST:    return BTF_KIND_CONST;
      case CTF_K_RESTRICT: return BTF_KIND_RESTRICT;
+    case CTF_K_DECL_TAG: return BTF_KIND_DECL_TAG;
+    case CTF_K_TYPE_TAG: return BTF_KIND_TYPE_TAG;
      default:;
      }
    return BTF_KIND_UNKN;
@@ -217,6 +219,7 @@ btf_calc_num_vbytes (ctf_dtdef_ref dtd)
      case BTF_KIND_CONST:
      case BTF_KIND_RESTRICT:
      case BTF_KIND_FUNC:
+    case BTF_KIND_TYPE_TAG:
      /* These kinds have no vlen data.  */
        break;
@@ -256,6 +259,10 @@ btf_calc_num_vbytes (ctf_dtdef_ref dtd)
        vlen_bytes += vlen * sizeof (struct btf_var_secinfo);
        break;
+ case BTF_KIND_DECL_TAG:
+      vlen_bytes += sizeof (struct btf_decl_tag);
+      break;
+
      default:
        break;
      }
@@ -452,6 +459,20 @@ btf_asm_type (ctf_dtdef_ref dtd)
         and should write 0.  */
        dw2_asm_output_data (4, 0, "(unused)");
        return;
+    case BTF_KIND_DECL_TAG:
+      {
+       if (dtd->ref_type)
+         break;
+       else if (dtd->dtd_u.dtu_tag.ref_var)
+         {
+           /* ref_type is NULL for decl tag attached to a variable.  */
+           ctf_dvdef_ref dvd = dtd->dtd_u.dtu_tag.ref_var;
+           dw2_asm_output_data (4, dvd->dvd_id,
+                                "btt_type: (BTF_KIND_VAR '%s')",
+                                dvd->dvd_name);
+           return;
+         }
+      }
      default:
        break;
      }
@@ -801,6 +822,12 @@ output_asm_btf_vlen_bytes (ctf_container_ref ctfc, 
ctf_dtdef_ref dtd)
         at this point.  */
        gcc_unreachable ();
+ case BTF_KIND_DECL_TAG:
+      dw2_asm_output_data (4, dtd->dtd_u.dtu_tag.component_idx,
+                          "component_idx=%d",
+                          dtd->dtd_u.dtu_tag.component_idx);
+      break;
+
      default:
        /* All other BTF type kinds have no variable length data.  */
        break;
@@ -851,6 +878,20 @@ output_btf_func_types (void)
      btf_asm_func_type (ref);
  }
+static void
+output_btf_tags (ctf_container_ref ctfc)
+{
+  /* If pruning, tags which are not pruned have already been added to
+     the used list and output by output_btf_types.  */
+  if (debug_prune_btf)
+    return;
+
+  ctf_dtdef_ref dtd;
+  unsigned i;
+  FOR_EACH_VEC_ELT (*ctfc->ctfc_tags, i, dtd)
+    output_asm_btf_type (ctfc, dtd);
+}
+
  /* Output all BTF_KIND_DATASEC records.  */
static void
@@ -869,6 +910,7 @@ btf_output (ctf_container_ref ctfc)
    output_btf_types (ctfc);
    output_btf_vars (ctfc);
    output_btf_func_types ();
+  output_btf_tags (ctfc);
    output_btf_datasec_types ();
    output_btf_strs (ctfc);
  }
@@ -985,7 +1027,8 @@ static vec<struct btf_fixup> fixups;
     is created and emitted.  This vector stores them.  */
  static GTY (()) vec<ctf_dtdef_ref, va_gc> *forwards;
-/* Recursively add type DTD and any types it references to the used set.
+/* Implementation of btf_add_used_type.
+   Recursively add type DTD and any types it references to the used set.
     Return a type that should be used for references to DTD - usually DTD 
itself,
     but may be NULL if DTD corresponds to a type which will not be emitted.
     CHECK_PTR is true if one of the predecessors in recursive calls is a struct
@@ -996,8 +1039,8 @@ static GTY (()) vec<ctf_dtdef_ref, va_gc> *forwards;
     CREATE_FIXUPS is false.  */
static ctf_dtdef_ref
-btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd,
-                  bool check_ptr, bool seen_ptr, bool create_fixups)
+btf_add_used_type_1 (ctf_container_ref ctfc, ctf_dtdef_ref dtd,
+                    bool check_ptr, bool seen_ptr, bool create_fixups)
  {
    if (dtd == NULL)
      return NULL;
@@ -1029,8 +1072,9 @@ btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref 
dtd,
                  fixups.unordered_remove (i);
/* Add the concrete base type. */
-             dtd->ref_type = btf_add_used_type (ctfc, dtd->ref_type, check_ptr,
-                                                seen_ptr, create_fixups);
+             dtd->ref_type = btf_add_used_type_1 (ctfc, dtd->ref_type,
+                                                  check_ptr, seen_ptr,
+                                                  create_fixups);
              return dtd;
            }
        default:
@@ -1044,8 +1088,8 @@ btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref 
dtd,
         the reference to the bitfield.  The slice type won't be emitted,
         but we need the information in it when writing out the bitfield
         encoding.  */
-      btf_add_used_type (ctfc, dtd->dtd_u.dtu_slice.cts_type,
-                        check_ptr, seen_ptr, create_fixups);
+      btf_add_used_type_1 (ctfc, dtd->dtd_u.dtu_slice.cts_type,
+                          check_ptr, seen_ptr, create_fixups);
        return dtd;
      }
@@ -1069,7 +1113,11 @@ btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd,
      case BTF_KIND_INT:
      case BTF_KIND_FLOAT:
      case BTF_KIND_FWD:
-      /* Leaf kinds which do not refer to any other types.  */
+    case BTF_KIND_DECL_TAG:
+      /* Leaf kinds which do not refer to any other types.
+        BTF_KIND_DECL_TAG is a special case: we treat it as though it does not
+        refer to any other types, since we only want the DECL_TAG to be added
+        if the type to which it refers has already been added.  */
        break;
case BTF_KIND_FUNC:
@@ -1082,6 +1130,7 @@ btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref 
dtd,
      case BTF_KIND_CONST:
      case BTF_KIND_VOLATILE:
      case BTF_KIND_RESTRICT:
+    case BTF_KIND_TYPE_TAG:
        {
        /* These type kinds refer to exactly one other type.  */
        if (check_ptr && !seen_ptr)
@@ -1106,18 +1155,18 @@ btf_add_used_type (ctf_container_ref ctfc, 
ctf_dtdef_ref dtd,
          }
/* Add the type to which this type refers. */
-       dtd->ref_type = btf_add_used_type (ctfc, dtd->ref_type, check_ptr,
-                                          seen_ptr, create_fixups);
+       dtd->ref_type = btf_add_used_type_1 (ctfc, dtd->ref_type, check_ptr,
+                                            seen_ptr, create_fixups);
        break;
        }
      case BTF_KIND_ARRAY:
        {
        /* Add element and index types.  */
        ctf_arinfo_t *arr = &(dtd->dtd_u.dtu_arr);
-       arr->ctr_contents = btf_add_used_type (ctfc, arr->ctr_contents, false,
-                                              false, create_fixups);
-       arr->ctr_index = btf_add_used_type (ctfc, arr->ctr_index, false, false,
-                                           create_fixups);
+       arr->ctr_contents = btf_add_used_type_1 (ctfc, arr->ctr_contents,
+                                                false, false, create_fixups);
+       arr->ctr_index = btf_add_used_type_1 (ctfc, arr->ctr_index, false,
+                                             false, create_fixups);
        break;
        }
      case BTF_KIND_STRUCT:
@@ -1133,8 +1182,8 @@ btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref 
dtd,
            /* Add member type for struct/union members.  For enums, only the
               enumerator names are needed.  */
            if (kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION)
-             dmd->dmd_type = btf_add_used_type (ctfc, dmd->dmd_type, true,
-                                                false, create_fixups);
+             dmd->dmd_type = btf_add_used_type_1 (ctfc, dmd->dmd_type, true,
+                                                  false, create_fixups);
            ctf_add_string (ctfc, dmd->dmd_name, &(dmd->dmd_name_offset),
                            CTF_STRTAB);
          }
@@ -1143,16 +1192,17 @@ btf_add_used_type (ctf_container_ref ctfc, 
ctf_dtdef_ref dtd,
      case BTF_KIND_FUNC_PROTO:
        {
        /* Add return type.  */
-       dtd->ref_type = btf_add_used_type (ctfc, dtd->ref_type, false, false,
-                                          create_fixups);
+       dtd->ref_type = btf_add_used_type_1 (ctfc, dtd->ref_type, false, false,
+                                            create_fixups);
/* Add arg types. */
        ctf_func_arg_t * farg;
        for (farg = dtd->dtd_u.dtu_argv;
             farg != NULL; farg = (ctf_func_arg_t *) ctf_farg_list_next (farg))
          {
-           farg->farg_type = btf_add_used_type (ctfc, farg->farg_type, false,
-                                                false, create_fixups);
+           farg->farg_type = btf_add_used_type_1 (ctfc, farg->farg_type,
+                                                  false, false,
+                                                  create_fixups);
            /* Note: argument names are stored in the auxilliary string table,
               since CTF does not include arg names.  That table has not been
               cleared, so no need to re-add argument names here.  */
@@ -1166,6 +1216,16 @@ btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref 
dtd,
    return dtd;
  }
+/* Recursively add type DTD and any types it references to the used set.
+   Return a type that should be used for references to DTD - usually DTD 
itself,
+   but may be NULL if DTD corresponds to a type which will not be emitted.  */
+
+static ctf_dtdef_ref
+btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
+{
+  return btf_add_used_type_1 (ctfc, dtd, false, false, true);
+}
+
  /* Initial entry point of BTF generation, called at early_finish () after
     CTF information has possibly been output.  Translate all CTF information
     to BTF, and do any processing that must be done early, such as creating
@@ -1402,7 +1462,7 @@ btf_add_vars (ctf_container_ref ctfc)
              ctf_dmdef_t *dmd;
              for (dmd = dtd->dtd_u.dtu_members;
                   dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
-               btf_add_used_type (ctfc, dmd->dmd_type, false, false, true);
+               btf_add_used_type (ctfc, dmd->dmd_type);
            }
        }
      }
@@ -1488,6 +1548,45 @@ btf_assign_var_ids (ctf_container_ref ctfc)
      }
  }
+/* Assign BTF IDs for type and decl tags and account for their size. */
+
+static void
+btf_assign_tag_ids (ctf_container_ref ctfc)
+{
+  /* Both decl and type tags may be pruned if the types/decls to which they
+     refer are pruned.  This is handled in btf_collect_pruned_types, and
+     through that process they have also been assigned ids already.  */
+  if (debug_prune_btf)
+    return;
+
+  size_t num_tags = vec_safe_length (ctfc->ctfc_tags);
+  if (num_tags == 0)
+    return;
+
+  unsigned int i;
+  ctf_dtdef_ref dtd;
+  FOR_EACH_VEC_ELT (*ctfc->ctfc_tags, i, dtd)
+    {
+      /* Assign BTF id.  */
+      ctf_id_t id = ctfc->ctfc_nextid++;
+      gcc_assert (id <= BTF_MAX_TYPE);
+      dtd->dtd_type = id;
+
+      /* Tags on functions will have a ref_type pointing to the
+        FUNC_PROTO, we want them to point the FUNC record instead.  */
+      ctf_dtdef_ref *pdtd = NULL;
+      if (dtd->ref_type && (pdtd = func_map->get (dtd->ref_type)) != NULL)
+       dtd->ref_type = *pdtd;
+
+      /* Strings for tags are stored in the auxilliary strtab, which is
+        concatenated after the regular strtab.  ctti_name only accounts
+        for offset in the auxiliary strtab until this point.  */
+      dtd->dtd_data.ctti_name += ctfc_get_strtab_len (ctfc, CTF_STRTAB);
+      ctfc->ctfc_num_types++;
+      ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (dtd);
+    }
+}
+
  /* Assign BTF IDs for datasec records and account for their size.  */
static void
@@ -1522,7 +1621,7 @@ btf_mark_type_used (tree t)
    if (!dtd)
      return;
- btf_add_used_type (ctfc, dtd, false, false, true);
+  btf_add_used_type (ctfc, dtd);
  }
/* Callback used for assembling the only-used-types list. Note that this is
@@ -1549,7 +1648,7 @@ btf_collect_pruned_types (ctf_container_ref ctfc)
    size_t i;
    FOR_EACH_VEC_ELT (*funcs, i, dtd)
      {
-      btf_add_used_type (ctfc, dtd->ref_type, false, false, true);
+      btf_add_used_type (ctfc, dtd->ref_type);
        ctf_add_string (ctfc, dtd->dtd_name, &(dtd->dtd_data.ctti_name),
                      CTF_STRTAB);
      }
@@ -1558,10 +1657,33 @@ btf_collect_pruned_types (ctf_container_ref ctfc)
    for (i = 0; i < ctfc->ctfc_vars_list_count; i++)
      {
        ctf_dvdef_ref dvd = ctfc->ctfc_vars_list[i];
-      btf_add_used_type (ctfc, dvd->dvd_type, false, false, true);
+      btf_add_used_type (ctfc, dvd->dvd_type);
        ctf_add_string (ctfc, dvd->dvd_name, &(dvd->dvd_name_offset), 
CTF_STRTAB);
      }
+ /* Used type tags will be added by recursive btf_add_used_type calls above.
+     For decl tags, scan the list and only add those decl tags whose referent
+     types are marked as used.  We may have pruned a struct type with members
+     annotated by a decl tag.  */
+  FOR_EACH_VEC_ELT (*ctfc->ctfc_tags, i, dtd)
+    {
+      /* Only add decl tags whose referent types have not been pruned.
+        Variables are never pruned, so decl tags on variables are always
+        used.  */
+      if (btf_dtd_kind (dtd) == BTF_KIND_DECL_TAG
+         && ((dtd->ref_type && btf_used_types->contains (dtd->ref_type))
+             || (dtd->dtd_u.dtu_tag.ref_var)))
+       btf_add_used_type (ctfc, dtd);
+
+      /* Tags on functions or function args will have a ref_type pointing to 
the
+        FUNC_PROTO, we want them to point the FUNC record instead.  */
+      ctf_dtdef_ref *pdtd = NULL;
+      if (dtd->ref_type
+         && btf_used_types->contains (dtd->ref_type)
+         && (pdtd = func_map->get (dtd->ref_type)) != NULL)
+       dtd->ref_type = *pdtd;
+    }
+
    /* Process fixups.  If the base type was never added, create a forward for 
it
       and adjust the reference to point to that.  If it was added, then nothing
       needs to change.  */
@@ -1634,6 +1756,7 @@ btf_finish (void)
btf_assign_var_ids (tu_ctfc);
    btf_assign_func_ids (tu_ctfc);
+  btf_assign_tag_ids (tu_ctfc);
    btf_assign_datasec_ids (tu_ctfc);
/* Finally, write out the complete .BTF section. */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-decl-tag-1.c 
b/gcc/testsuite/gcc.dg/debug/btf/btf-decl-tag-1.c
new file mode 100644
index 00000000000..c933d84b497
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-decl-tag-1.c
@@ -0,0 +1,14 @@
+/* Test simple BTF decl tag generation for variables.  */
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+#define __tag1 __attribute__((btf_decl_tag ("decl1")))
+#define __tag2 __attribute__((btf_decl_tag ("decl2")))
+#define __tag3 __attribute__((btf_decl_tag ("decl3")))
+
+int x __tag1 __tag2;
+int y __tag1;
+
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_VAR 
'x'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_VAR 
'x'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_VAR 
'y'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-decl-tag-2.c 
b/gcc/testsuite/gcc.dg/debug/btf/btf-decl-tag-2.c
new file mode 100644
index 00000000000..c4f09ca839c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-decl-tag-2.c
@@ -0,0 +1,22 @@
+/* Test BTF decl tag generation for structs.  */
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+#define __tag1 __attribute__((btf_decl_tag ("decl1")))
+#define __tag2 __attribute__((btf_decl_tag ("decl2")))
+#define __tag3 __attribute__((btf_decl_tag ("decl3")))
+
+struct Foo {
+  int a;
+  int b __tag3 __tag2;
+  char *z __tag1;
+};
+
+struct Foo f __tag1 __tag2;
+
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_STRUCT 
'Foo'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=1" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl3'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_STRUCT 
'Foo'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=1" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_STRUCT 
'Foo'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=2" 1} } */
+
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_VAR 
'f'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_VAR 
'f'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-decl-tag-3.c 
b/gcc/testsuite/gcc.dg/debug/btf/btf-decl-tag-3.c
new file mode 100644
index 00000000000..7eb8a93ec12
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-decl-tag-3.c
@@ -0,0 +1,22 @@
+/* Test BTF decl tag generation for functions and function args.  */
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+#define __tag1 __attribute__((btf_decl_tag ("decl1")))
+#define __tag2 __attribute__((btf_decl_tag ("decl2")))
+#define __tag3 __attribute__((btf_decl_tag ("decl3")))
+
+int __tag1 __tag2 func (int arg_a __tag3 __tag1, int arg_b __tag2)
+{
+  return arg_a * arg_b;
+}
+
+int foo (int x) {
+  return func (x, x + 1);
+}
+
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_FUNC 
'func'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_FUNC 
'func'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_FUNC 
'func'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=0" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl3'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_FUNC 
'func'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=0" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_FUNC 
'func'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=1" 1} } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-decl-tag-4.c 
b/gcc/testsuite/gcc.dg/debug/btf/btf-decl-tag-4.c
new file mode 100644
index 00000000000..a9022375529
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-decl-tag-4.c
@@ -0,0 +1,34 @@
+/* Test BTF decl tag generation with BTF pruning.  */
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -gprune-btf -dA" } */
+
+#define __decl1 __attribute__((btf_decl_tag ("decl1")))
+#define __decl2 __attribute__((btf_decl_tag ("decl2")))
+#define __decl3 __attribute__((btf_decl_tag ("decl3")))
+
+struct S {
+  /* This tag on S.v shall not be emitted, because struct S is pruned and
+     replaced with a FWD, which does not hold any member info.  */
+  int v __decl3;
+  int w;
+};
+
+struct T {
+  int a;
+  struct S *s __decl1;
+  int c __decl2;
+};
+
+struct T t __decl1;
+
+int __decl1 func (struct T *t __decl2)
+{
+  return t->a + t->c;
+}
+
+/* { dg-final { scan-assembler-not " BTF_KIND_DECL_TAG 'decl3'" } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_STRUCT 
'T'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=1" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_STRUCT 
'T'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=2" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_VAR 
't'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_FUNC 
'func'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 
'decl2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_FUNC 
'func'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=0" 1} } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-1.c 
b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-1.c
new file mode 100644
index 00000000000..750add9d923
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-1.c
@@ -0,0 +1,27 @@
+/* Test simple generation of BTF type tags.  */
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+#define __tag1 __attribute__((btf_type_tag("type1")))
+#define __tag2 __attribute__((btf_type_tag("type2")))
+#define __tag3 __attribute__((btf_type_tag("type3")))
+
+int __tag1 __tag2 x;
+int __tag1 y;
+
+struct Foo {
+  char a;
+  int b;
+};
+
+struct Foo * __tag1 __tag3 f;
+
+/* { dg-final { scan-assembler-times " BTF_KIND_VAR 
'x'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'type2'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'type2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'type1'\\)" 1} } */
+
+/* { dg-final { scan-assembler-times " BTF_KIND_VAR 
'y'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'type1'\\)" 1 } } */
+/* { dg-final { scan-assembler " BTF_KIND_TYPE_TAG 
'type1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT" } } */
+
+/* { dg-final { scan-assembler-times " BTF_KIND_VAR 
'f'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'type3'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'type3'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'type1'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'type1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_PTR" 1 } } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-2.c 
b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-2.c
new file mode 100644
index 00000000000..31b6b779727
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-2.c
@@ -0,0 +1,15 @@
+/* Test BTF type tag generation with cv-quals.  */
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+#define __tag1 __attribute__((btf_type_tag("type1")))
+#define __tag2 __attribute__((btf_type_tag("type2")))
+#define __tag3 __attribute__((btf_type_tag("type3")))
+
+/* var (a) -> const -> tag2 -> tag1 -> int  */
+const int __tag1 __tag2 a;
+
+/* { dg-final { scan-assembler-times " BTF_KIND_VAR 
'a'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_CONST ''\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'type2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'type1'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'type1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_CONST 
''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'type2'\\)" 1 } } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-3.c 
b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-3.c
new file mode 100644
index 00000000000..c6523830f07
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-3.c
@@ -0,0 +1,21 @@
+/* Test BTF type tag generation with typedefs.  */
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+#define __tag1 __attribute__((btf_type_tag("type1")))
+#define __tag2 __attribute__((btf_type_tag("type2")))
+#define __tag3 __attribute__((btf_type_tag("type3")))
+
+typedef const int foo;
+typedef int __tag1 bar;
+
+foo __tag2 x;
+const bar y;
+
+/* { dg-final { scan-assembler-times " BTF_KIND_VAR 
'x'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'type2'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'type2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPEDEF 'foo'\\)" 1 } } */
+
+/* { dg-final { scan-assembler-times " BTF_KIND_VAR 
'y'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_CONST" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_CONST 
''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPEDEF 'bar'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPEDEF 
'bar'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'type1'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'type1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT" 1 } } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-4.c 
b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-4.c
new file mode 100644
index 00000000000..1fb97232242
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-4.c
@@ -0,0 +1,25 @@
+/* Test BTF type tag generation with BTF pruning.  */
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -gprune-btf -dA" } */
+
+#define __tag1 __attribute__((btf_type_tag("type1")))
+#define __tag2 __attribute__((btf_type_tag("type2")))
+#define __tag3 __attribute__((btf_type_tag("type3")))
+
+struct S {
+  int v ;
+  /* This tag on S.w shall not be emitted because struct S is pruned.  */
+  int __tag3 w;
+};
+
+struct T {
+  int __tag1 a;
+  struct S * __tag2 s;
+  int c;
+};
+
+struct T t;
+
+/* { dg-final { scan-assembler-not " BTF_KIND_TYPE_TAG 'type3'" } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'type1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'type2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_PTR" 1} } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-5.c 
b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-5.c
new file mode 100644
index 00000000000..d8551381152
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-5.c
@@ -0,0 +1,35 @@
+/* Test type tags applied to struct, union, enum types.  */
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+enum E
+{
+  ONE,
+  TWO,
+  THREE,
+} __attribute__((btf_type_tag ("v1")));
+
+volatile enum E volatile_e;
+enum E plain_e;
+
+struct S
+{
+  int x;
+  char y;
+} __attribute__((btf_type_tag ("v2")));
+
+typedef struct S Stu;
+const Stu s;
+
+union U
+{
+  unsigned long ul;
+  unsigned char bytes[8];
+} __attribute__((btf_type_tag ("v3")));
+
+typedef const union U Uni;
+Uni u;
+
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'v1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_ENUM" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'v2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_STRUCT" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'v3'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_UNION" 1} } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-6.c 
b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-6.c
new file mode 100644
index 00000000000..f1511d57150
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-6.c
@@ -0,0 +1,15 @@
+/* Test type tags applied to struct typedefs.  */
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+typedef struct  __attribute__((btf_type_tag ("_s_tag"), btf_type_tag ("td")))
+{
+  int a;
+  int b;
+  int c;
+} Foo_Struct;
+
+Foo_Struct __attribute__((btf_type_tag ("_instance"))) f; /* { dg-warning "ignoring 
attributes applied to 'struct <anonymous>' after definition" } */
+
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'td'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG" 1} } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'_s_tag'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_STRUCT" 1} } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-c2x-1.c 
b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-c2x-1.c
new file mode 100644
index 00000000000..c5c915486e1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-c2x-1.c
@@ -0,0 +1,23 @@
+/* Test BTF type tag generation using C2x standard attribute syntax.
+   Note: in more complicated cases the __attribute__ syntax causes BTF type 
tags
+   to "slide around" in defined, but often unexpected ways.  */
+

What do you think about mentioning something along these lines in the documentation for the btf_type_tag in patch 5?

My thinking is that users may be caught off guard wrt expectation around ordering.

+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+#define __tag1 [[gnu::btf_type_tag ("type1")]]
+#define __tag2 [[gnu::btf_type_tag ("type2")]]
+#define __tag3 [[gnu::btf_type_tag ("type3")]]
+
+/* var (z) -> const -> tag2 -> tag3 -> ptr -> tag2 -> ptr -> volatile -> tag1 
-> int */
+volatile int __tag1 * __tag2 * __tag3 __tag2 const z;
+
+/* { dg-final { scan-assembler-times " BTF_KIND_VAR 
'z'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_CONST" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_CONST 
''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'type2'" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'type2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'type3'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'type3'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_PTR" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_PTR 
''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'type2'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'type2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_PTR" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_PTR 
''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_VOLATILE" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_VOLATILE 
''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'type1'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG 
'type1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT" 1 } } */
diff --git a/include/btf.h b/include/btf.h
index 994d02dcfb3..d20ede23fb3 100644
--- a/include/btf.h
+++ b/include/btf.h
@@ -114,6 +114,8 @@ struct btf_type
  #define BTF_KIND_VAR          14      /* Variable.  */
  #define BTF_KIND_DATASEC      15      /* Section such as .bss or .data.  */
  #define BTF_KIND_FLOAT                16      /* Floating point.  */
+#define BTF_KIND_DECL_TAG      17      /* Declaration tag.  */
+#define BTF_KIND_TYPE_TAG      18      /* Type tag.  */
  #define BTF_KIND_ENUM64       19      /* Enumeration up to 64 bits.  */
  #define BTF_KIND_MAX          BTF_KIND_ENUM64
  #define NR_BTF_KINDS          (BTF_KIND_MAX + 1)
@@ -227,6 +229,18 @@ struct btf_enum64
    uint32_t val_hi32;  /* high 32-bit value for a 64-bit value Enumerator */
  };
+/* BTF_KIND_DECL_TAG is followed by a single struct btf_decl_tag, which
+   describes the item to which the tag applies:
+   - If component_idx == (uint32_t) -1, then the tag applies to item referred
+     to by the type_id.
+   - Otherwise, the tag applies to the struct or union member, or function
+     argument of the type referred to by type_id with the 0-based index
+     given by component_idx.  */
+struct btf_decl_tag
+{
+  uint32_t component_idx;
+};
+
  #ifdef        __cplusplus
  }
  #endif

Reply via email to