public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH v11 0/6] c,dwarf,btf: Add btf_decl_tag and btf_type_tag C attributes
@ 2025-09-27  9:06 David Faust
  2025-09-27  9:06 ` [PATCH v11 1/6] c-family: add btf_type_tag and btf_decl_tag attributes David Faust
                   ` (7 more replies)
  0 siblings, 8 replies; 11+ messages in thread
From: David Faust @ 2025-09-27  9:06 UTC (permalink / raw)
  To: gcc-patches
  Cc: josmyers, rguenther, jose.marchesi, indu.bhagat, yonghong.song

[v10: https://gcc.gnu.org/pipermail/gcc-patches/2025-September/696032.html
 Changed from v10:
 - Patch 6: Fix error introduced in v10 that caused the patch to include
   duplicate documentation for counted_by variable attribute.

 Review status:
 - Patches 1-4 have been OK'd.
 - Patch 5 needs review.
 - Patch 6 adds two small BPF-specific tests that I'll count as obvious
   without further review unless there is an objection.  ]

This patch series adds support for the btf_decl_tag and btf_type_tag attributes
to GCC. This entails:

- Two new C-family attributes that allow to associate (to "tag") particular
  declarations and types with arbitrary strings. As explained below, this is
  intended to be used to, for example, characterize certain pointer types.  A
  single declaration or type may have multiple occurrences of these attributes.

- The conveyance of that information in the DWARF output in the form of a new
  DIE: DW_TAG_GNU_annotation, and a new attribute: DW_AT_GNU_annotation.

- The conveyance of that information in the BTF output in the form of two new
  kinds of BTF objects: BTF_KIND_DECL_TAG and BTF_KIND_TYPE_TAG. These BTF
  kinds are already supported by LLVM and other tools in the BPF ecosystem.

Both of these attributes are already supported by clang, and are already being
used in various ways by BPF support inside the Linux kernel.

It is worth noting that while the Linux kernel and BPF/BTF is the motivating use
case of this feature, the format of the new DWARF extension is generic.  This
work could be easily adapted to provide a general way for program authors to
annotate types and declarations with arbitrary information for any
post-compilation analysis needs, not just the Linux kernel BPF verifier.  For
example, these annotations could be used to aid in ABI analysis.

Purpose
=======

1)  Addition of C-family language constructs (attributes) to specify free-text
    tags on certain language elements, such as struct fields.

    The purpose of these annotations is to provide additional information about
    types, variables, and function parameters of interest to the kernel. A
    driving use case is to tag pointer types within the Linux kernel and BPF
    programs with additional semantic information, such as '__user' or '__rcu'.

    For example, consider the Linux kernel function do_execve with the
    following declaration:

      static int do_execve(struct filename *filename,
         const char __user *const __user *__argv,
         const char __user *const __user *__envp);

    Here, __user could be defined with these annotations to record semantic
    information about the pointer parameters (e.g., they are user-provided) in
    DWARF and BTF information. Other kernel facilities such as the BPF verifier
    can read the tags and make use of the information.

2)  Conveying the tags in the generated DWARF debug info.

    The main motivation for emitting the tags in DWARF is that the Linux kernel
    generates its BTF information via pahole, using DWARF as a source:

        +--------+  BTF                  BTF   +----------+
        | pahole |-------> vmlinux.btf ------->| verifier |
        +--------+                             +----------+
            ^                                        ^
            |                                        |
      DWARF |                                    BTF |
            |                                        |
         vmlinux                              +-------------+
         module1.ko                           | BPF program |
         module2.ko                           +-------------+
           ...

    This is because:

    a)  Unlike GCC, LLVM will only generate BTF for BPF programs.

    b)  GCC can generate BTF for whatever target with -gbtf, but there is no
        support for linking/deduplicating BTF in the linker.

    c)  pahole injects additional BTF information based on specific knowledge
        of kernel objects which is not available to the compiler.

    In the scenario above, the verifier needs access to the pointer tags of
    both the kernel types/declarations (conveyed in the DWARF and translated
    to BTF by pahole) and those of the BPF program (available directly in BTF).

    Another motivation for having the tag information in DWARF, unrelated to
    BPF and BTF, is that the drgn project (another DWARF consumer) also wants
    to benefit from these tags in order to differentiate between different
    kinds of pointers in the kernel.

3)  Conveying the tags in the generated BTF debug info.

    This is easy: the main purpose of having this info in BTF is for the
    compiled BPF programs. The kernel verifier can then access the tags
    of pointers used by the BPF programs.

For more information about these tags and the motivation behind them, please
refer to the following Linux kernel discussions: [1], [2], [3].

DWARF Representation
====================

Compared to prior iterations of this work, this patch series introduces a new
DWARF representation meant to address issues in the previously proposed format.
The format is detailed below.

Note that the obvious solution of introducing a new DIE to be chained in type
chains similar to type modifiers like const and volatile is not feasible
because it would break DWARF readers.

New DWARF extension: DW_TAG_GNU_annotation.  These DIEs encode the annotation
information.  They exist near the top level of the DIE tree as children of the
compilation unit DIE.  The user-supplied annotations ("tags") are encoded via
DW_AT_name and DW_AT_const_value.  DW_AT_name holds the name of the attribute
which is the source of the annotation (currently only "btf_type_tag" or
"btf_decl_tag").  DW_AT_const_value holds the arbitrary user string from the
attribute argument.

  DW_TAG_GNU_annotation
    DW_AT_name: "btf_decl_tag" or "btf_type_tag"
    DW_AT_const_value: <arbitrary user-provided string from attribute arg>
    DW_AT_GNU_annotation: see below.

New DWARF extension: DW_AT_GNU_annotation.  If present, the
DW_AT_GNU_annotation attribute is a reference to a DW_TAG_GNU_annotation DIE
holding annotations for the object.

If a single declaration or type at the language level has multiple occurrences
of btf_decl_tag or btf_type_tag attribute, then the DW_TAG_GNU_annotation DIE
referenced by that object will itself have DW_AT_GNU_annotation referring to
another annotation DIE.  In this way the annotation DIEs are chained together.

Multiple distinct declarations or types may refer via DW_AT_GNU_annotation to
the same DW_TAG_GNU_annotation DIE, if they share the same tags.

For more information on this format, please refer to recent talks at GNU Tools
Cauldron [4] and Linux Plumbers Conference [5]. Older iterations of this work
and related discussions may be found in [6,7,8].

BTF Representation
==================

In BTF, BTF_KIND_DECL_TAG and BTF_KIND_TYPE_TAG records convey the annotations.
Both records hold the annotation value in their name field.

BTF_KIND_DECL_TAG records refer to the annotated object by BTF ID.  Each
DECL_TAG record is followed by an additional 32-bit 'component_idx', which
indicates to which component of an object the tag applies.  If the annotated
object is a struct, union, or function, then 'component_idx' is the 0-based
index of the member or function parameter to which the tag applies.   If the
annotated object is a variable, or if it is a function and the tag applies to
the function declaration itself (rather than a parameter), then 'component_idx'
is -1.

BTF_KIND_TYPE_TAG records form part of the type chain.  Currently the BTF
format can only represent type tags applied to pointer types; type tags applied
to any non-pointer type cannot be represented in BTF.  For type tags applied to
pointer types, the BTF_KIND_PTR refers to the TYPE_TAG by ID, and the TYPE_TAG
refers to the pointee type by ID.

Example: btf_decl_tag
=====================

Consider the following declarations:

  int  *x __attribute__((btf_decl_tag ("rw"), btf_decl_tag ("devicemem")));
  struct {
    int size;
    char *ptr __attribute__((btf_decl_tag("rw")));
  } y;

These declarations produce the following DWARF information:

 <1><1e>: Abbrev Number: 3 (DW_TAG_variable)
    <1f>   DW_AT_name        : x
    <24>   DW_AT_type        : <0x36>
    <28>   DW_TAG_GNU_annotation: <0x4a>
    ...
 <1><36>: Abbrev Number: 1 (DW_TAG_pointer_type)
    <37>   DW_AT_byte_size   : 8
    <37>   DW_AT_type        : <0x3b>
 <1><3b>: Abbrev Number: 4 (DW_TAG_base_type)
    <3e>   DW_AT_name        : int
    ...
 <1><42>: Abbrev Number: 5 (DW_TAG_GNU_annotation)
    <43>   DW_AT_name        : (indirect string, offset: 0): btf_decl_tag
    <47>   DW_AT_const_value : rw
 <1><4a>: Abbrev Number: 6 (DW_TAG_GNU_annotation)
    <4b>   DW_AT_name        : (indirect string, offset: 0): btf_decl_tag
    <4f>   DW_AT_const_value : (indirect string, offset: 0x1f): devicemem
    <53>   DW_AT_GNU_annotation: <0x42>
 <1><57>: Abbrev Number: 7 (DW_TAG_structure_type)
    ...
 <2><60>: Abbrev Number: 8 (DW_TAG_member)
    <61>   DW_AT_name        : (indirect string, offset: 0x1a): size
    <68>   DW_AT_type        : <0x3b>
    ...
 <2><6d>: Abbrev Number: 9 (DW_TAG_member)
    <6e>   DW_AT_name        : ptr
    <75>   DW_AT_type        : <0x7f>
    <7a>   DW_AT_GNU_annotation: <0x42>
    ...
 <2><7e>: Abbrev Number: 0
 <1><7f>: Abbrev Number: 1 (DW_TAG_pointer_type)
    <80>   DW_AT_byte_size   : 8
    <80>   DW_AT_type        : <0x84>
 <1><84>: Abbrev Number: 10 (DW_TAG_base_type)
    <85>   DW_AT_byte_size   : 1
    <86>   DW_AT_encoding    : 6	(signed char)
    <87>   DW_AT_name        : (indirect string, offset: 0x5e): char
 <1><8b>: Abbrev Number: 11 (DW_TAG_variable)
    <8c>   DW_AT_name        : y
    <91>   DW_AT_type        : <0x57>
    ...

The variable DIE for 'x' refers by DW_AT_GNU_annotation to the DIE holding the
annotation for the "devicemem" tag, which in turn refers to the DIE holding
the annotation for "rw".  The DW_TAG_member DIE for the member 'ptr' of the
struct refers to the annotation die for "rw" directly, which is thereby shared
between the two declarations.

And BTF information:

  [1] STRUCT '(anon)' size=16 vlen=2
      'size' type_id=2 bits_offset=0
      'ptr' type_id=3 bits_offset=64
  [2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
  [3] PTR '(anon)' type_id=4
  [4] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED
  [5] PTR '(anon)' type_id=2
  [6] DECL_TAG 'devicemem' type_id=10 component_idx=-1
  [7] DECL_TAG 'rw' type_id=10 component_idx=-1
  [8] DECL_TAG 'rw' type_id=1 component_idx=1
  [9] VAR 'y' type_id=1, linkage=global
  [10] VAR 'x' type_id=5, linkage=global

Note how the component_idx identifies to which member of the struct type the
decl tag is applied.


Example: btf_type_tag
=====================

Consider the following code snippet:

  int __attribute__((btf_type_tag("rcu"), btf_type_tag ("foo"))) x;

  void
  do_thing (struct S * __attribute__((btf_type_tag ("rcu"))) rcu_s,
            void * __attribute__((btf_type_tag("foo"))) ptr)
  { ... }

The relevant DWARF information produced is as follows:

 <1><2e>: Abbrev Number: 3 (DW_TAG_structure_type)
    <2f>   DW_AT_name        : S
    ...
 ...
 <1><4d>: Abbrev Number: 6 (DW_TAG_variable)
    <4e>   DW_AT_name        : x
    <53>   DW_AT_type        : <0x61>
    ...
 <1><61>: Abbrev Number: 7 (DW_TAG_base_type)
    <62>   DW_AT_byte_size   : 4
    <63>   DW_AT_encoding    : 5	(signed)
    <64>   DW_AT_name        : int
    <68>   DW_AT_GNU_annotation: <0x75>
 <1><6c>: Abbrev Number: 1 (DW_TAG_GNU_annotation)
    <6d>   DW_AT_name        : (indirect string, offset: 0x13): btf_type_tag
    <71>   DW_AT_const_value : rcu
 <1><75>: Abbrev Number: 8 (DW_TAG_GNU_annotation)
    <76>   DW_AT_name        : (indirect string, offset: 0x13): btf_type_tag
    <7a>   DW_AT_const_value : foo
    <7e>   DW_AT_GNU_annotation: <0x6c>
 <1><82>: Abbrev Number: 9 (DW_TAG_subprogram)
    <83>   DW_AT_name        : (indirect string, offset: 0x20): do_thing
    ...
 <2><a1>: Abbrev Number: 10 (DW_TAG_formal_parameter)
    <a2>   DW_AT_name        : (indirect string, offset: 0x5): rcu_s
    <a9>   DW_AT_type        : <0xc0>
    ...
 <2><b0>: Abbrev Number: 11 (DW_TAG_formal_parameter)
    <b1>   DW_AT_name        : ptr
    <b8>   DW_AT_type        : <0xca>
    ...
 <2><bf>: Abbrev Number: 0
 <1><c0>: Abbrev Number: 12 (DW_TAG_pointer_type)
    <c1>   DW_AT_byte_size   : 8
    <c2>   DW_AT_type        : <0x2e>
    <c6>   DW_AT_GNU_annotation: <0x6c>
 <1><ca>: Abbrev Number: 13 (DW_TAG_pointer_type)
    <cb>   DW_AT_byte_size   : 8
    <cc>   DW_AT_GNU_annotation: <0xd0>
 <1><d0>: Abbrev Number: 1 (DW_TAG_GNU_annotation)
    <d1>   DW_AT_name        : (indirect string, offset: 0x13): btf_type_tag
    <d5>   DW_AT_const_value : foo

Note how in this case, two annotation DIEs for "foo" are produced, because
it is used in two distinct sets of type tags which do not allow it to be
shared. The DIE for "rcu", however, is shared between uses.

And BTF information:

  [1] STRUCT 'S' size=8 vlen=1
      ...
  [2] INT 'long int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED
  [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
  [4] FUNC_PROTO '(anon)' ret_type_id=0 vlen=2
      'rcu_s' type_id=5
      'ptr' type_id=6
  [5] PTR '(anon)' type_id=9
  [6] PTR '(anon)' type_id=10
  [7] VAR 'x' type_id=3, linkage=global
  [8] FUNC 'do_thing' type_id=4 linkage=global
  [9] TYPE_TAG 'rcu' type_id=1
  [10] TYPE_TAG 'foo' type_id=0

Note how the TYPE_TAG are injected into the type chain between the PTR
record and the pointee type, e.g.

  param 'rcu_s' -> PTR -> TYPE_TAG 'rcu' -> STRUCT 'S'

Note also that the type tags which apply to the integer type of variable 'x'
are not represented, since BTF currently has no way to represent type tags
on non-pointer types.

References
==========

[1] https://lore.kernel.org/bpf/20210914223004.244411-1-yhs@fb.com/
[2] https://lore.kernel.org/bpf/20211012164838.3345699-1-yhs@fb.com/
[3] https://lore.kernel.org/bpf/20211112012604.1504583-1-yhs@fb.com/
[4] https://gcc.gnu.org/wiki/cauldron2024#cauldron2024talks.what_is_new_in_the_bpf_support_in_the_gnu_toolchain
[5] https://lpc.events/event/18/contributions/1924/
[6] https://gcc.gnu.org/pipermail/gcc-patches/2022-April/592685.html
[7] https://gcc.gnu.org/pipermail/gcc-patches/2022-June/596355.html
[8] https://gcc.gnu.org/pipermail/gcc-patches/2023-July/624156.html


David Faust (6):
  c-family: add btf_type_tag and btf_decl_tag attributes
  dwarf: create annotation DIEs for btf tags
  ctf: translate annotation DIEs to internal ctf
  btf: generate and output DECL_TAG and TYPE_TAG records
  doc: document btf_type_tag and btf_decl_tag attributes
  bpf: add tests for CO-RE and BTF tag interaction

 gcc/btfout.cc                                 | 171 +++++++--
 gcc/c-family/c-attribs.cc                     | 110 +++++-
 gcc/ctfc.cc                                   |  80 ++++-
 gcc/ctfc.h                                    |  43 ++-
 gcc/doc/extend.texi                           |  79 +++++
 gcc/dwarf2ctf.cc                              | 135 ++++++-
 gcc/dwarf2out.cc                              | 328 ++++++++++++++++--
 gcc/testsuite/gcc.dg/attr-btf-decl-tag-1.c    |  14 +
 gcc/testsuite/gcc.dg/attr-btf-decl-tag-2.c    |  15 +
 gcc/testsuite/gcc.dg/attr-btf-type-tag-1.c    |  12 +
 gcc/testsuite/gcc.dg/attr-btf-type-tag-2.c    |   9 +
 gcc/testsuite/gcc.dg/attr-btf-type-tag-3.c    |   8 +
 .../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         |  26 ++
 .../gcc.dg/debug/btf/btf-type-tag-2.c         |  13 +
 .../gcc.dg/debug/btf/btf-type-tag-3.c         |  28 ++
 .../gcc.dg/debug/btf/btf-type-tag-4.c         |  24 ++
 .../gcc.dg/debug/btf/btf-type-tag-c2x-1.c     |  22 ++
 .../gcc.dg/debug/ctf/ctf-decl-tag-1.c         |  31 ++
 .../gcc.dg/debug/ctf/ctf-type-tag-1.c         |  19 +
 .../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      |  20 ++
 .../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 +++
 gcc/testsuite/gcc.target/bpf/core-btf-tag-1.c |  23 ++
 gcc/testsuite/gcc.target/bpf/core-btf-tag-2.c |  23 ++
 include/btf.h                                 |  14 +
 include/ctf.h                                 |   4 +
 include/dwarf2.def                            |   4 +
 41 files changed, 1555 insertions(+), 65 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/attr-btf-decl-tag-1.c
 create mode 100644 gcc/testsuite/gcc.dg/attr-btf-decl-tag-2.c
 create mode 100644 gcc/testsuite/gcc.dg/attr-btf-type-tag-1.c
 create mode 100644 gcc/testsuite/gcc.dg/attr-btf-type-tag-2.c
 create mode 100644 gcc/testsuite/gcc.dg/attr-btf-type-tag-3.c
 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-c2x-1.c
 create mode 100644 gcc/testsuite/gcc.dg/debug/ctf/ctf-decl-tag-1.c
 create mode 100644 gcc/testsuite/gcc.dg/debug/ctf/ctf-type-tag-1.c
 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
 create mode 100644 gcc/testsuite/gcc.target/bpf/core-btf-tag-1.c
 create mode 100644 gcc/testsuite/gcc.target/bpf/core-btf-tag-2.c

-- 
2.47.3


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v11 1/6] c-family: add btf_type_tag and btf_decl_tag attributes
  2025-09-27  9:06 [PATCH v11 0/6] c,dwarf,btf: Add btf_decl_tag and btf_type_tag C attributes David Faust
@ 2025-09-27  9:06 ` David Faust
  2025-09-27  9:06 ` [PATCH v11 2/6] dwarf: create annotation DIEs for btf tags David Faust
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: David Faust @ 2025-09-27  9:06 UTC (permalink / raw)
  To: gcc-patches
  Cc: josmyers, rguenther, jose.marchesi, indu.bhagat, yonghong.song

Add two new c-family attributes, "btf_type_tag" and "btf_decl_tag"
along with attribute handlers for them.  These attributes may be
used to annotate types or declarations respectively with arbitrary
strings, which will be recorded in DWARF and/or BTF information.
Both attributes accept exactly one string argument.  Wide strings
are not supported.

gcc/c-family/
	* c-attribs.cc (c_common_attribute_table): Add btf_decl_tag and
	btf_type_tag attributes.
	(handle_btf_decl_tag_attribute): New handler for btf_decl_tag.
	(hanlde_btf_type_tag_attribute): New handler for btf_type_tag.
	(btf_tag_args_ok): Helper for new attribute handlers.

gcc/testsuite/
	* gcc.dg/attr-btf-decl-tag-1.c: New test.
	* gcc.dg/attr-btf-decl-tag-2.c: New test.
	* gcc.dg/attr-btf-type-tag-1.c: New test.
	* gcc.dg/attr-btf-type-tag-2.c: New test.
	* gcc.dg/attr-btf-type-tag-3.c: New test.
---
 gcc/c-family/c-attribs.cc                  | 110 ++++++++++++++++++++-
 gcc/testsuite/gcc.dg/attr-btf-decl-tag-1.c |  14 +++
 gcc/testsuite/gcc.dg/attr-btf-decl-tag-2.c |  15 +++
 gcc/testsuite/gcc.dg/attr-btf-type-tag-1.c |  12 +++
 gcc/testsuite/gcc.dg/attr-btf-type-tag-2.c |   9 ++
 gcc/testsuite/gcc.dg/attr-btf-type-tag-3.c |   8 ++
 6 files changed, 167 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.dg/attr-btf-decl-tag-1.c
 create mode 100644 gcc/testsuite/gcc.dg/attr-btf-decl-tag-2.c
 create mode 100644 gcc/testsuite/gcc.dg/attr-btf-type-tag-1.c
 create mode 100644 gcc/testsuite/gcc.dg/attr-btf-type-tag-2.c
 create mode 100644 gcc/testsuite/gcc.dg/attr-btf-type-tag-3.c

diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index 1e3a94ed949..a7d9656c2bd 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -189,6 +189,9 @@ static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *);
 static tree handle_flag_enum_attribute (tree *, tree, tree, int, bool *);
 static tree handle_null_terminated_string_arg_attribute (tree *, tree, tree, int, bool *);
 
+static tree handle_btf_decl_tag_attribute (tree *, tree, tree, int, bool *);
+static tree handle_btf_type_tag_attribute (tree *, tree, tree, int, bool *);
+
 /* Helper to define attribute exclusions.  */
 #define ATTR_EXCL(name, function, type, variable)	\
   { name, function, type, variable }
@@ -640,7 +643,11 @@ const struct attribute_spec c_common_gnu_attributes[] =
   { "flag_enum",	      0, 0, false, true, false, false,
 			      handle_flag_enum_attribute, NULL },
   { "null_terminated_string_arg", 1, 1, false, true, true, false,
-			      handle_null_terminated_string_arg_attribute, NULL}
+			      handle_null_terminated_string_arg_attribute, NULL},
+  { "btf_type_tag",	      1, 1, false, true, false, false,
+			      handle_btf_type_tag_attribute, NULL},
+  { "btf_decl_tag",	      1, 1, true, false, false, false,
+			      handle_btf_decl_tag_attribute, NULL}
 };
 
 const struct scoped_attribute_specs c_common_gnu_attribute_table =
@@ -5155,6 +5162,107 @@ handle_null_terminated_string_arg_attribute (tree *node, tree name, tree args,
   return NULL_TREE;
 }
 
+/* Common argument checking for btf_type_tag and btf_decl_tag.
+   Return true if the ARGS are valid, otherwise emit an error and
+   return false.  */
+
+static bool
+btf_tag_args_ok (tree name, tree args)
+{
+  if (!args) /* Correct number of args (1) is checked for us.  */
+    return false;
+  else if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+    {
+      error ("%qE attribute requires a string argument", name);
+      return false;
+    }
+
+  /* Only narrow character strings are accepted.  */
+  tree argtype = TREE_TYPE (TREE_TYPE (TREE_VALUE (args)));
+  if (!(argtype == char_type_node
+	|| argtype == char8_type_node
+	|| argtype == signed_char_type_node
+	|| argtype == unsigned_char_type_node))
+    {
+      error ("unsupported wide string type argument in %qE attribute", name);
+      return false;
+    }
+
+  return true;
+}
+
+/* Handle the "btf_decl_tag" attribute.  */
+
+static tree
+handle_btf_decl_tag_attribute (tree * ARG_UNUSED (node), tree name, tree args,
+			       int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  if (!btf_tag_args_ok (name, args))
+    *no_add_attrs = true;
+
+  return NULL_TREE;
+}
+
+/* Handle the "btf_type_tag" attribute.  */
+
+static tree
+handle_btf_type_tag_attribute (tree *node, tree name, tree args,
+			       int flags, bool *no_add_attrs)
+{
+  if (!btf_tag_args_ok (name, args))
+    {
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
+  if (TREE_CODE (*node) == FUNCTION_TYPE || TREE_CODE (*node) == METHOD_TYPE)
+    {
+      warning (OPT_Wattributes,
+	       "%qE attribute does not apply to functions", name);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
+  /* Ensure a variant type is always created to hold the type_tag,
+     unless ATTR_FLAG_IN_PLACE is set.  Same logic as in
+     common_handle_aligned_attribute.  */
+  tree decl = NULL_TREE;
+  tree *type = NULL;
+  bool is_type = false;
+
+  if (DECL_P (*node))
+    {
+      decl = *node;
+      type = &TREE_TYPE (decl);
+      is_type = TREE_CODE (*node) == TYPE_DECL;
+    }
+  else if (TYPE_P (*node))
+    type = node, is_type = true;
+
+  if (is_type)
+    {
+      if ((flags & (int) ATTR_FLAG_TYPE_IN_PLACE))
+	/* OK, modify the type in place.  */;
+
+      /* If we have a TYPE_DECL, then copy the type, so that we
+	 don't accidentally modify a builtin type.  See pushdecl.  */
+      else if (decl && TREE_TYPE (decl) != error_mark_node
+	       && DECL_ORIGINAL_TYPE (decl) == NULL_TREE)
+	{
+	  tree tt = TREE_TYPE (decl);
+	  *type = build_variant_type_copy (*type);
+	  DECL_ORIGINAL_TYPE (decl) = tt;
+	  TYPE_NAME (*type) = decl;
+	  TREE_USED (*type) = TREE_USED (decl);
+	  TREE_TYPE (decl) = *type;
+	}
+      else
+	*type = build_variant_type_copy (*type);
+    }
+
+  return NULL_TREE;
+}
+
 /* Handle the "nonstring" variable attribute.  */
 
 static tree
diff --git a/gcc/testsuite/gcc.dg/attr-btf-decl-tag-1.c b/gcc/testsuite/gcc.dg/attr-btf-decl-tag-1.c
new file mode 100644
index 00000000000..d26d992c44d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-btf-decl-tag-1.c
@@ -0,0 +1,14 @@
+/* Test btf_decl_tag attribute argument checking.  */
+/* { dg-do compile } */
+
+void *vptr __attribute__((btf_decl_tag("vptr"), btf_decl_tag ("perthread")));
+
+struct Foo
+{
+  int x __attribute__((btf_decl_tag (0x55))); /* { dg-error "requires a string" } */
+  char *c __attribute__((btf_decl_tag (L"Lstr"))); /* { dg-error "unsupported wide string" } */
+};
+
+extern int foo (int x, int y __attribute__((btf_decl_tag))); /* { dg-error "wrong number of arguments" } */
+
+char *str __attribute__((btf_decl_tag("A", "B"))); /* { dg-error "wrong number of arguments" } */
diff --git a/gcc/testsuite/gcc.dg/attr-btf-decl-tag-2.c b/gcc/testsuite/gcc.dg/attr-btf-decl-tag-2.c
new file mode 100644
index 00000000000..956e20eb2ef
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-btf-decl-tag-2.c
@@ -0,0 +1,15 @@
+/* Test btf_decl_tag attribute argument checking for wide string types.  */
+/* { dg-do compile } */
+/* { dg-options "--std=c11" } */
+
+int **my_ptr __attribute__((btf_decl_tag("my_ptr")));
+
+void *x __attribute__((btf_decl_tag (U"Ustr"))); /* { dg-error "unsupported wide string" } */
+
+const int y __attribute__((btf_decl_tag (u"ustr"))); /* { dg-error "unsupported wide string" } */
+
+union U
+{
+  int x;
+  char c __attribute__((btf_decl_tag (u8"u8str"))); /* OK.  */
+};
diff --git a/gcc/testsuite/gcc.dg/attr-btf-type-tag-1.c b/gcc/testsuite/gcc.dg/attr-btf-type-tag-1.c
new file mode 100644
index 00000000000..2ed54ae5be3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-btf-type-tag-1.c
@@ -0,0 +1,12 @@
+/* Test btf_type_tag attribute argument checking.  */
+/* { dg-do compile } */
+
+void * __attribute__((btf_type_tag ("A"), btf_type_tag ("vptr"))) a;
+
+int __attribute__((btf_type_tag (5))) b; /* { dg-error "requires a string" } */
+
+char * __attribute__((btf_type_tag (L"Lstr"))) c; /* { dg-error "unsupported wide string" } */
+
+int * __attribute__((btf_type_tag)) d; /* { dg-error "wrong number of arguments" } */
+
+char * __attribute__((btf_type_tag ("A", "B"))) e; /* { dg-error "wrong number of arguments" } */
diff --git a/gcc/testsuite/gcc.dg/attr-btf-type-tag-2.c b/gcc/testsuite/gcc.dg/attr-btf-type-tag-2.c
new file mode 100644
index 00000000000..edc22b11ef8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-btf-type-tag-2.c
@@ -0,0 +1,9 @@
+/* Test btf_type_tag attribute argument checking for wide string types.  */
+/* { dg-do compile } */
+/* { dg-options "--std=c11" } */
+
+int __attribute__((btf_type_tag (U"Ustr"))) x; /* { dg-error "unsupported wide string" } */
+
+int __attribute__((btf_type_tag (u"ustr"))) y; /* { dg-error "unsupported wide string" } */
+
+int __attribute__((btf_type_tag (u8"u8str"))) z; /* OK.  */
diff --git a/gcc/testsuite/gcc.dg/attr-btf-type-tag-3.c b/gcc/testsuite/gcc.dg/attr-btf-type-tag-3.c
new file mode 100644
index 00000000000..afb14b12a22
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/attr-btf-type-tag-3.c
@@ -0,0 +1,8 @@
+/* Test btf_type_tag attribute warnings.  */
+/* { dg-do compile } */
+
+int __attribute__((btf_type_tag ("A"))) a (int x); /* { dg-warning "does not apply to functions" } */
+
+__attribute__((btf_type_tag ("B"))) int *b (int y); /* { dg-warning "does not apply to functions" } */
+
+int *c (int z) __attribute__((btf_type_tag ("C"))); /* { dg-warning "does not apply to functions" } */
-- 
2.47.3


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v11 2/6] dwarf: create annotation DIEs for btf tags
  2025-09-27  9:06 [PATCH v11 0/6] c,dwarf,btf: Add btf_decl_tag and btf_type_tag C attributes David Faust
  2025-09-27  9:06 ` [PATCH v11 1/6] c-family: add btf_type_tag and btf_decl_tag attributes David Faust
@ 2025-09-27  9:06 ` David Faust
  2025-10-16 16:05   ` Tom Tromey
  2025-09-27  9:06 ` [PATCH v11 3/6] ctf: translate annotation DIEs to internal ctf David Faust
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 11+ messages in thread
From: David Faust @ 2025-09-27  9:06 UTC (permalink / raw)
  To: gcc-patches
  Cc: josmyers, rguenther, jose.marchesi, indu.bhagat, yonghong.song

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, 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      |  20 ++
 .../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, 598 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 0bd8474bc37..a817c69c95a 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);
@@ -13675,13 +13702,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);
@@ -13690,6 +13903,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.  */
@@ -13709,7 +13923,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;
@@ -13786,8 +14001,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);
@@ -13799,10 +14014,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.  */
 	}
     }
@@ -13836,7 +14077,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
@@ -13903,6 +14145,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;
@@ -13967,9 +14219,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);
@@ -13987,8 +14242,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
@@ -14001,8 +14256,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));
@@ -14092,8 +14347,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);
@@ -15237,8 +15492,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;
@@ -22508,6 +22763,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);
 
@@ -22786,6 +23042,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
@@ -23156,6 +23413,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;
 	    }
 	}
@@ -23224,6 +23482,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;
 }
 
@@ -24577,10 +24837,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);
 
@@ -26448,6 +26710,10 @@ gen_tagged_type_die (tree type,
   else
     gen_struct_or_union_type_die (type, context_die, usage);
 
+  dw_die_ref die = lookup_type_die (type);
+  if (die)
+    maybe_gen_btf_type_tag_dies (type, die);
+
   /* 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.  */
@@ -27081,6 +27347,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);
     }
@@ -27458,6 +27725,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;
 }
 \f
@@ -32504,6 +32774,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 ());
 
@@ -33574,6 +33847,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..3ecd79f092f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-10.c
@@ -0,0 +1,20 @@
+/* Test btf_type_tag and btf_decl_tag on a function.
+   Here the decl_tag applies to the function, and the type_tag applies
+   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)
+{
+  if (x && y && *x > *y)
+    return x;
+  return 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
+   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..e5db652fd40
--- /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 "-std=c23 -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.3


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v11 3/6] ctf: translate annotation DIEs to internal ctf
  2025-09-27  9:06 [PATCH v11 0/6] c,dwarf,btf: Add btf_decl_tag and btf_type_tag C attributes David Faust
  2025-09-27  9:06 ` [PATCH v11 1/6] c-family: add btf_type_tag and btf_decl_tag attributes David Faust
  2025-09-27  9:06 ` [PATCH v11 2/6] dwarf: create annotation DIEs for btf tags David Faust
@ 2025-09-27  9:06 ` David Faust
  2025-09-27  9:06 ` [PATCH v11 4/6] btf: generate and output DECL_TAG and TYPE_TAG records David Faust
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: David Faust @ 2025-09-27  9:06 UTC (permalink / raw)
  To: gcc-patches
  Cc: josmyers, rguenther, jose.marchesi, indu.bhagat, yonghong.song

Translate DW_TAG_GNU_annotation DIEs created for C attributes
btf_decl_tag and btf_type_tag into an in-memory representation in the
CTF/BTF container.  They will be output in BTF as BTF_KIND_DECL_TAG and
BTF_KIND_TYPE_TAG records.

The new CTF kinds used to represent these annotations, CTF_K_DECL_TAG
and CTF_K_TYPE_TAG, are expected to be formalized in the next version of
the CTF specification.  For now they only exist in memory as a
translation step to BTF, and are not emitted when generating CTF
information.

gcc/
	* ctfc.cc (ctf_dtu_d_union_selector): Handle CTF_K_DECL_TAG and
	CTF_K_TYPE_TAG.
	(ctf_add_type_tag, ctf_add_decl_tag): New.
	(ctf_add_variable): Return the new ctf_dvdef_ref rather than zero.
	(new_ctf_container): Initialize new members.
	(ctfc_delete_container): Deallocate new members.
	* ctfc.h (ctf_dvdef, ctf_dvdef_t, ctf_dvdef_ref): Move forward
	declarations earlier in file.
	(ctf_decl_tag_t): New typedef.
	(ctf_dtdef): Add ctf_decl_tag_t member to dtd_u union.
	(ctf_dtu_d_union_enum): Add new CTF_DTU_D_TAG enumerator.
	(ctf_container): Add ctfc_tags vector and ctfc_type_tags_map hash_map
	members.
	(ctf_add_type_tag, ctf_add_decl_tag): New function protos.
	(ctf_add_variable): Change prototype return type to ctf_dvdef_ref.
	* dwarf2ctf.cc (gen_ctf_type_tags, gen_ctf_decl_tags)
	(gen_ctf_decl_tags_for_var): New static functions.
	(gen_ctf_pointer_type): Handle type tags.
	(gen_ctf_sou_type): Handle decl tags.
	(gen_ctf_function_type): Likewise.
	(gen_ctf_variable): Likewise.
	(gen_ctf_function): Likewise.
	(gen_ctf_type): Handle TAG_GNU_annotation DIEs.

gcc/testsuite/
	* gcc.dg/debug/ctf/ctf-decl-tag-1.c: New test.
	* gcc.dg/debug/ctf/ctf-type-tag-1.c: New test.

include/
	* ctf.h (CTF_K_DECL_TAG, CTF_K_TYPE_TAG): New defines.
---
 gcc/ctfc.cc                                   |  80 ++++++++++-
 gcc/ctfc.h                                    |  43 +++++-
 gcc/dwarf2ctf.cc                              | 135 +++++++++++++++++-
 .../gcc.dg/debug/ctf/ctf-decl-tag-1.c         |  31 ++++
 .../gcc.dg/debug/ctf/ctf-type-tag-1.c         |  19 +++
 include/ctf.h                                 |   4 +
 6 files changed, 299 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/debug/ctf/ctf-decl-tag-1.c
 create mode 100644 gcc/testsuite/gcc.dg/debug/ctf/ctf-type-tag-1.c

diff --git a/gcc/ctfc.cc b/gcc/ctfc.cc
index 221e62e8f45..3fae414bf53 100644
--- a/gcc/ctfc.cc
+++ b/gcc/ctfc.cc
@@ -107,6 +107,9 @@ ctf_dtu_d_union_selector (ctf_dtdef_ref ctftype)
       return CTF_DTU_D_ARGUMENTS;
     case CTF_K_SLICE:
       return CTF_DTU_D_SLICE;
+    case CTF_K_DECL_TAG:
+    case CTF_K_TYPE_TAG:
+      return CTF_DTU_D_TAG;
     default:
       /* The largest member as default.  */
       return CTF_DTU_D_ARRAY;
@@ -445,6 +448,68 @@ ctf_add_reftype (ctf_container_ref ctfc, uint32_t flag, ctf_dtdef_ref ref,
   return dtd;
 }
 
+ctf_dtdef_ref
+ctf_add_type_tag (ctf_container_ref ctfc, uint32_t flag, const char *value,
+		  ctf_dtdef_ref ref_dtd)
+{
+  ctf_dtdef_ref dtd;
+   /* Create a DTD for the tag, but do not place it in the regular types list;
+      CTF format does not (yet) encode tags.  */
+  dtd = ggc_cleared_alloc<ctf_dtdef_t> ();
+
+  dtd->dtd_name = ctf_add_string (ctfc, value, &(dtd->dtd_data.ctti_name),
+				  CTF_AUX_STRTAB);
+  /* A single DW_TAG_GNU_annotation DIE may be referenced by multiple DIEs,
+     e.g. when multiple distinct types specify the same type tag.  We will
+     synthesize multiple CTF DTD records in that case, so we cannot tie them
+     all to the same key (the DW_TAG_GNU_annotation DIE) in ctfc_types.  */
+  dtd->dtd_key = NULL;
+  dtd->ref_type = ref_dtd;
+  dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_TYPE_TAG, flag, 0);
+  dtd->dtd_u.dtu_tag.ref_var = NULL; /* Not used for type tags.  */
+  dtd->dtd_u.dtu_tag.component_idx = 0; /* Not used for type tags.  */
+
+  /* Insert tag directly into the tag list.  Type ID will be assigned later.  */
+  vec_safe_push (ctfc->ctfc_tags, dtd);
+
+  /* Keep ctfc_aux_strlen updated.  */
+  if ((value != NULL) && strcmp (value, ""))
+    ctfc->ctfc_aux_strlen += strlen (value) + 1;
+
+  return dtd;
+}
+
+ctf_dtdef_ref
+ctf_add_decl_tag (ctf_container_ref ctfc, uint32_t flag, const char *value,
+		  ctf_dtdef_ref ref_dtd, uint32_t comp_idx)
+{
+   ctf_dtdef_ref dtd;
+   /* Create a DTD for the tag, but do not place it in the regular types list;
+      ctf format does not (yet) encode tags.  */
+  dtd = ggc_cleared_alloc<ctf_dtdef_t> ();
+
+  dtd->dtd_name = ctf_add_string (ctfc, value, &(dtd->dtd_data.ctti_name),
+				  CTF_AUX_STRTAB);
+  /* A single DW_TAG_GNU_annotation DIE may be referenced by multiple DIEs,
+     e.g. when multiple distinct declarations specify the same decl tag.
+     We will synthesize multiple CTF DTD records in that case, so we cannot tie
+     them all to the same key (the DW_TAG_GNU_annotation DIE) in ctfc_types.  */
+  dtd->dtd_key = NULL;
+  dtd->ref_type = ref_dtd;
+  dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_DECL_TAG, flag, 0);
+  dtd->dtd_u.dtu_tag.ref_var = NULL;
+  dtd->dtd_u.dtu_tag.component_idx = comp_idx;
+
+  /* Insert tag directly into the tag list.  Type ID will be assigned later.  */
+  vec_safe_push (ctfc->ctfc_tags, dtd);
+
+  /* Keep ctfc_aux_strlen updated.  */
+  if ((value != NULL) && strcmp (value, ""))
+    ctfc->ctfc_aux_strlen += strlen (value) + 1;
+
+  return dtd;
+}
+
 ctf_dtdef_ref
 ctf_add_forward (ctf_container_ref ctfc, uint32_t flag, const char * name,
 		 uint32_t kind, dw_die_ref die)
@@ -691,12 +756,12 @@ ctf_add_member_offset (ctf_container_ref ctfc, dw_die_ref sou,
   return 0;
 }
 
-int
+ctf_dvdef_ref
 ctf_add_variable (ctf_container_ref ctfc, const char * name, ctf_dtdef_ref ref,
 		  dw_die_ref die, unsigned int external_vis,
 		  dw_die_ref die_var_decl)
 {
-  ctf_dvdef_ref dvd, dvd_ignore;
+  ctf_dvdef_ref dvd = NULL, dvd_ignore;
 
   gcc_assert (name);
 
@@ -732,7 +797,7 @@ ctf_add_variable (ctf_container_ref ctfc, const char * name, ctf_dtdef_ref ref,
 	ctfc->ctfc_strlen += strlen (name) + 1;
     }
 
-  return 0;
+  return dvd;
 }
 
 int
@@ -949,6 +1014,10 @@ new_ctf_container (void)
   tu_ctfc->ctfc_ignore_vars
     = hash_table<ctfc_dvd_hasher>::create_ggc (10);
 
+  vec_alloc (tu_ctfc->ctfc_tags, 100);
+  tu_ctfc->ctfc_type_tags_map
+    = hash_map<ctf_dtdef_ref, ctf_dtdef_ref>::create_ggc (100);
+
   return tu_ctfc;
 }
 
@@ -1003,6 +1072,11 @@ ctfc_delete_container (ctf_container_ref ctfc)
       ctfc->ctfc_ignore_vars->empty ();
       ctfc->ctfc_ignore_vars = NULL;
 
+      ctfc->ctfc_tags = NULL;
+
+      ctfc->ctfc_type_tags_map->empty ();
+      ctfc->ctfc_type_tags_map = NULL;
+
       ctfc_delete_strtab (&ctfc->ctfc_strtable);
       ctfc_delete_strtab (&ctfc->ctfc_aux_strtable);
       if (ctfc->ctfc_vars_list)
diff --git a/gcc/ctfc.h b/gcc/ctfc.h
index 26f35f0ac6f..a195804daa5 100644
--- a/gcc/ctfc.h
+++ b/gcc/ctfc.h
@@ -52,6 +52,10 @@ struct ctf_dtdef;
 typedef struct ctf_dtdef ctf_dtdef_t;
 typedef ctf_dtdef_t * ctf_dtdef_ref;
 
+struct ctf_dvdef;
+typedef struct ctf_dvdef ctf_dvdef_t;
+typedef ctf_dvdef_t * ctf_dvdef_ref;
+
 /* CTF string table element (list node).  */
 
 typedef struct GTY ((chain_next ("%h.cts_next"))) ctf_string
@@ -155,6 +159,16 @@ typedef struct GTY (()) ctf_func_arg
 
 #define ctf_farg_list_next(elem) ((ctf_func_arg_t *)((elem)->farg_next))
 
+/* Declaration Tag.
+   May be used to annotate struct/union members, variable declarations,
+   function declarations, and function arguments.  */
+
+typedef struct GTY (()) ctf_decl_tag
+{
+  uint32_t component_idx;  /* Index of component to which tag applies.  */
+  ctf_dvdef_ref ref_var;   /* Non-null iff this tag applies to a variable.  */
+} ctf_decl_tag_t;
+
 /* Type definition for CTF generation.  */
 
 struct GTY ((for_user)) ctf_dtdef
@@ -184,6 +198,8 @@ struct GTY ((for_user)) ctf_dtdef
     ctf_func_arg_t * GTY ((tag ("CTF_DTU_D_ARGUMENTS"))) dtu_argv;
     /* slice.  */
     ctf_sliceinfo_t GTY ((tag ("CTF_DTU_D_SLICE"))) dtu_slice;
+    /* decl tag.  */
+    ctf_decl_tag_t GTY ((tag ("CTF_DTU_D_TAG"))) dtu_tag;
   } dtd_u;
 };
 
@@ -201,9 +217,6 @@ struct GTY ((for_user)) ctf_dvdef
   ctf_id_t dvd_id;		/* ID of this variable.  Only used for BTF.  */
 };
 
-typedef struct ctf_dvdef ctf_dvdef_t;
-typedef ctf_dvdef_t * ctf_dvdef_ref;
-
 /* Location information for CTF Types and CTF Variables.  */
 
 typedef struct GTY (()) ctf_srcloc
@@ -222,7 +235,8 @@ enum ctf_dtu_d_union_enum {
   CTF_DTU_D_ARRAY,
   CTF_DTU_D_ENCODING,
   CTF_DTU_D_ARGUMENTS,
-  CTF_DTU_D_SLICE
+  CTF_DTU_D_SLICE,
+  CTF_DTU_D_TAG,
 };
 
 enum ctf_dtu_d_union_enum
@@ -287,6 +301,17 @@ typedef struct GTY (()) ctf_container
   /* CTF variables to be ignored.  */
   hash_table <ctfc_dvd_hasher> * GTY (()) ctfc_ignore_vars;
 
+  /* BTF type and decl tags.  Not yet represented in CTF.  These tags also
+     uniquely have a one-to-many relation with DIEs, meaning a single DIE
+     may translate to multiple CTF/BTF records.  For both of these reasons,
+     they cannot be stored in the regular types table.  */
+  vec <ctf_dtdef_ref, va_gc> * GTY (()) ctfc_tags;
+  /* Type tags logically form part of the type chain similar to cv-quals.
+     Therefore references to types need to know if the referred-to type has
+     any type tags, and if so to refer to the outermost type tag.  This map
+     maps a type to the outermost type tag created for it, if any.  */
+  hash_map <ctf_dtdef_ref, ctf_dtdef_ref> * GTY (()) ctfc_type_tags_map;
+
   /* CTF string table.  */
   ctf_strtable_t ctfc_strtable;
   /* Auxilliary string table.  At this time, used for keeping func arg names
@@ -440,15 +465,19 @@ extern ctf_dtdef_ref ctf_add_function (ctf_container_ref, uint32_t,
 				       dw_die_ref, bool, int);
 extern ctf_dtdef_ref ctf_add_sou (ctf_container_ref, uint32_t, const char *,
 				  uint32_t, unsigned HOST_WIDE_INT, dw_die_ref);
-
+extern ctf_dtdef_ref ctf_add_type_tag (ctf_container_ref, uint32_t,
+				       const char *, ctf_dtdef_ref);
+extern ctf_dtdef_ref ctf_add_decl_tag (ctf_container_ref, uint32_t,
+				       const char *, ctf_dtdef_ref, uint32_t);
 extern int ctf_add_enumerator (ctf_container_ref, ctf_dtdef_ref, const char *,
 			       HOST_WIDE_INT, dw_die_ref);
 extern int ctf_add_member_offset (ctf_container_ref, dw_die_ref, const char *,
 				  ctf_dtdef_ref, uint64_t);
 extern int ctf_add_function_arg (ctf_container_ref, dw_die_ref,
 				 const char *, ctf_dtdef_ref);
-extern int ctf_add_variable (ctf_container_ref, const char *, ctf_dtdef_ref,
-			     dw_die_ref, unsigned int, dw_die_ref);
+extern ctf_dvdef_ref ctf_add_variable (ctf_container_ref, const char *,
+				       ctf_dtdef_ref, dw_die_ref, unsigned int,
+				       dw_die_ref);
 
 extern ctf_dtdef_ref ctf_lookup_tree_type (ctf_container_ref, const tree);
 
diff --git a/gcc/dwarf2ctf.cc b/gcc/dwarf2ctf.cc
index 4b49b23f078..2ebcf2af40d 100644
--- a/gcc/dwarf2ctf.cc
+++ b/gcc/dwarf2ctf.cc
@@ -32,6 +32,17 @@ along with GCC; see the file COPYING3.  If not see
 static ctf_dtdef_ref
 gen_ctf_type (ctf_container_ref, dw_die_ref);
 
+static void
+gen_ctf_decl_tags (ctf_container_ref, dw_die_ref, ctf_dtdef_ref, uint32_t);
+
+static void
+gen_ctf_decl_tags_for_var (ctf_container_ref, dw_die_ref, ctf_dvdef_ref);
+
+static ctf_dtdef_ref
+handle_ctf_type_tags (ctf_container_ref ctc, dw_die_ref annot_die,
+		      ctf_dtdef_ref ref_dtd);
+
+
 /* All the DIE structures we handle come from the DWARF information
    generated by GCC.  However, there are three situations where we need
    to create our own created DIE structures because GCC doesn't
@@ -335,6 +346,17 @@ gen_ctf_pointer_type (ctf_container_ref ctfc, dw_die_ref ptr_type)
 
   pointed_dtd = gen_ctf_type (ctfc, pointed_type_die);
 
+  /* Handle BTF type tags.  */
+  if (btf_debuginfo_p ())
+    {
+      dw_die_ref annot_die = get_AT_ref (ptr_type, DW_AT_GNU_annotation);
+      ctf_dtdef_ref tag_dtd
+	= handle_ctf_type_tags (ctfc, annot_die, pointed_dtd);
+
+      if (tag_dtd)
+	pointed_dtd = tag_dtd;
+  }
+
   /* Type de-duplication.
      Consult the ctfc_types hash again before adding the CTF pointer type
      because there can be cases where a pointer type may have been added by
@@ -536,6 +558,7 @@ gen_ctf_sou_type (ctf_container_ref ctfc, dw_die_ref sou, uint32_t kind)
   /* Now process the struct members.  */
   {
     dw_die_ref c;
+    uint32_t idx = 0;
 
     c = dw_get_die_child (sou);
     if (c)
@@ -620,6 +643,9 @@ gen_ctf_sou_type (ctf_container_ref ctfc, dw_die_ref sou, uint32_t kind)
 				 field_name,
 				 field_dtd,
 				 field_location);
+
+	  gen_ctf_decl_tags (ctfc, c, sou_dtd, idx);
+	  idx++;
 	}
       while (c != dw_get_die_child (sou));
   }
@@ -703,14 +729,18 @@ gen_ctf_function_type (ctf_container_ref ctfc, dw_die_ref function,
 	      gcc_assert (i == num_args - 1);
 	      /* Add an argument with type 0 and no name.  */
 	      ctf_add_function_arg (ctfc, function, "", NULL);
+	      /* Handle any declaration tags on the argument.  */
+	      gen_ctf_decl_tags (ctfc, c, function_dtd, i);
 	    }
 	  else if (dw_get_die_tag (c) == DW_TAG_formal_parameter)
 	    {
-	      i++;
 	      arg_name = get_AT_string (c, DW_AT_name);
 	      arg_type = gen_ctf_type (ctfc, ctf_get_AT_type (c));
 	      /* Add the argument to the existing CTF function type.  */
 	      ctf_add_function_arg (ctfc, function, arg_name, arg_type);
+	      /* Handle any declaration tags on the argument.  */
+	      gen_ctf_decl_tags (ctfc, c, function_dtd, i);
+	      i++;
 	    }
 	  else
 	    /* This is a local variable.  Ignore.  */
@@ -801,6 +831,7 @@ gen_ctf_variable (ctf_container_ref ctfc, dw_die_ref die)
   dw_die_ref var_type = ctf_get_AT_type (die);
   unsigned int external_vis = get_AT_flag (die, DW_AT_external);
   ctf_dtdef_ref var_dtd;
+  ctf_dvdef_ref dvd;
 
   /* Avoid duplicates.  */
   if (ctf_dvd_lookup (ctfc, die))
@@ -821,10 +852,13 @@ gen_ctf_variable (ctf_container_ref ctfc, dw_die_ref die)
   var_dtd = gen_ctf_type (ctfc, var_type);
 
   /* Generate the new CTF variable and update global counter.  */
-  (void) ctf_add_variable (ctfc, var_name, var_dtd, die, external_vis, decl);
+  dvd = ctf_add_variable (ctfc, var_name, var_dtd, die, external_vis, decl);
   /* Skip updating the number of global objects at this time.  This is updated
      later after pre-processing as some CTF variable records although
      generated now, will not be emitted later.  [PR105089].  */
+
+  /* Handle declaration tags on the variable.  */
+  gen_ctf_decl_tags_for_var (ctfc, die, dvd);
 }
 
 /* Add a CTF function record for the given input DWARF DIE.  */
@@ -842,8 +876,97 @@ gen_ctf_function (ctf_container_ref ctfc, dw_die_ref die)
      counter.  Note that DWARF encodes function types in both
      DW_TAG_subroutine_type and DW_TAG_subprogram in exactly the same
      way.  */
-  (void) gen_ctf_function_type (ctfc, die, true /* from_global_func */);
+  function_dtd = gen_ctf_function_type (ctfc, die, true /* from_global_func */);
   ctfc->ctfc_num_global_funcs += 1;
+
+  /* Handle declaration tags on the function.  */
+  gen_ctf_decl_tags (ctfc, die, function_dtd, -1U);
+}
+
+static ctf_dtdef_ref
+handle_ctf_type_tags (ctf_container_ref ctfc, dw_die_ref annot_die,
+		      ctf_dtdef_ref ref_dtd)
+{
+  if (!annot_die || !ref_dtd)
+    return NULL;
+
+  /* Recurse first.  */
+  dw_die_ref next_annot;
+  if ((next_annot = get_AT_ref (annot_die, DW_AT_GNU_annotation)))
+    ref_dtd = handle_ctf_type_tags (ctfc, next_annot, ref_dtd);
+
+  ctf_dtdef_ref tag_dtd = NULL;
+  const char *name = get_AT_string (annot_die, DW_AT_name);
+  const char *value = get_AT_string (annot_die, DW_AT_const_value);
+
+  if (strcmp (name, "btf_type_tag") == 0)
+    tag_dtd = ctf_add_type_tag (ctfc, CTF_ADD_ROOT, value, ref_dtd);
+
+  return tag_dtd;
+}
+
+/* Handle any DW_AT_GNU_annotation on decl DIE by constructing a CTF_K_DECL_TAG
+   type for the DW_TAG_GNU_annotation DIE to which it points, if this has not
+   been previously constructed.  There may be multiple annotations chained
+   together by further occurances of DW_AT_GNU_annotation in the annoation DIEs
+   themselves, in which case a corresponding CTF_K_DECL_TAG type is created for
+   each.  Unlike TYPE_TAGs, which form a chain, each DECL_TAG individually
+   refers directly to the annotated decl, which should be supplied in REF_DTD.
+   IDX is the zero-based component index indicating to which function parameter
+   or struct or union member the DECL_TAG refers, or (uint32_t) -1 if it refers
+   to a function decl or sou itself.
+   Note that because individual DECL_TAGs refer direcly to the annotated decl,
+   they cannot be deduplicated across usages.  */
+
+static void
+gen_ctf_decl_tags (ctf_container_ref ctfc, dw_die_ref die,
+		   ctf_dtdef_ref ref_dtd, uint32_t idx)
+{
+  if (!btf_debuginfo_p () || !die || !ref_dtd)
+    return;
+
+  dw_die_ref annot_die = get_AT_ref (die, DW_AT_GNU_annotation);
+  while (annot_die)
+    {
+      const char *name = get_AT_string (annot_die, DW_AT_name);
+      const char *value = get_AT_string (annot_die, DW_AT_const_value);
+
+      if (strcmp (name, "btf_decl_tag") == 0)
+	(void) ctf_add_decl_tag (ctfc, CTF_ADD_ROOT, value, ref_dtd, idx);
+
+      annot_die = get_AT_ref (annot_die, DW_AT_GNU_annotation);
+    }
+}
+
+/* Like gen_ctf_decl_tags above, but specifically for variables.  Declaration
+   tags may appear on variables or other declarations like functions, but due
+   to the distinction in CTF between variables and types the processing in
+   each case is slightly different.  REF_DVD is the CTF record for the variable
+   which is annotated.  */
+
+static void
+gen_ctf_decl_tags_for_var (ctf_container_ref ctfc, dw_die_ref die,
+			   ctf_dvdef_ref ref_dvd)
+{
+  if (!btf_debuginfo_p () || !die || !ref_dvd)
+    return;
+
+  ctf_dtdef_ref tag_dtd = NULL;
+
+  dw_die_ref annot_die = get_AT_ref (die, DW_AT_GNU_annotation);
+  while (annot_die)
+    {
+      const char *name = get_AT_string (annot_die, DW_AT_name);
+      const char *value = get_AT_string (annot_die, DW_AT_const_value);
+
+      if (strcmp (name, "btf_decl_tag") == 0)
+	{
+	  tag_dtd = ctf_add_decl_tag (ctfc, CTF_ADD_ROOT, value, NULL, -1U);
+	  tag_dtd->dtd_u.dtu_tag.ref_var = ref_dvd;
+	}
+
+      annot_die = get_AT_ref (annot_die, DW_AT_GNU_annotation);
+    }
 }
 
 /* Add CTF type record(s) for the given input DWARF DIE and return its type id.
@@ -910,6 +1033,12 @@ gen_ctf_type (ctf_container_ref ctfc, dw_die_ref die)
 
 	break;
       }
+    case DW_TAG_GNU_annotation:
+      /* These DIEs are traversed by gen_ctf_type_tags by following
+	 any DW_AT_GNU_annotation present in other types.  */
+      dtd = NULL;
+      unrecog_die = true;
+      break;
     case DW_TAG_reference_type:
       dtd = NULL;
       break;
diff --git a/gcc/testsuite/gcc.dg/debug/ctf/ctf-decl-tag-1.c b/gcc/testsuite/gcc.dg/debug/ctf/ctf-decl-tag-1.c
new file mode 100644
index 00000000000..564bbc76468
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/ctf/ctf-decl-tag-1.c
@@ -0,0 +1,31 @@
+/* CTF generation for btf_decl_tag attribute.
+
+   CTF does not encode these attributes and no CTF_K_DECL_TAG record should be
+   emitted; these records only exist internally to facilitate translation
+   to BTF.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O0 -gctf -dA" } */
+
+int x __attribute__((btf_decl_tag ("some_tag")));
+
+struct S {
+  int a;
+  int b __attribute__((btf_decl_tag ("_b")));
+  int c;
+};
+
+struct S some_S;
+
+void
+__attribute__((btf_decl_tag ("__func")))
+foo (int *ptr __attribute__((btf_decl_tag ("w"))), int val)
+{
+  *ptr = val;
+}
+
+/* Expect 5 CTF types: int, struct, void, int*, void (int*, int).  */
+/* { dg-final { scan-assembler-times "ctt_info" 5 } } */
+/* Ensure no CTF_K_DECL_TAG record is emitted.  */
+/* { dg-final { scan-assembler-not "\[\t \]0x3c*\[\t \]+\[^\n\]*ctt_info" } } */
+/* { dg-final { scan-assembler-not "\[\t \]0x3e*\[\t \]+\[^\n\]*ctt_info" } } */
diff --git a/gcc/testsuite/gcc.dg/debug/ctf/ctf-type-tag-1.c b/gcc/testsuite/gcc.dg/debug/ctf/ctf-type-tag-1.c
new file mode 100644
index 00000000000..ca37ef44fe2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/ctf/ctf-type-tag-1.c
@@ -0,0 +1,19 @@
+/* CTF generation for btf_type_tag attribute.
+
+   CTF does not encode these attributes and no CTF_K_TYPE_TAG record should be
+   emitted; these records only exist internally to facilitate translation
+   to BTF.  */
+
+/* { dg-do compile } */
+/* { dg-options "-O0 -gctf -dA" } */
+
+int * __attribute__((btf_type_tag ("some_tag"))) x;
+
+typedef unsigned int __attribute__((btf_type_tag ("other_tag"))) uint;
+const uint u;
+
+/* Expect 5 CTF types: int, int*, unsigned int, typedef, const.  */
+/* { dg-final { scan-assembler-times "ctt_info" 5 } } */
+/* Ensure no CTF_K_TYPE_TAG record is emitted.  */
+/* { dg-final { scan-assembler-not "\[\t \]0x40*\[\t \]+\[^\n\]*ctt_info" } } */
+/* { dg-final { scan-assembler-not "\[\t \]0x42*\[\t \]+\[^\n\]*ctt_info" } } */
diff --git a/include/ctf.h b/include/ctf.h
index 72a639ac9b4..f75e337ba0f 100644
--- a/include/ctf.h
+++ b/include/ctf.h
@@ -423,6 +423,10 @@ union
 #define CTF_K_CONST	12	/* ctt_type is base type.  */
 #define CTF_K_RESTRICT	13	/* ctt_type is base type.  */
 #define CTF_K_SLICE	14	/* Variant data is a ctf_slice_t.  */
+#define CTF_K_DECL_TAG	15	/* Declaration tag.  Internal use only.
+				   Not valid externally until CTF V4.  */
+#define CTF_K_TYPE_TAG	16	/* Type tag.  Internal use only.
+				   Not valid externally until CTF V4.  */
 
 #define CTF_K_MAX	63	/* Maximum possible (V2) CTF_K_* value.  */
 
-- 
2.47.3


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v11 4/6] btf: generate and output DECL_TAG and TYPE_TAG records
  2025-09-27  9:06 [PATCH v11 0/6] c,dwarf,btf: Add btf_decl_tag and btf_type_tag C attributes David Faust
                   ` (2 preceding siblings ...)
  2025-09-27  9:06 ` [PATCH v11 3/6] ctf: translate annotation DIEs to internal ctf David Faust
@ 2025-09-27  9:06 ` David Faust
  2025-09-27  9:07 ` [PATCH v11 5/6] doc: document btf_type_tag and btf_decl_tag attributes David Faust
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: David Faust @ 2025-09-27  9:06 UTC (permalink / raw)
  To: gcc-patches
  Cc: josmyers, rguenther, jose.marchesi, indu.bhagat, yonghong.song

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.

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-c2x-1.c: New test.

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         |  26 +++
 .../gcc.dg/debug/btf/btf-type-tag-2.c         |  13 ++
 .../gcc.dg/debug/btf/btf-type-tag-3.c         |  28 +++
 .../gcc.dg/debug/btf/btf-type-tag-4.c         |  24 +++
 .../gcc.dg/debug/btf/btf-type-tag-c2x-1.c     |  22 +++
 include/btf.h                                 |  14 ++
 11 files changed, 366 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-c2x-1.c

diff --git a/gcc/btfout.cc b/gcc/btfout.cc
index ff7ea42a961..5a210cd51e6 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,39 @@ 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)
+{
+  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 auxiliary 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 +1615,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 +1642,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 +1651,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 +1750,13 @@ btf_finish (void)
 
   btf_assign_var_ids (tu_ctfc);
   btf_assign_func_ids (tu_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)
+    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..63a6f324ab1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-1.c
@@ -0,0 +1,26 @@
+/* Test simple generation of BTF type tags.  */
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+#define __tag1 __attribute__((btf_type_tag("1")))
+#define __tag2 __attribute__((btf_type_tag("2")))
+
+/* var("kp") -> ptr -> type_tag("1") -> int  */
+int * __tag1 kp;
+
+struct Foo {
+  char a;
+  int b;
+};
+
+/* var("f") -> ptr -> type_tag("2") -> type_tag("1") -> struct("Foo")  */
+struct Foo * __tag1 __tag2 f;
+
+/* { dg-final { scan-assembler-times " BTF_KIND_VAR 'kp'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_PTR" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT" 1 } } */
+
+/* { dg-final { scan-assembler-times " BTF_KIND_VAR 'f'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_PTR" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG '1'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_STRUCT" 1 } } */
+
+/* { dg-final { scan-assembler-times " BTF_KIND_PTR ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG" 2 } } */
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..5adf3d041c7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-2.c
@@ -0,0 +1,13 @@
+/* Test generation of BTF type tags with cv-quals.  */
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+#define __tag __attribute__((btf_type_tag("1")))
+
+/* var("pci") -> const -> ptr -> type_tag("1") -> int  */
+int __tag *const pci;
+
+/* { dg-final { scan-assembler-times " BTF_KIND_VAR 'pci'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_CONST" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_CONST ''(\[\\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" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT" 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..0bf38532456
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-3.c
@@ -0,0 +1,28 @@
+/* Test generation of BTF type tags with typedefs.  */
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+#define __tag1 __attribute__((btf_type_tag("1")))
+#define __tag2 __attribute__((btf_type_tag("2")))
+
+typedef int *const cp;
+typedef int __tag1 *tp;
+
+/* var("x") -> ptr -> type_tag("2") -> typedef("cp") -> const -> ptr -> int  */
+cp __tag2 * x;
+
+/* var("y") -> const -> typedef("tp") -> ptr -> type_tag("1") -> int  */
+const tp y;
+
+/* { dg-final { scan-assembler-times " BTF_KIND_VAR 'x'(\[\\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 '2'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPEDEF 'cp'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPEDEF 'cp'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_CONST" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_CONST ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_PTR" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_PTR ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT" 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 'tp'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPEDEF 'tp'(\[\\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 '1'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '1'(\[\\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..0ec7e571db2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-4.c
@@ -0,0 +1,24 @@
+/* Test generation of BTF type tag when applied to function prototypes.  */
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+#define __tag1 __attribute__((btf_type_tag("1")))
+#define __tag2 __attribute__((btf_type_tag("2")))
+
+int * __tag1
+dothing (void __tag2 *ptr, int __tag2 __tag1 *xi)
+{
+  if (xi)
+    {
+      int *tmp = (int *) ptr;
+      return tmp + (*xi);
+    }
+
+  return (int *) ptr;
+}
+
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '2'(\[\\r\\n\]+\[^\\r\\n\]*){2} btt_type: void" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG '2'" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_PTR ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG '1'" 2 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_PTR ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG '2'" 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..30aff57618e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-c2x-1.c
@@ -0,0 +1,22 @@
+/* Test BTF type tag generation using C2x standard attribute syntax.
+   C2x attribute syntax does not allow attributes to "slide around".  */
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA -std=c23" } */
+
+#define __tag1 [[gnu::btf_type_tag ("1")]]
+#define __tag2 [[gnu::btf_type_tag ("2")]]
+#define __tag3 [[gnu::btf_type_tag ("3")]]
+
+/* Note that the BTF format still only allows to represent type tags on
+   pointer types, so we do not get any type_tag("1") from the below, as
+   it applies to the 'volatile int' type and cannot be represented.  */
+
+/* var(z) -> const -> ptr -> type_tag(2) -> type_tag(3) -> ptr -> type_tag(2) -> volatile -> 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_PTR" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_PTR ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG '2'\\)" 2 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG '3'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '3'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_PTR" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_VOLATILE" 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
-- 
2.47.3


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v11 5/6] doc: document btf_type_tag and btf_decl_tag attributes
  2025-09-27  9:06 [PATCH v11 0/6] c,dwarf,btf: Add btf_decl_tag and btf_type_tag C attributes David Faust
                   ` (3 preceding siblings ...)
  2025-09-27  9:06 ` [PATCH v11 4/6] btf: generate and output DECL_TAG and TYPE_TAG records David Faust
@ 2025-09-27  9:07 ` David Faust
  2025-09-27  9:07 ` [PATCH v11 6/6] bpf: add tests for CO-RE and BTF tag interaction David Faust
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 11+ messages in thread
From: David Faust @ 2025-09-27  9:07 UTC (permalink / raw)
  To: gcc-patches
  Cc: josmyers, rguenther, jose.marchesi, indu.bhagat, yonghong.song

gcc/
	* doc/extend.texi (Common Function Attributes)
	(Common Variable Attributes): Document btf_decl_tag attribute.
	(Common Type Attributes): Document btf_type_tag attribute.
---
 gcc/doc/extend.texi | 79 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)

diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 596cb5d3259..131205303ea 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -1971,6 +1971,13 @@ declares that @code{my_alloc1} returns 16-byte aligned pointers and
 that @code{my_alloc2} returns a pointer whose value modulo 32 is equal
 to 8.
 
+@cindex @code{btf_decl_tag} function attribute
+@item btf_decl_tag
+The @code{btf_decl_tag} attribute may be used to associate function
+declarations with arbitrary strings by recording those strings in DWARF
+and/or BTF information in the same way that it is used for variables.
+See @ref{Common Variable Attributes}.
+
 @cindex @code{cold} function attribute
 @item cold
 The @code{cold} attribute on functions is used to inform the compiler that
@@ -7167,6 +7174,41 @@ is given by the product of arguments 1 and 2, and similarly, that
 @code{malloc_ptr}, like the standard C function @code{malloc},
 returns an object whose size is given by argument 1 to the function.
 
+@cindex @code{btf_decl_tag} variable attribute
+@item btf_decl_tag (@var{argument})
+The @code{btf_decl_tag} attribute may be used to associate variable
+declarations, struct or union member declarations, function
+declarations, and function parameter declarations with arbitrary strings.
+These strings are not interpreted by the compiler in any way, and have
+no effect on code generation.  Instead, these user-provided strings
+are recorded in DWARF (via @code{DW_AT_GNU_annotation} and
+@code{DW_TAG_GNU_annotation} extensions) and BTF information (via
+@code{BTF_KIND_DECL_TAG} records), and associated to the attributed
+declaration.  If neither DWARF nor BTF information is generated, the
+attribute has no effect.
+
+The argument is treated as a null-terminated sequence of zero or more
+non-null bytes.  Wide character strings are not supported.
+
+The attribute may be supplied multiple times for a single declaration,
+in which case each distinct argument string will be recorded in a
+separate DIE or BTF record, each associated to the declaration.  For
+a single declaration with multiple @code{btf_decl_tag} attributes,
+the order of the @code{DW_TAG_GNU_annotation} DIEs produced is not
+guaranteed to maintain the order of attributes in the source code.
+
+For example:
+
+@smallexample
+int *foo __attribute__ ((btf_decl_tag ("__percpu")));
+@end smallexample
+
+@noindent
+when compiled with @option{-gbtf} results in an additional
+@code{BTF_KIND_DECL_TAG} BTF record to be emitted in the BTF info,
+associating the string @samp{__percpu} with the @code{BTF_KIND_VAR}
+record for the variable @code{foo}.
+
 @cindex @code{cleanup} variable attribute
 @item cleanup (@var{cleanup_function})
 The @code{cleanup} attribute runs a function when the variable goes
@@ -8356,6 +8398,43 @@ is given by the product of arguments 1 and 2, and that
 @code{malloc_type}, like the standard C function @code{malloc},
 returns an object whose size is given by argument 1 to the function.
 
+@cindex @code{btf_type_tag} type attribute
+@item btf_type_tag (@var{argument})
+The @code{btf_type_tag} attribute may be used to associate (to ``tag'')
+particular types with arbitrary string annotations.  These annotations
+are recorded in debugging info by supported debug formats, currently
+DWARF (via @code{DW_AT_GNU_annotation} and @code{DW_TAG_GNU_annotation}
+extensions) and BTF (via @code{BTF_KIND_TYPE_TAG} records).  These
+annotation strings are not interpreted by the compiler in any way, and
+have no effect on code generation.  If neither DWARF nor BTF
+information is generated, the attribute has no effect.
+
+The argument is treated as a null-terminated sequence of zero or more
+non-null bytes.  Wide character strings are not supported.
+
+The attribute may be supplied multiple times for a single type, in
+which case each distinct argument string will be recorded in a
+separate DIE or BTF record, each associated to the type.  For a single
+type with multiple @code{btf_type_tag} attributes, the order of the
+@code{DW_TAG_GNU_annotation} DIEs produced is not guaranteed to
+maintain the order of attributes in the source code.
+
+For example the following code:
+
+@smallexample
+int * __attribute__ ((btf_type_tag ("__user"))) foo;
+@end smallexample
+
+@noindent
+when compiled with @option{-gbtf} results in an additional
+@code{BTF_KIND_TYPE_TAG} BTF record to be emitted in the BTF info,
+associating the string @samp{__user} with the normal @code{BTF_KIND_PTR}
+record for the pointer-to-integer type used in the declaration.
+
+Note that the BTF format currently only has a representation for type
+tags associated with pointer types.  Type tags on non-pointer types
+may be silently skipped when generating BTF.
+
 @cindex @code{copy} type attribute
 @item copy
 @itemx copy (@var{expression})
-- 
2.47.3


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v11 6/6] bpf: add tests for CO-RE and BTF tag interaction
  2025-09-27  9:06 [PATCH v11 0/6] c,dwarf,btf: Add btf_decl_tag and btf_type_tag C attributes David Faust
                   ` (4 preceding siblings ...)
  2025-09-27  9:07 ` [PATCH v11 5/6] doc: document btf_type_tag and btf_decl_tag attributes David Faust
@ 2025-09-27  9:07 ` David Faust
  2025-09-27 10:30 ` [PATCH v11 0/6] c, dwarf, btf: Add btf_decl_tag and btf_type_tag C attributes David Faust
  2025-10-06 17:55 ` David Faust
  7 siblings, 0 replies; 11+ messages in thread
From: David Faust @ 2025-09-27  9:07 UTC (permalink / raw)
  To: gcc-patches
  Cc: josmyers, rguenther, jose.marchesi, indu.bhagat, yonghong.song

Add a couple of tests to ensure that BTF type/decl tags do not interfere
with generation of BPF CO-RE relocations.

gcc/testsuite/
	* gcc.target/bpf/core-btf-tag-1.c: New test.
	* gcc.target/bpf/core-btf-tag-2.c: New test.
---
 gcc/testsuite/gcc.target/bpf/core-btf-tag-1.c | 23 +++++++++++++++++++
 gcc/testsuite/gcc.target/bpf/core-btf-tag-2.c | 23 +++++++++++++++++++
 2 files changed, 46 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/bpf/core-btf-tag-1.c
 create mode 100644 gcc/testsuite/gcc.target/bpf/core-btf-tag-2.c

diff --git a/gcc/testsuite/gcc.target/bpf/core-btf-tag-1.c b/gcc/testsuite/gcc.target/bpf/core-btf-tag-1.c
new file mode 100644
index 00000000000..bd0fb3e40be
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-btf-tag-1.c
@@ -0,0 +1,23 @@
+/* Test that BTF type tags do not interfere with CO-RE relocations.  */
+
+/* { dg-do compile } */
+/* { dg-options "-gbtf -dA -mco-re" } */
+
+struct bpf_cpumask {
+  int i;
+  char c;
+} __attribute__((preserve_access_index));
+
+struct kptr_nested {
+	struct bpf_cpumask * __attribute__((btf_type_tag("kptr"))) mask;
+} __attribute__((preserve_access_index));
+
+void foo (struct kptr_nested *nested)
+{
+  if (nested && nested->mask)
+    nested->mask->i = 5;
+}
+
+/* { dg-final { scan-assembler-times "bpfcr_insn" 3 } } */
+/* { dg-final { scan-assembler-times "bpfcr_type \\(struct" 3 } } */
+/* { dg-final { scan-assembler-times "bpfcr_astr_off \\(\"0:0\"\\)" 3 } } */
diff --git a/gcc/testsuite/gcc.target/bpf/core-btf-tag-2.c b/gcc/testsuite/gcc.target/bpf/core-btf-tag-2.c
new file mode 100644
index 00000000000..6654ffe3ae0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/bpf/core-btf-tag-2.c
@@ -0,0 +1,23 @@
+/* Test that BTF decl tags do not interfere with CO-RE relocations.  */
+
+/* { dg-do compile } */
+/* { dg-options "-gbtf -dA -mco-re" } */
+
+struct bpf_cpumask {
+  int i;
+  char c;
+} __attribute__((preserve_access_index));
+
+struct kptr_nested {
+	struct bpf_cpumask * mask __attribute__((btf_decl_tag ("decltag")));
+} __attribute__((preserve_access_index));
+
+void foo (struct kptr_nested *nested __attribute__((btf_decl_tag ("foo"))))
+{
+  if (nested && nested->mask)
+    nested->mask->i = 5;
+}
+
+/* { dg-final { scan-assembler-times "bpfcr_insn" 3 } } */
+/* { dg-final { scan-assembler-times "bpfcr_type \\(struct" 3 } } */
+/* { dg-final { scan-assembler-times "bpfcr_astr_off \\(\"0:0\"\\)" 3 } } */
-- 
2.47.3


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v11 0/6] c, dwarf, btf: Add btf_decl_tag and btf_type_tag C attributes
  2025-09-27  9:06 [PATCH v11 0/6] c,dwarf,btf: Add btf_decl_tag and btf_type_tag C attributes David Faust
                   ` (5 preceding siblings ...)
  2025-09-27  9:07 ` [PATCH v11 6/6] bpf: add tests for CO-RE and BTF tag interaction David Faust
@ 2025-09-27 10:30 ` David Faust
  2025-10-06 17:55 ` David Faust
  7 siblings, 0 replies; 11+ messages in thread
From: David Faust @ 2025-09-27 10:30 UTC (permalink / raw)
  To: gcc-patches; +Cc: Joseph Myers


On 9/27/25 02:06, David Faust wrote:
> [v10: https://gcc.gnu.org/pipermail/gcc-patches/2025-September/696032.html
>  Changed from v10:
>  - Patch 6: Fix error introduced in v10 that caused the patch to include
>    duplicate documentation for counted_by variable attribute.

Note: the error was in v10 patch 5 (documentation patch) not 6.
Apologies for adding more confusion.

> 
>  Review status:
>  - Patches 1-4 have been OK'd.
>  - Patch 5 needs review.
>  - Patch 6 adds two small BPF-specific tests that I'll count as obvious
>    without further review unless there is an objection.  ]
> 
> This patch series adds support for the btf_decl_tag and btf_type_tag attributes
> to GCC. This entails:
> 
> - Two new C-family attributes that allow to associate (to "tag") particular
>   declarations and types with arbitrary strings. As explained below, this is
>   intended to be used to, for example, characterize certain pointer types.  A
>   single declaration or type may have multiple occurrences of these attributes.
> 
> - The conveyance of that information in the DWARF output in the form of a new
>   DIE: DW_TAG_GNU_annotation, and a new attribute: DW_AT_GNU_annotation.
> 
> - The conveyance of that information in the BTF output in the form of two new
>   kinds of BTF objects: BTF_KIND_DECL_TAG and BTF_KIND_TYPE_TAG. These BTF
>   kinds are already supported by LLVM and other tools in the BPF ecosystem.
> 
> Both of these attributes are already supported by clang, and are already being
> used in various ways by BPF support inside the Linux kernel.
> 
> It is worth noting that while the Linux kernel and BPF/BTF is the motivating use
> case of this feature, the format of the new DWARF extension is generic.  This
> work could be easily adapted to provide a general way for program authors to
> annotate types and declarations with arbitrary information for any
> post-compilation analysis needs, not just the Linux kernel BPF verifier.  For
> example, these annotations could be used to aid in ABI analysis.
> 
> Purpose
> =======
> 
> 1)  Addition of C-family language constructs (attributes) to specify free-text
>     tags on certain language elements, such as struct fields.
> 
>     The purpose of these annotations is to provide additional information about
>     types, variables, and function parameters of interest to the kernel. A
>     driving use case is to tag pointer types within the Linux kernel and BPF
>     programs with additional semantic information, such as '__user' or '__rcu'.
> 
>     For example, consider the Linux kernel function do_execve with the
>     following declaration:
> 
>       static int do_execve(struct filename *filename,
>          const char __user *const __user *__argv,
>          const char __user *const __user *__envp);
> 
>     Here, __user could be defined with these annotations to record semantic
>     information about the pointer parameters (e.g., they are user-provided) in
>     DWARF and BTF information. Other kernel facilities such as the BPF verifier
>     can read the tags and make use of the information.
> 
> 2)  Conveying the tags in the generated DWARF debug info.
> 
>     The main motivation for emitting the tags in DWARF is that the Linux kernel
>     generates its BTF information via pahole, using DWARF as a source:
> 
>         +--------+  BTF                  BTF   +----------+
>         | pahole |-------> vmlinux.btf ------->| verifier |
>         +--------+                             +----------+
>             ^                                        ^
>             |                                        |
>       DWARF |                                    BTF |
>             |                                        |
>          vmlinux                              +-------------+
>          module1.ko                           | BPF program |
>          module2.ko                           +-------------+
>            ...
> 
>     This is because:
> 
>     a)  Unlike GCC, LLVM will only generate BTF for BPF programs.
> 
>     b)  GCC can generate BTF for whatever target with -gbtf, but there is no
>         support for linking/deduplicating BTF in the linker.
> 
>     c)  pahole injects additional BTF information based on specific knowledge
>         of kernel objects which is not available to the compiler.
> 
>     In the scenario above, the verifier needs access to the pointer tags of
>     both the kernel types/declarations (conveyed in the DWARF and translated
>     to BTF by pahole) and those of the BPF program (available directly in BTF).
> 
>     Another motivation for having the tag information in DWARF, unrelated to
>     BPF and BTF, is that the drgn project (another DWARF consumer) also wants
>     to benefit from these tags in order to differentiate between different
>     kinds of pointers in the kernel.
> 
> 3)  Conveying the tags in the generated BTF debug info.
> 
>     This is easy: the main purpose of having this info in BTF is for the
>     compiled BPF programs. The kernel verifier can then access the tags
>     of pointers used by the BPF programs.
> 
> For more information about these tags and the motivation behind them, please
> refer to the following Linux kernel discussions: [1], [2], [3].
> 
> DWARF Representation
> ====================
> 
> Compared to prior iterations of this work, this patch series introduces a new
> DWARF representation meant to address issues in the previously proposed format.
> The format is detailed below.
> 
> Note that the obvious solution of introducing a new DIE to be chained in type
> chains similar to type modifiers like const and volatile is not feasible
> because it would break DWARF readers.
> 
> New DWARF extension: DW_TAG_GNU_annotation.  These DIEs encode the annotation
> information.  They exist near the top level of the DIE tree as children of the
> compilation unit DIE.  The user-supplied annotations ("tags") are encoded via
> DW_AT_name and DW_AT_const_value.  DW_AT_name holds the name of the attribute
> which is the source of the annotation (currently only "btf_type_tag" or
> "btf_decl_tag").  DW_AT_const_value holds the arbitrary user string from the
> attribute argument.
> 
>   DW_TAG_GNU_annotation
>     DW_AT_name: "btf_decl_tag" or "btf_type_tag"
>     DW_AT_const_value: <arbitrary user-provided string from attribute arg>
>     DW_AT_GNU_annotation: see below.
> 
> New DWARF extension: DW_AT_GNU_annotation.  If present, the
> DW_AT_GNU_annotation attribute is a reference to a DW_TAG_GNU_annotation DIE
> holding annotations for the object.
> 
> If a single declaration or type at the language level has multiple occurrences
> of btf_decl_tag or btf_type_tag attribute, then the DW_TAG_GNU_annotation DIE
> referenced by that object will itself have DW_AT_GNU_annotation referring to
> another annotation DIE.  In this way the annotation DIEs are chained together.
> 
> Multiple distinct declarations or types may refer via DW_AT_GNU_annotation to
> the same DW_TAG_GNU_annotation DIE, if they share the same tags.
> 
> For more information on this format, please refer to recent talks at GNU Tools
> Cauldron [4] and Linux Plumbers Conference [5]. Older iterations of this work
> and related discussions may be found in [6,7,8].
> 
> BTF Representation
> ==================
> 
> In BTF, BTF_KIND_DECL_TAG and BTF_KIND_TYPE_TAG records convey the annotations.
> Both records hold the annotation value in their name field.
> 
> BTF_KIND_DECL_TAG records refer to the annotated object by BTF ID.  Each
> DECL_TAG record is followed by an additional 32-bit 'component_idx', which
> indicates to which component of an object the tag applies.  If the annotated
> object is a struct, union, or function, then 'component_idx' is the 0-based
> index of the member or function parameter to which the tag applies.   If the
> annotated object is a variable, or if it is a function and the tag applies to
> the function declaration itself (rather than a parameter), then 'component_idx'
> is -1.
> 
> BTF_KIND_TYPE_TAG records form part of the type chain.  Currently the BTF
> format can only represent type tags applied to pointer types; type tags applied
> to any non-pointer type cannot be represented in BTF.  For type tags applied to
> pointer types, the BTF_KIND_PTR refers to the TYPE_TAG by ID, and the TYPE_TAG
> refers to the pointee type by ID.
> 
> Example: btf_decl_tag
> =====================
> 
> Consider the following declarations:
> 
>   int  *x __attribute__((btf_decl_tag ("rw"), btf_decl_tag ("devicemem")));
>   struct {
>     int size;
>     char *ptr __attribute__((btf_decl_tag("rw")));
>   } y;
> 
> These declarations produce the following DWARF information:
> 
>  <1><1e>: Abbrev Number: 3 (DW_TAG_variable)
>     <1f>   DW_AT_name        : x
>     <24>   DW_AT_type        : <0x36>
>     <28>   DW_TAG_GNU_annotation: <0x4a>
>     ...
>  <1><36>: Abbrev Number: 1 (DW_TAG_pointer_type)
>     <37>   DW_AT_byte_size   : 8
>     <37>   DW_AT_type        : <0x3b>
>  <1><3b>: Abbrev Number: 4 (DW_TAG_base_type)
>     <3e>   DW_AT_name        : int
>     ...
>  <1><42>: Abbrev Number: 5 (DW_TAG_GNU_annotation)
>     <43>   DW_AT_name        : (indirect string, offset: 0): btf_decl_tag
>     <47>   DW_AT_const_value : rw
>  <1><4a>: Abbrev Number: 6 (DW_TAG_GNU_annotation)
>     <4b>   DW_AT_name        : (indirect string, offset: 0): btf_decl_tag
>     <4f>   DW_AT_const_value : (indirect string, offset: 0x1f): devicemem
>     <53>   DW_AT_GNU_annotation: <0x42>
>  <1><57>: Abbrev Number: 7 (DW_TAG_structure_type)
>     ...
>  <2><60>: Abbrev Number: 8 (DW_TAG_member)
>     <61>   DW_AT_name        : (indirect string, offset: 0x1a): size
>     <68>   DW_AT_type        : <0x3b>
>     ...
>  <2><6d>: Abbrev Number: 9 (DW_TAG_member)
>     <6e>   DW_AT_name        : ptr
>     <75>   DW_AT_type        : <0x7f>
>     <7a>   DW_AT_GNU_annotation: <0x42>
>     ...
>  <2><7e>: Abbrev Number: 0
>  <1><7f>: Abbrev Number: 1 (DW_TAG_pointer_type)
>     <80>   DW_AT_byte_size   : 8
>     <80>   DW_AT_type        : <0x84>
>  <1><84>: Abbrev Number: 10 (DW_TAG_base_type)
>     <85>   DW_AT_byte_size   : 1
>     <86>   DW_AT_encoding    : 6	(signed char)
>     <87>   DW_AT_name        : (indirect string, offset: 0x5e): char
>  <1><8b>: Abbrev Number: 11 (DW_TAG_variable)
>     <8c>   DW_AT_name        : y
>     <91>   DW_AT_type        : <0x57>
>     ...
> 
> The variable DIE for 'x' refers by DW_AT_GNU_annotation to the DIE holding the
> annotation for the "devicemem" tag, which in turn refers to the DIE holding
> the annotation for "rw".  The DW_TAG_member DIE for the member 'ptr' of the
> struct refers to the annotation die for "rw" directly, which is thereby shared
> between the two declarations.
> 
> And BTF information:
> 
>   [1] STRUCT '(anon)' size=16 vlen=2
>       'size' type_id=2 bits_offset=0
>       'ptr' type_id=3 bits_offset=64
>   [2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
>   [3] PTR '(anon)' type_id=4
>   [4] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED
>   [5] PTR '(anon)' type_id=2
>   [6] DECL_TAG 'devicemem' type_id=10 component_idx=-1
>   [7] DECL_TAG 'rw' type_id=10 component_idx=-1
>   [8] DECL_TAG 'rw' type_id=1 component_idx=1
>   [9] VAR 'y' type_id=1, linkage=global
>   [10] VAR 'x' type_id=5, linkage=global
> 
> Note how the component_idx identifies to which member of the struct type the
> decl tag is applied.
> 
> 
> Example: btf_type_tag
> =====================
> 
> Consider the following code snippet:
> 
>   int __attribute__((btf_type_tag("rcu"), btf_type_tag ("foo"))) x;
> 
>   void
>   do_thing (struct S * __attribute__((btf_type_tag ("rcu"))) rcu_s,
>             void * __attribute__((btf_type_tag("foo"))) ptr)
>   { ... }
> 
> The relevant DWARF information produced is as follows:
> 
>  <1><2e>: Abbrev Number: 3 (DW_TAG_structure_type)
>     <2f>   DW_AT_name        : S
>     ...
>  ...
>  <1><4d>: Abbrev Number: 6 (DW_TAG_variable)
>     <4e>   DW_AT_name        : x
>     <53>   DW_AT_type        : <0x61>
>     ...
>  <1><61>: Abbrev Number: 7 (DW_TAG_base_type)
>     <62>   DW_AT_byte_size   : 4
>     <63>   DW_AT_encoding    : 5	(signed)
>     <64>   DW_AT_name        : int
>     <68>   DW_AT_GNU_annotation: <0x75>
>  <1><6c>: Abbrev Number: 1 (DW_TAG_GNU_annotation)
>     <6d>   DW_AT_name        : (indirect string, offset: 0x13): btf_type_tag
>     <71>   DW_AT_const_value : rcu
>  <1><75>: Abbrev Number: 8 (DW_TAG_GNU_annotation)
>     <76>   DW_AT_name        : (indirect string, offset: 0x13): btf_type_tag
>     <7a>   DW_AT_const_value : foo
>     <7e>   DW_AT_GNU_annotation: <0x6c>
>  <1><82>: Abbrev Number: 9 (DW_TAG_subprogram)
>     <83>   DW_AT_name        : (indirect string, offset: 0x20): do_thing
>     ...
>  <2><a1>: Abbrev Number: 10 (DW_TAG_formal_parameter)
>     <a2>   DW_AT_name        : (indirect string, offset: 0x5): rcu_s
>     <a9>   DW_AT_type        : <0xc0>
>     ...
>  <2><b0>: Abbrev Number: 11 (DW_TAG_formal_parameter)
>     <b1>   DW_AT_name        : ptr
>     <b8>   DW_AT_type        : <0xca>
>     ...
>  <2><bf>: Abbrev Number: 0
>  <1><c0>: Abbrev Number: 12 (DW_TAG_pointer_type)
>     <c1>   DW_AT_byte_size   : 8
>     <c2>   DW_AT_type        : <0x2e>
>     <c6>   DW_AT_GNU_annotation: <0x6c>
>  <1><ca>: Abbrev Number: 13 (DW_TAG_pointer_type)
>     <cb>   DW_AT_byte_size   : 8
>     <cc>   DW_AT_GNU_annotation: <0xd0>
>  <1><d0>: Abbrev Number: 1 (DW_TAG_GNU_annotation)
>     <d1>   DW_AT_name        : (indirect string, offset: 0x13): btf_type_tag
>     <d5>   DW_AT_const_value : foo
> 
> Note how in this case, two annotation DIEs for "foo" are produced, because
> it is used in two distinct sets of type tags which do not allow it to be
> shared. The DIE for "rcu", however, is shared between uses.
> 
> And BTF information:
> 
>   [1] STRUCT 'S' size=8 vlen=1
>       ...
>   [2] INT 'long int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED
>   [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
>   [4] FUNC_PROTO '(anon)' ret_type_id=0 vlen=2
>       'rcu_s' type_id=5
>       'ptr' type_id=6
>   [5] PTR '(anon)' type_id=9
>   [6] PTR '(anon)' type_id=10
>   [7] VAR 'x' type_id=3, linkage=global
>   [8] FUNC 'do_thing' type_id=4 linkage=global
>   [9] TYPE_TAG 'rcu' type_id=1
>   [10] TYPE_TAG 'foo' type_id=0
> 
> Note how the TYPE_TAG are injected into the type chain between the PTR
> record and the pointee type, e.g.
> 
>   param 'rcu_s' -> PTR -> TYPE_TAG 'rcu' -> STRUCT 'S'
> 
> Note also that the type tags which apply to the integer type of variable 'x'
> are not represented, since BTF currently has no way to represent type tags
> on non-pointer types.
> 
> References
> ==========
> 
> [1] https://lore.kernel.org/bpf/20210914223004.244411-1-yhs@fb.com/
> [2] https://lore.kernel.org/bpf/20211012164838.3345699-1-yhs@fb.com/
> [3] https://lore.kernel.org/bpf/20211112012604.1504583-1-yhs@fb.com/
> [4] https://gcc.gnu.org/wiki/cauldron2024#cauldron2024talks.what_is_new_in_the_bpf_support_in_the_gnu_toolchain
> [5] https://lpc.events/event/18/contributions/1924/
> [6] https://gcc.gnu.org/pipermail/gcc-patches/2022-April/592685.html
> [7] https://gcc.gnu.org/pipermail/gcc-patches/2022-June/596355.html
> [8] https://gcc.gnu.org/pipermail/gcc-patches/2023-July/624156.html
> 
> 
> David Faust (6):
>   c-family: add btf_type_tag and btf_decl_tag attributes
>   dwarf: create annotation DIEs for btf tags
>   ctf: translate annotation DIEs to internal ctf
>   btf: generate and output DECL_TAG and TYPE_TAG records
>   doc: document btf_type_tag and btf_decl_tag attributes
>   bpf: add tests for CO-RE and BTF tag interaction
> 
>  gcc/btfout.cc                                 | 171 +++++++--
>  gcc/c-family/c-attribs.cc                     | 110 +++++-
>  gcc/ctfc.cc                                   |  80 ++++-
>  gcc/ctfc.h                                    |  43 ++-
>  gcc/doc/extend.texi                           |  79 +++++
>  gcc/dwarf2ctf.cc                              | 135 ++++++-
>  gcc/dwarf2out.cc                              | 328 ++++++++++++++++--
>  gcc/testsuite/gcc.dg/attr-btf-decl-tag-1.c    |  14 +
>  gcc/testsuite/gcc.dg/attr-btf-decl-tag-2.c    |  15 +
>  gcc/testsuite/gcc.dg/attr-btf-type-tag-1.c    |  12 +
>  gcc/testsuite/gcc.dg/attr-btf-type-tag-2.c    |   9 +
>  gcc/testsuite/gcc.dg/attr-btf-type-tag-3.c    |   8 +
>  .../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         |  26 ++
>  .../gcc.dg/debug/btf/btf-type-tag-2.c         |  13 +
>  .../gcc.dg/debug/btf/btf-type-tag-3.c         |  28 ++
>  .../gcc.dg/debug/btf/btf-type-tag-4.c         |  24 ++
>  .../gcc.dg/debug/btf/btf-type-tag-c2x-1.c     |  22 ++
>  .../gcc.dg/debug/ctf/ctf-decl-tag-1.c         |  31 ++
>  .../gcc.dg/debug/ctf/ctf-type-tag-1.c         |  19 +
>  .../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      |  20 ++
>  .../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 +++
>  gcc/testsuite/gcc.target/bpf/core-btf-tag-1.c |  23 ++
>  gcc/testsuite/gcc.target/bpf/core-btf-tag-2.c |  23 ++
>  include/btf.h                                 |  14 +
>  include/ctf.h                                 |   4 +
>  include/dwarf2.def                            |   4 +
>  41 files changed, 1555 insertions(+), 65 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/attr-btf-decl-tag-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/attr-btf-decl-tag-2.c
>  create mode 100644 gcc/testsuite/gcc.dg/attr-btf-type-tag-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/attr-btf-type-tag-2.c
>  create mode 100644 gcc/testsuite/gcc.dg/attr-btf-type-tag-3.c
>  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-c2x-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/debug/ctf/ctf-decl-tag-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/debug/ctf/ctf-type-tag-1.c
>  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
>  create mode 100644 gcc/testsuite/gcc.target/bpf/core-btf-tag-1.c
>  create mode 100644 gcc/testsuite/gcc.target/bpf/core-btf-tag-2.c
> 


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v11 0/6] c, dwarf, btf: Add btf_decl_tag and btf_type_tag C attributes
  2025-09-27  9:06 [PATCH v11 0/6] c,dwarf,btf: Add btf_decl_tag and btf_type_tag C attributes David Faust
                   ` (6 preceding siblings ...)
  2025-09-27 10:30 ` [PATCH v11 0/6] c, dwarf, btf: Add btf_decl_tag and btf_type_tag C attributes David Faust
@ 2025-10-06 17:55 ` David Faust
  7 siblings, 0 replies; 11+ messages in thread
From: David Faust @ 2025-10-06 17:55 UTC (permalink / raw)
  To: gcc-patches
  Cc: josmyers, rguenther, jose.marchesi, indu.bhagat, yonghong.song


On 9/27/25 02:06, David Faust wrote:
> [v10: https://gcc.gnu.org/pipermail/gcc-patches/2025-September/696032.html
>  Changed from v10:
>  - Patch 6: Fix error introduced in v10 that caused the patch to include
>    duplicate documentation for counted_by variable attribute.

(note: error introduced in v10/fixed in v11 was in patch 5 not 6.)

> 
>  Review status:
>  - Patches 1-4 have been OK'd.
>  - Patch 5 needs review.
>  - Patch 6 adds two small BPF-specific tests that I'll count as obvious
>    without further review unless there is an objection.  ]
Given that the functionality of this series in patches 1-4 has been OK'd,
I intend to push this series to trunk in a few days if there are no
further comments.

> 
> This patch series adds support for the btf_decl_tag and btf_type_tag attributes
> to GCC. This entails:
> 
> - Two new C-family attributes that allow to associate (to "tag") particular
>   declarations and types with arbitrary strings. As explained below, this is
>   intended to be used to, for example, characterize certain pointer types.  A
>   single declaration or type may have multiple occurrences of these attributes.
> 
> - The conveyance of that information in the DWARF output in the form of a new
>   DIE: DW_TAG_GNU_annotation, and a new attribute: DW_AT_GNU_annotation.
> 
> - The conveyance of that information in the BTF output in the form of two new
>   kinds of BTF objects: BTF_KIND_DECL_TAG and BTF_KIND_TYPE_TAG. These BTF
>   kinds are already supported by LLVM and other tools in the BPF ecosystem.
> 
> Both of these attributes are already supported by clang, and are already being
> used in various ways by BPF support inside the Linux kernel.
> 
> It is worth noting that while the Linux kernel and BPF/BTF is the motivating use
> case of this feature, the format of the new DWARF extension is generic.  This
> work could be easily adapted to provide a general way for program authors to
> annotate types and declarations with arbitrary information for any
> post-compilation analysis needs, not just the Linux kernel BPF verifier.  For
> example, these annotations could be used to aid in ABI analysis.
> 
> Purpose
> =======
> 
> 1)  Addition of C-family language constructs (attributes) to specify free-text
>     tags on certain language elements, such as struct fields.
> 
>     The purpose of these annotations is to provide additional information about
>     types, variables, and function parameters of interest to the kernel. A
>     driving use case is to tag pointer types within the Linux kernel and BPF
>     programs with additional semantic information, such as '__user' or '__rcu'.
> 
>     For example, consider the Linux kernel function do_execve with the
>     following declaration:
> 
>       static int do_execve(struct filename *filename,
>          const char __user *const __user *__argv,
>          const char __user *const __user *__envp);
> 
>     Here, __user could be defined with these annotations to record semantic
>     information about the pointer parameters (e.g., they are user-provided) in
>     DWARF and BTF information. Other kernel facilities such as the BPF verifier
>     can read the tags and make use of the information.
> 
> 2)  Conveying the tags in the generated DWARF debug info.
> 
>     The main motivation for emitting the tags in DWARF is that the Linux kernel
>     generates its BTF information via pahole, using DWARF as a source:
> 
>         +--------+  BTF                  BTF   +----------+
>         | pahole |-------> vmlinux.btf ------->| verifier |
>         +--------+                             +----------+
>             ^                                        ^
>             |                                        |
>       DWARF |                                    BTF |
>             |                                        |
>          vmlinux                              +-------------+
>          module1.ko                           | BPF program |
>          module2.ko                           +-------------+
>            ...
> 
>     This is because:
> 
>     a)  Unlike GCC, LLVM will only generate BTF for BPF programs.
> 
>     b)  GCC can generate BTF for whatever target with -gbtf, but there is no
>         support for linking/deduplicating BTF in the linker.
> 
>     c)  pahole injects additional BTF information based on specific knowledge
>         of kernel objects which is not available to the compiler.
> 
>     In the scenario above, the verifier needs access to the pointer tags of
>     both the kernel types/declarations (conveyed in the DWARF and translated
>     to BTF by pahole) and those of the BPF program (available directly in BTF).
> 
>     Another motivation for having the tag information in DWARF, unrelated to
>     BPF and BTF, is that the drgn project (another DWARF consumer) also wants
>     to benefit from these tags in order to differentiate between different
>     kinds of pointers in the kernel.
> 
> 3)  Conveying the tags in the generated BTF debug info.
> 
>     This is easy: the main purpose of having this info in BTF is for the
>     compiled BPF programs. The kernel verifier can then access the tags
>     of pointers used by the BPF programs.
> 
> For more information about these tags and the motivation behind them, please
> refer to the following Linux kernel discussions: [1], [2], [3].
> 
> DWARF Representation
> ====================
> 
> Compared to prior iterations of this work, this patch series introduces a new
> DWARF representation meant to address issues in the previously proposed format.
> The format is detailed below.
> 
> Note that the obvious solution of introducing a new DIE to be chained in type
> chains similar to type modifiers like const and volatile is not feasible
> because it would break DWARF readers.
> 
> New DWARF extension: DW_TAG_GNU_annotation.  These DIEs encode the annotation
> information.  They exist near the top level of the DIE tree as children of the
> compilation unit DIE.  The user-supplied annotations ("tags") are encoded via
> DW_AT_name and DW_AT_const_value.  DW_AT_name holds the name of the attribute
> which is the source of the annotation (currently only "btf_type_tag" or
> "btf_decl_tag").  DW_AT_const_value holds the arbitrary user string from the
> attribute argument.
> 
>   DW_TAG_GNU_annotation
>     DW_AT_name: "btf_decl_tag" or "btf_type_tag"
>     DW_AT_const_value: <arbitrary user-provided string from attribute arg>
>     DW_AT_GNU_annotation: see below.
> 
> New DWARF extension: DW_AT_GNU_annotation.  If present, the
> DW_AT_GNU_annotation attribute is a reference to a DW_TAG_GNU_annotation DIE
> holding annotations for the object.
> 
> If a single declaration or type at the language level has multiple occurrences
> of btf_decl_tag or btf_type_tag attribute, then the DW_TAG_GNU_annotation DIE
> referenced by that object will itself have DW_AT_GNU_annotation referring to
> another annotation DIE.  In this way the annotation DIEs are chained together.
> 
> Multiple distinct declarations or types may refer via DW_AT_GNU_annotation to
> the same DW_TAG_GNU_annotation DIE, if they share the same tags.
> 
> For more information on this format, please refer to recent talks at GNU Tools
> Cauldron [4] and Linux Plumbers Conference [5]. Older iterations of this work
> and related discussions may be found in [6,7,8].
> 
> BTF Representation
> ==================
> 
> In BTF, BTF_KIND_DECL_TAG and BTF_KIND_TYPE_TAG records convey the annotations.
> Both records hold the annotation value in their name field.
> 
> BTF_KIND_DECL_TAG records refer to the annotated object by BTF ID.  Each
> DECL_TAG record is followed by an additional 32-bit 'component_idx', which
> indicates to which component of an object the tag applies.  If the annotated
> object is a struct, union, or function, then 'component_idx' is the 0-based
> index of the member or function parameter to which the tag applies.   If the
> annotated object is a variable, or if it is a function and the tag applies to
> the function declaration itself (rather than a parameter), then 'component_idx'
> is -1.
> 
> BTF_KIND_TYPE_TAG records form part of the type chain.  Currently the BTF
> format can only represent type tags applied to pointer types; type tags applied
> to any non-pointer type cannot be represented in BTF.  For type tags applied to
> pointer types, the BTF_KIND_PTR refers to the TYPE_TAG by ID, and the TYPE_TAG
> refers to the pointee type by ID.
> 
> Example: btf_decl_tag
> =====================
> 
> Consider the following declarations:
> 
>   int  *x __attribute__((btf_decl_tag ("rw"), btf_decl_tag ("devicemem")));
>   struct {
>     int size;
>     char *ptr __attribute__((btf_decl_tag("rw")));
>   } y;
> 
> These declarations produce the following DWARF information:
> 
>  <1><1e>: Abbrev Number: 3 (DW_TAG_variable)
>     <1f>   DW_AT_name        : x
>     <24>   DW_AT_type        : <0x36>
>     <28>   DW_TAG_GNU_annotation: <0x4a>
>     ...
>  <1><36>: Abbrev Number: 1 (DW_TAG_pointer_type)
>     <37>   DW_AT_byte_size   : 8
>     <37>   DW_AT_type        : <0x3b>
>  <1><3b>: Abbrev Number: 4 (DW_TAG_base_type)
>     <3e>   DW_AT_name        : int
>     ...
>  <1><42>: Abbrev Number: 5 (DW_TAG_GNU_annotation)
>     <43>   DW_AT_name        : (indirect string, offset: 0): btf_decl_tag
>     <47>   DW_AT_const_value : rw
>  <1><4a>: Abbrev Number: 6 (DW_TAG_GNU_annotation)
>     <4b>   DW_AT_name        : (indirect string, offset: 0): btf_decl_tag
>     <4f>   DW_AT_const_value : (indirect string, offset: 0x1f): devicemem
>     <53>   DW_AT_GNU_annotation: <0x42>
>  <1><57>: Abbrev Number: 7 (DW_TAG_structure_type)
>     ...
>  <2><60>: Abbrev Number: 8 (DW_TAG_member)
>     <61>   DW_AT_name        : (indirect string, offset: 0x1a): size
>     <68>   DW_AT_type        : <0x3b>
>     ...
>  <2><6d>: Abbrev Number: 9 (DW_TAG_member)
>     <6e>   DW_AT_name        : ptr
>     <75>   DW_AT_type        : <0x7f>
>     <7a>   DW_AT_GNU_annotation: <0x42>
>     ...
>  <2><7e>: Abbrev Number: 0
>  <1><7f>: Abbrev Number: 1 (DW_TAG_pointer_type)
>     <80>   DW_AT_byte_size   : 8
>     <80>   DW_AT_type        : <0x84>
>  <1><84>: Abbrev Number: 10 (DW_TAG_base_type)
>     <85>   DW_AT_byte_size   : 1
>     <86>   DW_AT_encoding    : 6	(signed char)
>     <87>   DW_AT_name        : (indirect string, offset: 0x5e): char
>  <1><8b>: Abbrev Number: 11 (DW_TAG_variable)
>     <8c>   DW_AT_name        : y
>     <91>   DW_AT_type        : <0x57>
>     ...
> 
> The variable DIE for 'x' refers by DW_AT_GNU_annotation to the DIE holding the
> annotation for the "devicemem" tag, which in turn refers to the DIE holding
> the annotation for "rw".  The DW_TAG_member DIE for the member 'ptr' of the
> struct refers to the annotation die for "rw" directly, which is thereby shared
> between the two declarations.
> 
> And BTF information:
> 
>   [1] STRUCT '(anon)' size=16 vlen=2
>       'size' type_id=2 bits_offset=0
>       'ptr' type_id=3 bits_offset=64
>   [2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
>   [3] PTR '(anon)' type_id=4
>   [4] INT 'char' size=1 bits_offset=0 nr_bits=8 encoding=SIGNED
>   [5] PTR '(anon)' type_id=2
>   [6] DECL_TAG 'devicemem' type_id=10 component_idx=-1
>   [7] DECL_TAG 'rw' type_id=10 component_idx=-1
>   [8] DECL_TAG 'rw' type_id=1 component_idx=1
>   [9] VAR 'y' type_id=1, linkage=global
>   [10] VAR 'x' type_id=5, linkage=global
> 
> Note how the component_idx identifies to which member of the struct type the
> decl tag is applied.
> 
> 
> Example: btf_type_tag
> =====================
> 
> Consider the following code snippet:
> 
>   int __attribute__((btf_type_tag("rcu"), btf_type_tag ("foo"))) x;
> 
>   void
>   do_thing (struct S * __attribute__((btf_type_tag ("rcu"))) rcu_s,
>             void * __attribute__((btf_type_tag("foo"))) ptr)
>   { ... }
> 
> The relevant DWARF information produced is as follows:
> 
>  <1><2e>: Abbrev Number: 3 (DW_TAG_structure_type)
>     <2f>   DW_AT_name        : S
>     ...
>  ...
>  <1><4d>: Abbrev Number: 6 (DW_TAG_variable)
>     <4e>   DW_AT_name        : x
>     <53>   DW_AT_type        : <0x61>
>     ...
>  <1><61>: Abbrev Number: 7 (DW_TAG_base_type)
>     <62>   DW_AT_byte_size   : 4
>     <63>   DW_AT_encoding    : 5	(signed)
>     <64>   DW_AT_name        : int
>     <68>   DW_AT_GNU_annotation: <0x75>
>  <1><6c>: Abbrev Number: 1 (DW_TAG_GNU_annotation)
>     <6d>   DW_AT_name        : (indirect string, offset: 0x13): btf_type_tag
>     <71>   DW_AT_const_value : rcu
>  <1><75>: Abbrev Number: 8 (DW_TAG_GNU_annotation)
>     <76>   DW_AT_name        : (indirect string, offset: 0x13): btf_type_tag
>     <7a>   DW_AT_const_value : foo
>     <7e>   DW_AT_GNU_annotation: <0x6c>
>  <1><82>: Abbrev Number: 9 (DW_TAG_subprogram)
>     <83>   DW_AT_name        : (indirect string, offset: 0x20): do_thing
>     ...
>  <2><a1>: Abbrev Number: 10 (DW_TAG_formal_parameter)
>     <a2>   DW_AT_name        : (indirect string, offset: 0x5): rcu_s
>     <a9>   DW_AT_type        : <0xc0>
>     ...
>  <2><b0>: Abbrev Number: 11 (DW_TAG_formal_parameter)
>     <b1>   DW_AT_name        : ptr
>     <b8>   DW_AT_type        : <0xca>
>     ...
>  <2><bf>: Abbrev Number: 0
>  <1><c0>: Abbrev Number: 12 (DW_TAG_pointer_type)
>     <c1>   DW_AT_byte_size   : 8
>     <c2>   DW_AT_type        : <0x2e>
>     <c6>   DW_AT_GNU_annotation: <0x6c>
>  <1><ca>: Abbrev Number: 13 (DW_TAG_pointer_type)
>     <cb>   DW_AT_byte_size   : 8
>     <cc>   DW_AT_GNU_annotation: <0xd0>
>  <1><d0>: Abbrev Number: 1 (DW_TAG_GNU_annotation)
>     <d1>   DW_AT_name        : (indirect string, offset: 0x13): btf_type_tag
>     <d5>   DW_AT_const_value : foo
> 
> Note how in this case, two annotation DIEs for "foo" are produced, because
> it is used in two distinct sets of type tags which do not allow it to be
> shared. The DIE for "rcu", however, is shared between uses.
> 
> And BTF information:
> 
>   [1] STRUCT 'S' size=8 vlen=1
>       ...
>   [2] INT 'long int' size=8 bits_offset=0 nr_bits=64 encoding=SIGNED
>   [3] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
>   [4] FUNC_PROTO '(anon)' ret_type_id=0 vlen=2
>       'rcu_s' type_id=5
>       'ptr' type_id=6
>   [5] PTR '(anon)' type_id=9
>   [6] PTR '(anon)' type_id=10
>   [7] VAR 'x' type_id=3, linkage=global
>   [8] FUNC 'do_thing' type_id=4 linkage=global
>   [9] TYPE_TAG 'rcu' type_id=1
>   [10] TYPE_TAG 'foo' type_id=0
> 
> Note how the TYPE_TAG are injected into the type chain between the PTR
> record and the pointee type, e.g.
> 
>   param 'rcu_s' -> PTR -> TYPE_TAG 'rcu' -> STRUCT 'S'
> 
> Note also that the type tags which apply to the integer type of variable 'x'
> are not represented, since BTF currently has no way to represent type tags
> on non-pointer types.
> 
> References
> ==========
> 
> [1] https://lore.kernel.org/bpf/20210914223004.244411-1-yhs@fb.com/
> [2] https://lore.kernel.org/bpf/20211012164838.3345699-1-yhs@fb.com/
> [3] https://lore.kernel.org/bpf/20211112012604.1504583-1-yhs@fb.com/
> [4] https://gcc.gnu.org/wiki/cauldron2024#cauldron2024talks.what_is_new_in_the_bpf_support_in_the_gnu_toolchain
> [5] https://lpc.events/event/18/contributions/1924/
> [6] https://gcc.gnu.org/pipermail/gcc-patches/2022-April/592685.html
> [7] https://gcc.gnu.org/pipermail/gcc-patches/2022-June/596355.html
> [8] https://gcc.gnu.org/pipermail/gcc-patches/2023-July/624156.html
> 
> 
> David Faust (6):
>   c-family: add btf_type_tag and btf_decl_tag attributes
>   dwarf: create annotation DIEs for btf tags
>   ctf: translate annotation DIEs to internal ctf
>   btf: generate and output DECL_TAG and TYPE_TAG records
>   doc: document btf_type_tag and btf_decl_tag attributes
>   bpf: add tests for CO-RE and BTF tag interaction
> 
>  gcc/btfout.cc                                 | 171 +++++++--
>  gcc/c-family/c-attribs.cc                     | 110 +++++-
>  gcc/ctfc.cc                                   |  80 ++++-
>  gcc/ctfc.h                                    |  43 ++-
>  gcc/doc/extend.texi                           |  79 +++++
>  gcc/dwarf2ctf.cc                              | 135 ++++++-
>  gcc/dwarf2out.cc                              | 328 ++++++++++++++++--
>  gcc/testsuite/gcc.dg/attr-btf-decl-tag-1.c    |  14 +
>  gcc/testsuite/gcc.dg/attr-btf-decl-tag-2.c    |  15 +
>  gcc/testsuite/gcc.dg/attr-btf-type-tag-1.c    |  12 +
>  gcc/testsuite/gcc.dg/attr-btf-type-tag-2.c    |   9 +
>  gcc/testsuite/gcc.dg/attr-btf-type-tag-3.c    |   8 +
>  .../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         |  26 ++
>  .../gcc.dg/debug/btf/btf-type-tag-2.c         |  13 +
>  .../gcc.dg/debug/btf/btf-type-tag-3.c         |  28 ++
>  .../gcc.dg/debug/btf/btf-type-tag-4.c         |  24 ++
>  .../gcc.dg/debug/btf/btf-type-tag-c2x-1.c     |  22 ++
>  .../gcc.dg/debug/ctf/ctf-decl-tag-1.c         |  31 ++
>  .../gcc.dg/debug/ctf/ctf-type-tag-1.c         |  19 +
>  .../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      |  20 ++
>  .../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 +++
>  gcc/testsuite/gcc.target/bpf/core-btf-tag-1.c |  23 ++
>  gcc/testsuite/gcc.target/bpf/core-btf-tag-2.c |  23 ++
>  include/btf.h                                 |  14 +
>  include/ctf.h                                 |   4 +
>  include/dwarf2.def                            |   4 +
>  41 files changed, 1555 insertions(+), 65 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/attr-btf-decl-tag-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/attr-btf-decl-tag-2.c
>  create mode 100644 gcc/testsuite/gcc.dg/attr-btf-type-tag-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/attr-btf-type-tag-2.c
>  create mode 100644 gcc/testsuite/gcc.dg/attr-btf-type-tag-3.c
>  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-c2x-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/debug/ctf/ctf-decl-tag-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/debug/ctf/ctf-type-tag-1.c
>  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
>  create mode 100644 gcc/testsuite/gcc.target/bpf/core-btf-tag-1.c
>  create mode 100644 gcc/testsuite/gcc.target/bpf/core-btf-tag-2.c
> 


^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v11 2/6] dwarf: create annotation DIEs for btf tags
  2025-09-27  9:06 ` [PATCH v11 2/6] dwarf: create annotation DIEs for btf tags David Faust
@ 2025-10-16 16:05   ` Tom Tromey
  2025-10-16 17:32     ` David Faust
  0 siblings, 1 reply; 11+ messages in thread
From: Tom Tromey @ 2025-10-16 16:05 UTC (permalink / raw)
  To: David Faust
  Cc: gcc-patches, josmyers, rguenther, jose.marchesi, indu.bhagat,
	yonghong.song

>>>>> "David" == David Faust <david.faust@oracle.com> writes:

I was comparing the GCC and GDB copies of dwarf2.def today and ran
across this patch.

David> DW_AT_GNU_annotation is a new attribute extension used to refer to these
David> new annotation DIEs.  If non-null in any given declaration or type DIE,
David> it is a reference to a DW_TAG_GNU_annotation DIE holding an annotation
David> for that declaration or type.  In addition, the DW_TAG_GNU_annotation
David> DIEs may also have a non-null DW_AT_GNU_annotation, referring to another
David> annotation DIE.

David> +DW_TAG (DW_TAG_GNU_annotation, 0x6001)

It seems strange that this was given this value and not something closer
to the other GNU extension tag values.

However, that's a minor thing.  The real reason I am writing is that I
think all DWARF extensions should come with documentation.  In the old
days this wasn't done and it's proved to be a pain for gdb.

The normal thing to do is write a page on the GCC wiki that explains the
extension, then add a link from the .def file to that page.

Furthermore I think requiring documentation for GNU DWARF extensions
should be part of the dwarf2.{def,h} review checklist.

thanks,
Tom

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v11 2/6] dwarf: create annotation DIEs for btf tags
  2025-10-16 16:05   ` Tom Tromey
@ 2025-10-16 17:32     ` David Faust
  0 siblings, 0 replies; 11+ messages in thread
From: David Faust @ 2025-10-16 17:32 UTC (permalink / raw)
  To: Tom Tromey
  Cc: gcc-patches, josmyers, rguenther, jose.marchesi, indu.bhagat,
	yonghong.song

Hi Tom,

On 10/16/25 09:05, Tom Tromey wrote:
>>>>>> "David" == David Faust <david.faust@oracle.com> writes:
> 
> I was comparing the GCC and GDB copies of dwarf2.def today and ran
> across this patch.
> 
> David> DW_AT_GNU_annotation is a new attribute extension used to refer to these
> David> new annotation DIEs.  If non-null in any given declaration or type DIE,
> David> it is a reference to a DW_TAG_GNU_annotation DIE holding an annotation
> David> for that declaration or type.  In addition, the DW_TAG_GNU_annotation
> David> DIEs may also have a non-null DW_AT_GNU_annotation, referring to another
> David> annotation DIE.
> 
> David> +DW_TAG (DW_TAG_GNU_annotation, 0x6001)
> 
> It seems strange that this was given this value and not something closer
> to the other GNU extension tag values.

For a little bit of context, conceptually the DWARF was initially designed
and implemented in LLVM with the value 0x6000, and we were implementing the
same.  But, their DWARF format was found to be lacking during early reviews
of prior iterations of this series, and we diverged from it slightly.
That is why I chose 0x6001, but there wasn't really particular discussion
on it.

> 
> However, that's a minor thing.  The real reason I am writing is that I
> think all DWARF extensions should come with documentation.  In the old
> days this wasn't done and it's proved to be a pain for gdb.
> 
> The normal thing to do is write a page on the GCC wiki that explains the
> extension, then add a link from the .def file to that page.

Sorry for forgetting this, and thanks for raising the issue.

I have just created a wiki page here:

  https://gcc.gnu.org/wiki/GNUAnnotationsDwarf

The initial contents are an adaptation of this patch series cover
letter, aiming to describe the purpose of the new C attributes and the
DWARF representation (both TAG_GNU_annotation and AT_GNU_annotation).

I will update dwarf2.def with a link to the wiki page shortly.

Please let me know if there is more info that would be useful there
or if it can otherwise be improved.

> 
> Furthermore I think requiring documentation for GNU DWARF extensions
> should be part of the dwarf2.{def,h} review checklist.
> 
> thanks,
> Tom

Thanks,
David

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2025-10-16 17:32 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-09-27  9:06 [PATCH v11 0/6] c,dwarf,btf: Add btf_decl_tag and btf_type_tag C attributes David Faust
2025-09-27  9:06 ` [PATCH v11 1/6] c-family: add btf_type_tag and btf_decl_tag attributes David Faust
2025-09-27  9:06 ` [PATCH v11 2/6] dwarf: create annotation DIEs for btf tags David Faust
2025-10-16 16:05   ` Tom Tromey
2025-10-16 17:32     ` David Faust
2025-09-27  9:06 ` [PATCH v11 3/6] ctf: translate annotation DIEs to internal ctf David Faust
2025-09-27  9:06 ` [PATCH v11 4/6] btf: generate and output DECL_TAG and TYPE_TAG records David Faust
2025-09-27  9:07 ` [PATCH v11 5/6] doc: document btf_type_tag and btf_decl_tag attributes David Faust
2025-09-27  9:07 ` [PATCH v11 6/6] bpf: add tests for CO-RE and BTF tag interaction David Faust
2025-09-27 10:30 ` [PATCH v11 0/6] c, dwarf, btf: Add btf_decl_tag and btf_type_tag C attributes David Faust
2025-10-06 17:55 ` David Faust

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).