public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH v3 0/6] btf: refactor and add pruning option
@ 2024-05-30 21:32 David Faust
  2024-05-30 21:32 ` [PATCH v3 1/6] ctf, btf: restructure CTF/BTF emission David Faust
                   ` (5 more replies)
  0 siblings, 6 replies; 16+ messages in thread
From: David Faust @ 2024-05-30 21:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: indu.bhagat, jose.marchesi, cupertino.miranda

[v2: https://gcc.gnu.org/pipermail/gcc-patches/2024-May/650482.html
 Changes from v2:
 - Handle -flto when generating BTF (in patch 1).  For LTO builds, BTF
   is emitted at early_finish as before.  For non-LTO builds, BTF will
   be emitted at (late) finish for all targets.
 - Move the option parsing change to allow -gbtf and -gctf together to
   the end of the series.  This patch is relatively separate from the
   other changes, and only depends upon patch 1.
 - Include Indu's various comments and suggestions throughout the
   series.
 - Fix a few GNU style issues with indentation and long lines.
 - Fix the special handling for .maps section variables in BTF in
   patch 4.  Previously the type marking was too aggressive, and undid
   nearly all the pruning in some kernel selftests.  In one test this
   fix reduced the emitted BTF from over 6000 types to about 200 types,
   very similar to clang's output for the same test.  ]

This patch series signficantly refactors the BTF generation in gcc,
making it simpler and easier to understand, extend and maintain.

It also introduces an optional algorithm to "prune" BTF information
before emission.  This pruning is meant to be used for BPF programs, to
alleviate the massive bloating of BTF information caused by including
Linux kernel internal headers.  The pruning is designed to be compatible
with the unconditional pruning performed  by the LLVM BPF backend when
generating BTF information.

While the changes are fairly significant, there is no actual change in
emitted BTF information (unless pruning is enabled), other than bug
fixes and small additions to the assembler debug comments.

Patch 1 restructures the emission of CTF and BTF information, with the
result that CTF is always completely generated and emitted before any
BTF-related procedures are run.  BTF emission is moved to late finish
for all targets, except when building with -flto.

Patch 2 changes the data structures shared by CTF and BTF to use
pointers rather than type IDs for all inter-type references.  This
change is completely transparent to both CTF and BTF.

Patch 3 heavily refactors btfout.cc to take advantage of the prior
changes and significantly simplify the BTF implementation.  The changes
are nearly transparent, however some small but important improvements
are also made possible by the refactor, such as fixing PR113566 for
non-LTO builds.

Patch 4 adds a new option to perform pruning of the BTF information
before emission.  This is intended to be used for BPF programs which
often include kernel headers, and in many cases reduces the size of
the resulting BTF information by a factor of 10.

Patch 5 makes BTF pruning work with BPF CO-RE, and enables the pruning
by default in the BPF backend.

Patch 6 takes advantage of the prior changes, and removes the
restriction on generating both CTF and BTF in the same compiler run,
allowing for any combinaion of -gdwarf, -gctf and -gbtf.

Tested on x86_64-linux-gnu, and on x86_64-linux-gnu host for
bpf-unknown-none target.

Also tested by compiling and runninng Linux kernel BPF selftests.
No known regressions.

David Faust (6):
  ctf, btf: restructure CTF/BTF emission
  ctf: use pointers instead of IDs internally
  btf: refactor and simplify implementation
  btf: add -fprune-btf option
  bpf,btf: enable BTF pruning by default for BPF
  opts: allow any combination of DWARF, CTF, BTF

 gcc/btfout.cc                                 | 1613 +++++++++--------
 gcc/common.opt                                |    4 +
 gcc/config/bpf/bpf.cc                         |    5 +
 gcc/config/bpf/btfext-out.cc                  |   14 +-
 gcc/config/bpf/core-builtins.cc               |   74 +-
 gcc/ctfc.cc                                   |  141 +-
 gcc/ctfc.h                                    |  113 +-
 gcc/ctfout.cc                                 |   22 +-
 gcc/doc/invoke.texi                           |   23 +
 gcc/dwarf2ctf.cc                              |  311 ++--
 gcc/dwarf2ctf.h                               |    2 +-
 gcc/dwarf2out.cc                              |    4 +-
 gcc/opts.cc                                   |   20 +-
 gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c  |   25 +
 gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c  |   33 +
 gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c  |   35 +
 .../gcc.dg/debug/btf/btf-prune-maps.c         |   20 +
 .../gcc.dg/debug/btf/btf-variables-5.c        |    6 +-
 include/btf.h                                 |    5 +
 19 files changed, 1420 insertions(+), 1050 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c
 create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c
 create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c
 create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c

-- 
2.43.0


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

* [PATCH v3 1/6] ctf, btf: restructure CTF/BTF emission
  2024-05-30 21:32 [PATCH v3 0/6] btf: refactor and add pruning option David Faust
@ 2024-05-30 21:32 ` David Faust
  2024-06-05  7:27   ` Indu Bhagat
  2024-05-30 21:32 ` [PATCH v3 2/6] ctf: use pointers instead of IDs internally David Faust
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 16+ messages in thread
From: David Faust @ 2024-05-30 21:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: indu.bhagat, jose.marchesi, cupertino.miranda

This commit makes some structural changes to the CTF/BTF debug info
emission.  In particular:

 a) CTF is new always fully generated and emitted before any
    BTF-related procedures are run.  This means that BTF-related
    functions can change, even irreversibly, the shared in-memory
    representation used by the two formats without issue.

 b) BTF generation has fewer entry points, and is cleanly divided
    into early_finish and finish.

 c) BTF is now always emitted at finish (called from dwarf2out_finish),
    for all targets in non-LTO builds, rather than being emitted at
    early_finish for targets other than BPF CO-RE.  In LTO builds,
    BTF is emitted at early_finish as before.

    Note that this change alone does not alter the contents of BTF at
    all, regardless of whether it would have previously been emitted at
    early_finish or finish, because the calculation of the BTF to be
    emitted is not moved by this patch, only the write-out.

The changes are transparent to both CTF and BTF emission.

gcc/
	* btfout.cc (btf_init_postprocess): Rename to...
	(btf_early_finish): ...this.
	(btf_output): Rename to...
	(btf_finish): ...this.
	* ctfc.h: Analogous changes.
	* dwarf2ctf.cc (ctf_debug_early_finish): Conditionally call
	btf_early_finish, or ctf_finalize as appropriate.  Emit BTF
	here for LTO builds.
	(ctf_debug_finish): Always call btf_finish here if generating
	BTF info in non-LTO builds.
	(ctf_debug_finalize, ctf_debug_init_postprocess): Delete.
	* dwarf2out.cc (dwarf2out_early_finish): Remove call to
	ctf_debug_init_postprocess.
---
 gcc/btfout.cc    | 28 +++++++++++++++++++++
 gcc/ctfc.h       |  4 +--
 gcc/dwarf2ctf.cc | 65 +++++++++++++++---------------------------------
 gcc/dwarf2out.cc |  2 --
 4 files changed, 50 insertions(+), 49 deletions(-)

diff --git a/gcc/btfout.cc b/gcc/btfout.cc
index 07f066a47068..1b6a9ed811f0 100644
--- a/gcc/btfout.cc
+++ b/gcc/btfout.cc
@@ -1491,6 +1491,34 @@ btf_finalize (void)
   tu_ctfc = NULL;
 }
 
+/* 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
+   BTF_KIND_FUNC records.  */
+
+void
+btf_early_finish (void)
+{
+  btf_init_postprocess ();
+}
+
+/* Late entry point for BTF generation, called from dwarf2out_finish ().
+   Complete and emit BTF information.  */
+
+void
+btf_finish (const char * filename)
+{
+  btf_output (filename);
+
+  /* If compiling for BPF with CO-RE info, we cannot deallocate until after
+     CO-RE information is created, which happens very late in BPF backend.
+     Therefore, the deallocation (i.e. btf_finalize ()) is delayed until
+     TARGET_ASM_FILE_END for BPF CO-RE.  */
+  if (!btf_with_core_debuginfo_p ())
+    btf_finalize ();
+}
+
+
 /* Traversal function for all BTF_KIND_FUNC type records.  */
 
 bool
diff --git a/gcc/ctfc.h b/gcc/ctfc.h
index fa188bf2f5a4..e7bd93901cfa 100644
--- a/gcc/ctfc.h
+++ b/gcc/ctfc.h
@@ -384,8 +384,8 @@ extern void ctf_init (void);
 extern void ctf_output (const char * filename);
 extern void ctf_finalize (void);
 
-extern void btf_output (const char * filename);
-extern void btf_init_postprocess (void);
+extern void btf_early_finish (void);
+extern void btf_finish (const char * filename);
 extern void btf_finalize (void);
 
 extern ctf_container_ref ctf_get_tu_ctfc (void);
diff --git a/gcc/dwarf2ctf.cc b/gcc/dwarf2ctf.cc
index dc59569fe560..8f9e2fada9e3 100644
--- a/gcc/dwarf2ctf.cc
+++ b/gcc/dwarf2ctf.cc
@@ -933,30 +933,6 @@ gen_ctf_type (ctf_container_ref ctfc, dw_die_ref die)
   return type_id;
 }
 
-/* Prepare for output and write out the CTF debug information.  */
-
-static void
-ctf_debug_finalize (const char *filename, bool btf)
-{
-  if (btf)
-    {
-      btf_output (filename);
-      /* btf_finalize when compiling BPF applciations gets deallocated by the
-	 BPF target in bpf_file_end.  */
-      if (btf_debuginfo_p () && !btf_with_core_debuginfo_p ())
-	btf_finalize ();
-    }
-
-  else
-    {
-      /* Emit the collected CTF information.  */
-      ctf_output (filename);
-
-      /* Reset the CTF state.  */
-      ctf_finalize ();
-    }
-}
-
 bool
 ctf_do_die (dw_die_ref die)
 {
@@ -996,27 +972,27 @@ ctf_debug_init (void)
   add_name_attribute (ctf_unknown_die, "unknown");
 }
 
-/* Preprocess the CTF debug information after initialization.  */
-
-void
-ctf_debug_init_postprocess (bool btf)
-{
-  /* Only BTF requires postprocessing right after init.  */
-  if (btf)
-    btf_init_postprocess ();
-}
-
 /* Early finish CTF/BTF debug info.  */
 
 void
 ctf_debug_early_finish (const char * filename)
 {
-  /* Emit CTF debug info early always.  */
-  if (ctf_debug_info_level > CTFINFO_LEVEL_NONE
-      /* Emit BTF debug info early if CO-RE relocations are not
-	 required.  */
-      || (btf_debuginfo_p () && !btf_with_core_debuginfo_p ()))
-    ctf_debug_finalize (filename, btf_debuginfo_p ());
+  /* Emit the collected CTF information.  */
+  if (ctf_debug_info_level > CTFINFO_LEVEL_NONE)
+    ctf_output (filename);
+
+  /* If emitting BTF, start translation to BTF.  */
+  if (btf_debuginfo_p ())
+    {
+      btf_early_finish ();
+
+      /* For LTO builds, also emit BTF now.  */
+      if (flag_lto && !in_lto_p)
+	btf_finish (filename);
+    }
+  else
+    /* Otherwise, done with the CTF container.  */
+    ctf_finalize ();
 }
 
 /* Finish CTF/BTF debug info emission.  */
@@ -1024,11 +1000,10 @@ ctf_debug_early_finish (const char * filename)
 void
 ctf_debug_finish (const char * filename)
 {
-  /* Emit BTF debug info here when CO-RE relocations need to be generated.
-     BTF with CO-RE relocations needs to be generated when CO-RE is in effect
-     for the BPF target.  */
-  if (btf_debuginfo_p () && btf_with_core_debuginfo_p ())
-    ctf_debug_finalize (filename, btf_debuginfo_p ());
+  /* Emit BTF late, unless this is an LTO build in which case it was
+     already done early.  */
+  if (btf_debuginfo_p () && !flag_lto)
+    btf_finish (filename);
 }
 
 #include "gt-dwarf2ctf.h"
diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
index 5b064ffd78ad..e406df8673ce 100644
--- a/gcc/dwarf2out.cc
+++ b/gcc/dwarf2out.cc
@@ -33202,8 +33202,6 @@ dwarf2out_early_finish (const char *filename)
       ctf_debug_do_cu (comp_unit_die ());
       for (limbo_die_node *node = limbo_die_list; node; node = node->next)
 	ctf_debug_do_cu (node->die);
-      /* Post process the debug data in the CTF container if necessary.  */
-      ctf_debug_init_postprocess (btf_debuginfo_p ());
 
       ctf_debug_early_finish (filename);
     }
-- 
2.43.0


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

* [PATCH v3 2/6] ctf: use pointers instead of IDs internally
  2024-05-30 21:32 [PATCH v3 0/6] btf: refactor and add pruning option David Faust
  2024-05-30 21:32 ` [PATCH v3 1/6] ctf, btf: restructure CTF/BTF emission David Faust
@ 2024-05-30 21:32 ` David Faust
  2024-06-05  7:31   ` Indu Bhagat
  2024-05-30 21:32 ` [PATCH v3 3/6] btf: refactor and simplify implementation David Faust
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 16+ messages in thread
From: David Faust @ 2024-05-30 21:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: indu.bhagat, jose.marchesi, cupertino.miranda

This patch replaces all inter-type references in the ctfc internal data
structures with pointers, rather than the references-by-ID which were
used previously.

A couple of small updates in the BPF backend are included to make it
compatible with the change.

This change is only to the in-memory representation of various CTF
structures to make them easier to work with in various cases.  It is
outwardly transparent; there is no change in emitted CTF.

gcc/
	* btfout.cc (BTF_VOID_TYPEID, BTF_INIT_TYPEID): Move defines to
	include/btf.h.
	(btf_dvd_emit_preprocess_cb, btf_emit_preprocess)
	(btf_dmd_representable_bitfield_p, btf_asm_array, btf_asm_varent)
	(btf_asm_sou_member, btf_asm_func_arg, btf_init_postprocess):
	Adapt to structural changes in ctf_* structs.
	* ctfc.h (struct ctf_dtdef): Add forward declaration.
	(ctf_dtdef_t, ctf_dtdef_ref): Move typedefs earlier.
	(struct ctf_arinfo, struct ctf_funcinfo, struct ctf_sliceinfo)
	(struct ctf_itype, struct ctf_dmdef, struct ctf_func_arg)
	(struct ctf_dvdef): Use pointers instead of type IDs for
	references to other types and use typedefs where appropriate.
	(struct ctf_dtdef): Add ref_type member.
	(ctf_type_exists): Use pointer instead of type ID.
	(ctf_add_reftype, ctf_add_enum, ctf_add_slice, ctf_add_float)
	(ctf_add_integer, ctf_add_unknown, ctf_add_pointer)
	(ctf_add_array, ctf_add_forward, ctf_add_typedef)
	(ctf_add_function, ctf_add_sou, ctf_add_enumerator)
	(ctf_add_variable): Likewise. Return pointer instead of ID.
	(ctf_lookup_tree_type): Return pointer to type instead of ID.
	* ctfc.cc: Analogous changes.
	* ctfout.cc (ctf_asm_type, ctf_asm_slice, ctf_asm_varent)
	(ctf_asm_sou_lmember, ctf_asm_sou_member, ctf_asm_func_arg)
	(output_ctf_objt_info): Adapt to changes.
	* dwarf2ctf.cc (gen_ctf_type, gen_ctf_void_type)
	(gen_ctf_unknown_type, gen_ctf_base_type, gen_ctf_pointer_type)
	(gen_ctf_subrange_type, gen_ctf_array_type, gen_ctf_typedef)
	(gen_ctf_modifier_type, gen_ctf_sou_type, gen_ctf_function_type)
	(gen_ctf_enumeration_type, gen_ctf_variable, gen_ctf_function)
	(gen_ctf_type, ctf_do_die): Likewise.
	* config/bpf/btfext-out.cc (struct btf_ext_core_reloc): Use
	pointer instead of type ID.
	(bpf_core_reloc_add, bpf_core_get_sou_member_index)
	(output_btfext_core_sections): Adapt to above changes.
	* config/bpf/core-builtins.cc (process_type): Likewise.

include/
	* btf.h (BTF_VOID_TYPEID, BTF_INIT_TYPEID): Move defines here,
	from gcc/btfout.cc.
---
 gcc/btfout.cc                   |  40 +++---
 gcc/config/bpf/btfext-out.cc    |  14 +-
 gcc/config/bpf/core-builtins.cc |   3 +-
 gcc/ctfc.cc                     | 139 +++++++++---------
 gcc/ctfc.h                      |  90 ++++++------
 gcc/ctfout.cc                   |  22 +--
 gcc/dwarf2ctf.cc                | 244 +++++++++++++++-----------------
 include/btf.h                   |   5 +
 8 files changed, 278 insertions(+), 279 deletions(-)

diff --git a/gcc/btfout.cc b/gcc/btfout.cc
index 1b6a9ed811f0..40e8d8c5c01b 100644
--- a/gcc/btfout.cc
+++ b/gcc/btfout.cc
@@ -61,11 +61,6 @@ static char btf_info_section_label[MAX_BTF_LABEL_BYTES];
 #define BTF_INFO_SECTION_LABEL  "Lbtf"
 #endif
 
-/* BTF encodes void as type id 0.  */
-
-#define BTF_VOID_TYPEID 0
-#define BTF_INIT_TYPEID 1
-
 #define BTF_INVALID_TYPEID 0xFFFFFFFF
 
 /* Mapping of CTF variables to the IDs they will be assigned when they are
@@ -626,7 +621,8 @@ btf_dvd_emit_preprocess_cb (ctf_dvdef_ref *slot, ctf_container_ref arg_ctfc)
     return 1;
 
   /* Do not add variables which refer to unsupported types.  */
-  if (!voids.contains (var->dvd_type) && btf_removed_type_p (var->dvd_type))
+  if (!voids.contains (var->dvd_type->dtd_type)
+      && btf_removed_type_p (var->dvd_type->dtd_type))
     return 1;
 
   arg_ctfc->ctfc_vars_list[num_vars_added] = var;
@@ -716,7 +712,7 @@ btf_emit_preprocess (ctf_container_ref ctfc)
 static bool
 btf_dmd_representable_bitfield_p (ctf_container_ref ctfc, ctf_dmdef_t *dmd)
 {
-  ctf_dtdef_ref ref_type = ctfc->ctfc_types_list[dmd->dmd_type];
+  ctf_dtdef_ref ref_type = ctfc->ctfc_types_list[dmd->dmd_type->dtd_type];
 
   if (CTF_V2_INFO_KIND (ref_type->dtd_data.ctti_info) == CTF_K_SLICE)
     {
@@ -913,8 +909,8 @@ btf_asm_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
 static void
 btf_asm_array (ctf_container_ref ctfc, ctf_arinfo_t arr)
 {
-  btf_asm_type_ref ("bta_elem_type", ctfc, arr.ctr_contents);
-  btf_asm_type_ref ("bta_index_type", ctfc, arr.ctr_index);
+  btf_asm_type_ref ("bta_elem_type", ctfc, arr.ctr_contents->dtd_type);
+  btf_asm_type_ref ("bta_index_type", ctfc, arr.ctr_index->dtd_type);
   dw2_asm_output_data (4, arr.ctr_nelems, "bta_nelems");
 }
 
@@ -927,7 +923,7 @@ btf_asm_varent (ctf_container_ref ctfc, ctf_dvdef_ref var)
 		       (*(btf_var_ids->get (var)) + num_types_added + 1),
 		       var->dvd_name);
   dw2_asm_output_data (4, BTF_TYPE_INFO (BTF_KIND_VAR, 0, 0), "btv_info");
-  btf_asm_type_ref ("btv_type", ctfc, var->dvd_type);
+  btf_asm_type_ref ("btv_type", ctfc, var->dvd_type->dtd_type);
   dw2_asm_output_data (4, var->dvd_visibility, "btv_linkage");
 }
 
@@ -937,8 +933,8 @@ btf_asm_varent (ctf_container_ref ctfc, ctf_dvdef_ref var)
 static void
 btf_asm_sou_member (ctf_container_ref ctfc, ctf_dmdef_t * dmd, unsigned int idx)
 {
-  ctf_dtdef_ref ref_type = ctfc->ctfc_types_list[dmd->dmd_type];
-  ctf_id_t base_type = dmd->dmd_type;
+  ctf_dtdef_ref ref_type = ctfc->ctfc_types_list[dmd->dmd_type->dtd_type];
+  ctf_id_t base_type = dmd->dmd_type->dtd_type;
   uint64_t sou_offset = dmd->dmd_offset;
 
   dw2_asm_output_data (4, dmd->dmd_name_offset,
@@ -959,7 +955,7 @@ btf_asm_sou_member (ctf_container_ref ctfc, ctf_dmdef_t * dmd, unsigned int idx)
 	  sou_offset |= ((bits & 0xff) << 24);
 
 	  /* Refer to the base type of the slice.  */
-	  base_type = ref_type->dtd_u.dtu_slice.cts_type;
+	  base_type = ref_type->dtd_u.dtu_slice.cts_type->dtd_type;
 	}
       else
 	{
@@ -1003,9 +999,11 @@ btf_asm_func_arg (ctf_container_ref ctfc, ctf_func_arg_t * farg,
   else
     dw2_asm_output_data (4, 0, "farg_name");
 
-  btf_asm_type_ref ("farg_type", ctfc, (btf_removed_type_p (farg->farg_type)
-					? BTF_VOID_TYPEID
-					: farg->farg_type));
+  ctf_id_t ref_id = BTF_VOID_TYPEID;
+  if (farg->farg_type && !btf_removed_type_p (farg->farg_type->dtd_type))
+    ref_id = farg->farg_type->dtd_type;
+
+  btf_asm_type_ref ("farg_type", ctfc, ref_id);
 }
 
 /* Asm'out a BTF_KIND_FUNC type.  */
@@ -1381,7 +1379,7 @@ btf_init_postprocess (void)
      to create the const modifier type (if needed) now, before making the types
      list.  So we can't avoid iterating with FOR_EACH_VARIABLE here, and then
      again when creating the DATASEC entries.  */
-  ctf_id_t constvoid_id = CTF_NULL_TYPEID;
+  ctf_dtdef_ref constvoid_dtd = NULL;
   varpool_node *var;
   FOR_EACH_VARIABLE (var)
     {
@@ -1400,10 +1398,10 @@ btf_init_postprocess (void)
 	    continue;
 
 	  /* Create the 'const' modifier type for void.  */
-	  if (constvoid_id == CTF_NULL_TYPEID)
-	    constvoid_id = ctf_add_reftype (tu_ctfc, CTF_ADD_ROOT,
-					    dvd->dvd_type, CTF_K_CONST, NULL);
-	  dvd->dvd_type = constvoid_id;
+	  if (constvoid_dtd == NULL)
+	    constvoid_dtd = ctf_add_reftype (tu_ctfc, CTF_ADD_ROOT,
+					     dvd->dvd_type, CTF_K_CONST, NULL);
+	  dvd->dvd_type = constvoid_dtd;
 	}
     }
 
diff --git a/gcc/config/bpf/btfext-out.cc b/gcc/config/bpf/btfext-out.cc
index 7ec438fd1d10..b3df7b555d5f 100644
--- a/gcc/config/bpf/btfext-out.cc
+++ b/gcc/config/bpf/btfext-out.cc
@@ -134,7 +134,7 @@ struct GTY ((chain_next ("%h.next"))) btf_ext_lineinfo
 
 /* Internal representation of a BPF CO-RE relocation record.  */
 struct GTY ((chain_next ("%h.next"))) btf_ext_core_reloc {
-  unsigned int bpfcr_type;		/* BTF type ID of container.  */
+  ctf_dtdef_ref bpfcr_type;		/* BTF type involved in relocation.  */
   unsigned int  bpfcr_astr_off;		/* Offset of access string in .BTF
 					   string table.  */
   rtx_code_label * bpfcr_insn_label;	/* RTX label attached to instruction
@@ -296,13 +296,14 @@ bpf_core_reloc_add (const tree type, const char * section_name,
   struct btf_ext_core_reloc *bpfcr = bpf_create_core_reloc (section_name, &sec);
 
   ctf_container_ref ctfc = ctf_get_tu_ctfc ();
+  ctf_dtdef_ref dtd = ctf_lookup_tree_type (ctfc, type);
 
   /* Buffer the access string in the auxiliary strtab.  */
   bpfcr->bpfcr_astr_off = 0;
   gcc_assert (accessor != NULL);
   bpfcr->bpfcr_astr_off = btf_ext_add_string (accessor);
 
-  bpfcr->bpfcr_type = get_btf_id (ctf_lookup_tree_type (ctfc, type));
+  bpfcr->bpfcr_type = dtd;
   bpfcr->bpfcr_insn_label = label;
   bpfcr->bpfcr_kind = kind;
 
@@ -341,7 +342,8 @@ bpf_core_get_sou_member_index (ctf_container_ref ctfc, const tree node)
       for (dmd = dtd->dtd_u.dtu_members;
            dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
         {
-	  bool field_has_btf = get_btf_id (dmd->dmd_type) <= BTF_MAX_TYPE;
+	  bool field_has_btf = (dmd->dmd_type
+				&& dmd->dmd_type->dtd_type <= BTF_MAX_TYPE);
 
 	  if (field == node)
 	    return field_has_btf ? i : -1;
@@ -574,8 +576,10 @@ output_btfext_core_sections (void)
 				 false);
 	      char *str = xstrdup (pp_formatted_text (&pp));
 
-	      dw2_asm_output_data (4, bpfcr->bpfcr_type, "bpfcr_type (%s)",
-				   str);
+	      uint32_t type_id = bpfcr->bpfcr_type
+		? bpfcr->bpfcr_type->dtd_type
+		: BTF_VOID_TYPEID;
+	      dw2_asm_output_data (4, type_id, "bpfcr_type (%s)", str);
 	      dw2_asm_output_data (4, bpfcr->bpfcr_astr_off + str_aux_off,
 				   "bpfcr_astr_off (\"%s\")",
 				   bpfcr->info.accessor_str);
diff --git a/gcc/config/bpf/core-builtins.cc b/gcc/config/bpf/core-builtins.cc
index 829acea98f79..232bebcadbd5 100644
--- a/gcc/config/bpf/core-builtins.cc
+++ b/gcc/config/bpf/core-builtins.cc
@@ -1021,7 +1021,8 @@ process_type (struct cr_builtins *data)
       && data->default_value != NULL)
   {
     ctf_container_ref ctfc = ctf_get_tu_ctfc ();
-    unsigned int btf_id = get_btf_id (ctf_lookup_tree_type (ctfc, ret.type));
+    ctf_dtdef_ref dtd = ctf_lookup_tree_type (ctfc, ret.type);
+    unsigned int btf_id = dtd ? dtd->dtd_type : BTF_VOID_TYPEID;
     data->rtx_default_value = expand_normal (build_int_cst (integer_type_node,
 							    btf_id));
   }
diff --git a/gcc/ctfc.cc b/gcc/ctfc.cc
index 67711606ab85..678ca2a072cf 100644
--- a/gcc/ctfc.cc
+++ b/gcc/ctfc.cc
@@ -373,9 +373,9 @@ ctf_add_cuname (ctf_container_ref ctfc, const char * filename)
    ctf_dvd_lookup, as applicable, to ascertain that the CTF type or the CTF
    variable respectively does not already exist, and then add it.  */
 
-static ctf_id_t
+static ctf_dtdef_ref
 ctf_add_generic (ctf_container_ref ctfc, uint32_t flag, const char * name,
-		 ctf_dtdef_ref * rp, dw_die_ref die)
+		 dw_die_ref die)
 {
   ctf_dtdef_ref dtd;
   ctf_id_t type;
@@ -397,18 +397,16 @@ ctf_add_generic (ctf_container_ref ctfc, uint32_t flag, const char * name,
 
   ctf_dtd_insert (ctfc, dtd);
 
-  *rp = dtd;
-  return type;
+  return dtd;
 }
 
-static ctf_id_t
+static ctf_dtdef_ref
 ctf_add_encoded (ctf_container_ref ctfc, uint32_t flag, const char * name,
 		 const ctf_encoding_t * ep, uint32_t kind, dw_die_ref die)
 {
   ctf_dtdef_ref dtd;
-  ctf_id_t type;
 
-  type = ctf_add_generic (ctfc, flag, name, &dtd, die);
+  dtd = ctf_add_generic (ctfc, flag, name, die);
 
   dtd->dtd_data.ctti_info = CTF_TYPE_INFO (kind, flag, 0);
 
@@ -424,83 +422,81 @@ ctf_add_encoded (ctf_container_ref ctfc, uint32_t flag, const char * name,
 
   ctfc->ctfc_num_stypes++;
 
-  return type;
+  return dtd;
 }
 
-ctf_id_t
-ctf_add_reftype (ctf_container_ref ctfc, uint32_t flag, ctf_id_t ref,
+ctf_dtdef_ref
+ctf_add_reftype (ctf_container_ref ctfc, uint32_t flag, ctf_dtdef_ref ref,
 		 uint32_t kind, dw_die_ref die)
 {
   ctf_dtdef_ref dtd;
-  ctf_id_t type;
 
-  gcc_assert (ref <= CTF_MAX_TYPE);
+  gcc_assert (ref != NULL);
 
-  type = ctf_add_generic (ctfc, flag, NULL, &dtd, die);
+  dtd = ctf_add_generic (ctfc, flag, NULL, die);
   dtd->dtd_data.ctti_info = CTF_TYPE_INFO (kind, flag, 0);
   /* Caller of this API must guarantee that a CTF type with id = ref already
      exists.  This will also be validated for us at link-time.  */
-  dtd->dtd_data.ctti_type = (uint32_t) ref;
+  dtd->dtd_data.ctti_type = (uint32_t) ref->dtd_type;
+  dtd->ref_type = ref;
 
   ctfc->ctfc_num_stypes++;
 
-  return type;
+  return dtd;
 }
 
-ctf_id_t
+ctf_dtdef_ref
 ctf_add_forward (ctf_container_ref ctfc, uint32_t flag, const char * name,
 		 uint32_t kind, dw_die_ref die)
 {
   ctf_dtdef_ref dtd;
-  ctf_id_t type = 0;
 
-  type = ctf_add_generic (ctfc, flag, name, &dtd, die);
+  dtd = ctf_add_generic (ctfc, flag, name, die);
 
   dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_FORWARD, flag, 0);
   dtd->dtd_data.ctti_type = kind;
 
   ctfc->ctfc_num_stypes++;
 
-  return type;
+  return dtd;
 }
 
-ctf_id_t
+ctf_dtdef_ref
 ctf_add_typedef (ctf_container_ref ctfc, uint32_t flag, const char * name,
-		 ctf_id_t ref, dw_die_ref die)
+		 ctf_dtdef_ref ref, dw_die_ref die)
 {
   ctf_dtdef_ref dtd;
-  ctf_id_t type;
 
-  gcc_assert (ref <= CTF_MAX_TYPE);
+  gcc_assert (ref != NULL);
   /* Nameless Typedefs are not expected.  */
   gcc_assert ((name != NULL) && strcmp (name, ""));
 
-  type = ctf_add_generic (ctfc, flag, name, &dtd, die);
+  dtd = ctf_add_generic (ctfc, flag, name, die);
   dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_TYPEDEF, flag, 0);
   /* Caller of this API must guarantee that a CTF type with id = ref already
      exists.  This will also be validated for us at link-time.  */
-  dtd->dtd_data.ctti_type = (uint32_t) ref;
+  dtd->dtd_data.ctti_type = (uint32_t) ref->dtd_type;
+  dtd->ref_type = ref;
 
   gcc_assert (dtd->dtd_type != dtd->dtd_data.ctti_type);
 
   ctfc->ctfc_num_stypes++;
 
-  return type;
+  return dtd;
 }
 
-ctf_id_t
-ctf_add_slice (ctf_container_ref ctfc, uint32_t flag, ctf_id_t ref,
+ctf_dtdef_ref
+ctf_add_slice (ctf_container_ref ctfc, uint32_t flag, ctf_dtdef_ref ref,
 	       uint32_t bit_offset, uint32_t bit_size, dw_die_ref die)
 {
   ctf_dtdef_ref dtd;
-  ctf_id_t type;
   uint32_t roundup_nbytes;
 
   gcc_assert ((bit_size <= 255) && (bit_offset <= 255));
 
-  gcc_assert (ref <= CTF_MAX_TYPE);
+  gcc_assert (ref != NULL);
 
-  type = ctf_add_generic (ctfc, flag, NULL, &dtd, die);
+  dtd = ctf_add_generic (ctfc, flag, NULL, die);
 
   dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_SLICE, flag, 0);
 
@@ -514,49 +510,48 @@ ctf_add_slice (ctf_container_ref ctfc, uint32_t flag, ctf_id_t ref,
 
   /* Caller of this API must guarantee that a CTF type with id = ref already
      exists.  This will also be validated for us at link-time.  */
-  dtd->dtd_u.dtu_slice.cts_type = (uint32_t) ref;
+  dtd->dtd_u.dtu_slice.cts_type = ref;
   dtd->dtd_u.dtu_slice.cts_bits = bit_size;
   dtd->dtd_u.dtu_slice.cts_offset = bit_offset;
 
   ctfc->ctfc_num_stypes++;
 
-  return type;
+  return dtd;
 }
 
-ctf_id_t
+ctf_dtdef_ref
 ctf_add_float (ctf_container_ref ctfc, uint32_t flag,
 	       const char * name, const ctf_encoding_t * ep, dw_die_ref die)
 {
   return (ctf_add_encoded (ctfc, flag, name, ep, CTF_K_FLOAT, die));
 }
 
-ctf_id_t
+ctf_dtdef_ref
 ctf_add_integer (ctf_container_ref ctfc, uint32_t flag,
 		 const char * name, const ctf_encoding_t * ep, dw_die_ref die)
 {
   return (ctf_add_encoded (ctfc, flag, name, ep, CTF_K_INTEGER, die));
 }
 
-ctf_id_t
+ctf_dtdef_ref
 ctf_add_unknown (ctf_container_ref ctfc, uint32_t flag,
 		 const char * name, const ctf_encoding_t * ep, dw_die_ref die)
 {
   return (ctf_add_encoded (ctfc, flag, name, ep, CTF_K_UNKNOWN, die));
 }
 
-ctf_id_t
-ctf_add_pointer (ctf_container_ref ctfc, uint32_t flag, ctf_id_t ref,
+ctf_dtdef_ref
+ctf_add_pointer (ctf_container_ref ctfc, uint32_t flag, ctf_dtdef_ref ref,
 		 dw_die_ref die)
 {
   return (ctf_add_reftype (ctfc, flag, ref, CTF_K_POINTER, die));
 }
 
-ctf_id_t
+ctf_dtdef_ref
 ctf_add_array (ctf_container_ref ctfc, uint32_t flag, const ctf_arinfo_t * arp,
 	       dw_die_ref die)
 {
   ctf_dtdef_ref dtd;
-  ctf_id_t type;
 
   gcc_assert (arp);
 
@@ -564,7 +559,7 @@ ctf_add_array (ctf_container_ref ctfc, uint32_t flag, const ctf_arinfo_t * arp,
      arp->ctr_index are already added.  This will also be validated for us at
      link-time.  */
 
-  type = ctf_add_generic (ctfc, flag, NULL, &dtd, die);
+  dtd = ctf_add_generic (ctfc, flag, NULL, die);
 
   dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_ARRAY, flag, 0);
   dtd->dtd_data.ctti_size = 0;
@@ -572,15 +567,14 @@ ctf_add_array (ctf_container_ref ctfc, uint32_t flag, const ctf_arinfo_t * arp,
 
   ctfc->ctfc_num_stypes++;
 
-  return type;
+  return dtd;
 }
 
-ctf_id_t
+ctf_dtdef_ref
 ctf_add_enum (ctf_container_ref ctfc, uint32_t flag, const char * name,
 	      HOST_WIDE_INT size, bool eunsigned, dw_die_ref die)
 {
   ctf_dtdef_ref dtd;
-  ctf_id_t type;
 
   /* In the compiler, no need to handle the case of promoting forwards to
      enums.  This comment is simply to note a divergence from libctf.  */
@@ -595,7 +589,7 @@ ctf_add_enum (ctf_container_ref ctfc, uint32_t flag, const char * name,
 	= CTF_TYPE_INFO (CTF_K_FORWARD, CTF_ADD_NONROOT, 0);
     }
 
-  type = ctf_add_generic (ctfc, flag, name, &dtd, die);
+  dtd = ctf_add_generic (ctfc, flag, name, die);
 
   dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_ENUM, flag, 0);
 
@@ -608,12 +602,12 @@ ctf_add_enum (ctf_container_ref ctfc, uint32_t flag, const char * name,
 
   ctfc->ctfc_num_stypes++;
 
-  return type;
+  return dtd;
 }
 
 int
-ctf_add_enumerator (ctf_container_ref ctfc, ctf_id_t enid, const char * name,
-		    HOST_WIDE_INT value, dw_die_ref die)
+ctf_add_enumerator (ctf_container_ref ctfc, ctf_dtdef_ref enum_dtd,
+		    const char * name, HOST_WIDE_INT value, dw_die_ref die)
 {
   ctf_dmdef_t * dmd;
   uint32_t kind, vlen, root;
@@ -622,7 +616,7 @@ ctf_add_enumerator (ctf_container_ref ctfc, ctf_id_t enid, const char * name,
      addded.  This will also be validated for us at link-time.  */
   ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, die);
   gcc_assert (dtd);
-  gcc_assert (dtd->dtd_type == enid);
+  gcc_assert (dtd == enum_dtd);
   gcc_assert (name);
 
   kind = CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info);
@@ -646,7 +640,7 @@ ctf_add_enumerator (ctf_container_ref ctfc, ctf_id_t enid, const char * name,
 
   /* Buffer the strings in the CTF string table.  */
   dmd->dmd_name = ctf_add_string (ctfc, name, &(dmd->dmd_name_offset));
-  dmd->dmd_type = CTF_NULL_TYPEID;
+  dmd->dmd_type = NULL;
   dmd->dmd_offset = 0;
 
   dmd->dmd_value = value;
@@ -662,7 +656,7 @@ ctf_add_enumerator (ctf_container_ref ctfc, ctf_id_t enid, const char * name,
 
 int
 ctf_add_member_offset (ctf_container_ref ctfc, dw_die_ref sou,
-		       const char * name, ctf_id_t type,
+		       const char * name, ctf_dtdef_ref type,
 		       uint64_t bit_offset)
 {
   ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, sou);
@@ -702,7 +696,7 @@ ctf_add_member_offset (ctf_container_ref ctfc, dw_die_ref sou,
 }
 
 int
-ctf_add_variable (ctf_container_ref ctfc, const char * name, ctf_id_t 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)
 {
@@ -747,16 +741,16 @@ ctf_add_variable (ctf_container_ref ctfc, const char * name, ctf_id_t ref,
 
 int
 ctf_add_function_arg (ctf_container_ref ctfc, dw_die_ref func,
-		      const char * name, ctf_id_t type)
+		      const char * name, ctf_dtdef_ref arg_dtd)
 {
-  ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, func);
+  ctf_dtdef_ref func_dtd = ctf_dtd_lookup (ctfc, func);
   ctf_func_arg_t * farg;
   uint32_t vlen;
 
   /* The function to which argument is being added must already exist.  */
-  gcc_assert (dtd);
+  gcc_assert (func_dtd);
   /* The number of args must have been non-zero.  */
-  vlen = CTF_V2_INFO_VLEN (dtd->dtd_data.ctti_info);
+  vlen = CTF_V2_INFO_VLEN (func_dtd->dtd_data.ctti_info);
   gcc_assert (vlen);
 
   farg = ggc_cleared_alloc<ctf_func_arg_t> ();
@@ -766,9 +760,9 @@ ctf_add_function_arg (ctf_container_ref ctfc, dw_die_ref func,
      these strings to avoid unnecessary bloat in CTF section in CTF V3.  */
   farg->farg_name = ctf_add_string (ctfc, name, &(farg->farg_name_offset),
 				    CTF_AUX_STRTAB);
-  farg->farg_type = type;
+  farg->farg_type = arg_dtd;
 
-  ctf_farg_list_append (&dtd->dtd_u.dtu_argv, farg);
+  ctf_farg_list_append (&func_dtd->dtd_u.dtu_argv, farg);
 
   /* For aux_str, keep ctfc_aux_strlen updated for debugging.  */
   if ((name != NULL) && strcmp (name, ""))
@@ -777,13 +771,12 @@ ctf_add_function_arg (ctf_container_ref ctfc, dw_die_ref func,
   return 0;
 }
 
-ctf_id_t
+ctf_dtdef_ref
 ctf_add_function (ctf_container_ref ctfc, uint32_t flag, const char * name,
 		  const ctf_funcinfo_t * ctc, dw_die_ref die,
 		  bool from_global_func, int linkage)
 {
   ctf_dtdef_ref dtd;
-  ctf_id_t type;
   uint32_t vlen;
 
   gcc_assert (ctc);
@@ -791,27 +784,27 @@ ctf_add_function (ctf_container_ref ctfc, uint32_t flag, const char * name,
   vlen = ctc->ctc_argc;
   gcc_assert (vlen <= CTF_MAX_VLEN);
 
-  type = ctf_add_generic (ctfc, flag, name, &dtd, die);
+  dtd = ctf_add_generic (ctfc, flag, name, die);
 
   dtd->from_global_func = from_global_func;
   dtd->linkage = linkage;
   dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_FUNCTION, flag, vlen);
+  dtd->ref_type = ctc->ctc_return;
   /* Caller must make sure CTF types for ctc->ctc_return are already added.  */
-  dtd->dtd_data.ctti_type = (uint32_t) ctc->ctc_return;
+  dtd->dtd_data.ctti_type = (uint32_t) ctc->ctc_return->dtd_type;
   /* Caller must make sure CTF types for function arguments are already added
      via ctf_add_function_arg () API.  */
 
   ctfc->ctfc_num_stypes++;
 
-  return type;
+  return dtd;
 }
 
-ctf_id_t
+ctf_dtdef_ref
 ctf_add_sou (ctf_container_ref ctfc, uint32_t flag, const char * name,
 	     uint32_t kind, size_t size, dw_die_ref die)
 {
   ctf_dtdef_ref dtd;
-  ctf_id_t type = 0;
 
   gcc_assert ((kind == CTF_K_STRUCT) || (kind == CTF_K_UNION));
 
@@ -828,7 +821,7 @@ ctf_add_sou (ctf_container_ref ctfc, uint32_t flag, const char * name,
 	= CTF_TYPE_INFO (CTF_K_FORWARD, CTF_ADD_NONROOT, 0);
     }
 
-  type = ctf_add_generic (ctfc, flag, name, &dtd, die);
+  dtd = ctf_add_generic (ctfc, flag, name, die);
 
   dtd->dtd_data.ctti_info = CTF_TYPE_INFO (kind, flag, 0);
 
@@ -845,23 +838,23 @@ ctf_add_sou (ctf_container_ref ctfc, uint32_t flag, const char * name,
       ctfc->ctfc_num_stypes++;
     }
 
-  return type;
+  return dtd;
 }
 
 /* Given a TREE_TYPE node, return the CTF type ID for that type.  */
 
-ctf_id_t
+ctf_dtdef_ref
 ctf_lookup_tree_type (ctf_container_ref ctfc, const tree type)
 {
   dw_die_ref die = lookup_type_die (type);
   if (die == NULL)
-    return CTF_NULL_TYPEID;
+    return NULL;
 
   ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, die);
   if (dtd == NULL)
-    return CTF_NULL_TYPEID;
+    return NULL;
 
-  return dtd->dtd_type;
+  return dtd;
 }
 
 /* Check if CTF for TYPE has already been generated.  Mainstay for
@@ -870,7 +863,7 @@ ctf_lookup_tree_type (ctf_container_ref ctfc, const tree type)
 
 bool
 ctf_type_exists (ctf_container_ref ctfc, dw_die_ref type,
-		 ctf_id_t * type_id)
+		 ctf_dtdef_ref * dtd)
 {
   bool exists = false;
   ctf_dtdef_ref ctf_type_seen = ctf_dtd_lookup (ctfc, type);
@@ -879,7 +872,7 @@ ctf_type_exists (ctf_container_ref ctfc, dw_die_ref type,
     {
       exists = true;
       /* CTF type for this type exists.  */
-      *type_id = ctf_type_seen->dtd_type;
+      *dtd = ctf_type_seen;
     }
 
   return exists;
diff --git a/gcc/ctfc.h b/gcc/ctfc.h
index e7bd93901cfa..b2060eaf7e9b 100644
--- a/gcc/ctfc.h
+++ b/gcc/ctfc.h
@@ -48,6 +48,10 @@ along with GCC; see the file COPYING3.  If not see
 
 typedef uint64_t ctf_id_t;
 
+struct ctf_dtdef;
+typedef struct ctf_dtdef ctf_dtdef_t;
+typedef ctf_dtdef_t * ctf_dtdef_ref;
+
 /* CTF string table element (list node).  */
 
 typedef struct GTY ((chain_next ("%h.cts_next"))) ctf_string
@@ -81,8 +85,8 @@ typedef struct GTY (()) ctf_encoding
 
 typedef struct GTY (()) ctf_arinfo
 {
-  ctf_id_t ctr_contents;	/* Type of array contents.  */
-  ctf_id_t ctr_index;		/* Type of array index.  */
+  ctf_dtdef_ref ctr_contents;	/* Type of array contents.  */
+  ctf_dtdef_ref ctr_index;	/* Type of array index.  */
   unsigned int ctr_nelems;	/* Number of elements.  */
 } ctf_arinfo_t;
 
@@ -90,14 +94,14 @@ typedef struct GTY (()) ctf_arinfo
 
 typedef struct GTY (()) ctf_funcinfo
 {
-  ctf_id_t ctc_return;		/* Function return type.  */
-  unsigned int ctc_argc;	/* Number of typed arguments to function.  */
-  unsigned int ctc_flags;	/* Function attributes (see below).  */
+  ctf_dtdef_ref ctc_return;	 /* Function return type.  */
+  unsigned int ctc_argc;	 /* Number of typed arguments to function.  */
+  unsigned int ctc_flags;	 /* Function attributes (see below).  */
 } ctf_funcinfo_t;
 
 typedef struct GTY (()) ctf_sliceinfo
 {
-  unsigned int cts_type;	/* Reference CTF type.  */
+  ctf_dtdef_ref cts_type;	/* Reference CTF type.  */
   unsigned short cts_offset;	/* Offset in bits of the first bit.  */
   unsigned short cts_bits;	/* Size in bits.  */
 } ctf_sliceinfo_t;
@@ -130,7 +134,7 @@ typedef struct GTY (()) ctf_itype
 typedef struct GTY ((chain_next ("%h.dmd_next"))) ctf_dmdef
 {
   const char * dmd_name;	/* Name of this member.  */
-  ctf_id_t dmd_type;		/* Type of this member (for sou).  */
+  ctf_dtdef_ref dmd_type;	/* Type of this member (for sou).  */
   uint32_t dmd_name_offset;	/* Offset of the name in str table.  */
   uint64_t dmd_offset;		/* Offset of this member in bits (for sou).  */
   HOST_WIDE_INT dmd_value;	/* Value of this member (for enum).  */
@@ -143,7 +147,7 @@ typedef struct GTY ((chain_next ("%h.dmd_next"))) ctf_dmdef
 
 typedef struct GTY (()) ctf_func_arg
 {
-  ctf_id_t farg_type;		  /* Type identifier of the argument.  */
+  ctf_dtdef_ref farg_type;	  /* Type of the argument.  */
   const char * farg_name;	  /* Name of the argument.  */
   uint32_t farg_name_offset;	  /* Offset of the name in str table.  */
   struct ctf_func_arg * farg_next;/* A list node.  */
@@ -158,6 +162,7 @@ struct GTY ((for_user)) ctf_dtdef
   dw_die_ref dtd_key;	      /* Type key for hashing.  */
   const char * dtd_name;      /* Name associated with definition (if any).  */
   ctf_id_t dtd_type;	      /* Type identifier for this definition.  */
+  ctf_dtdef_ref ref_type;     /* Type referred to by this type (if any).  */
   ctf_itype_t dtd_data;	      /* Type node.  */
   bool from_global_func; /* Whether this type was added from a global
 			    function.  */
@@ -178,7 +183,7 @@ struct GTY ((for_user)) ctf_dtdef
   } dtd_u;
 };
 
-typedef struct ctf_dtdef ctf_dtdef_t;
+#define ctf_type_id(dtd) ((uint32_t) dtd->dtd_type)
 
 /* Variable definition for CTF generation.  */
 
@@ -188,13 +193,11 @@ struct GTY ((for_user)) ctf_dvdef
   const char * dvd_name;	/* Name associated with variable.  */
   uint32_t dvd_name_offset;	/* Offset of the name in str table.  */
   unsigned int dvd_visibility;	/* External visibility.  0=static,1=global.  */
-  ctf_id_t dvd_type;		/* Type of variable.  */
+  ctf_dtdef_ref dvd_type;	/* Type of variable.  */
 };
 
 typedef struct ctf_dvdef ctf_dvdef_t;
-
 typedef ctf_dvdef_t * ctf_dvdef_ref;
-typedef ctf_dtdef_t * ctf_dtdef_ref;
 
 /* Location information for CTF Types and CTF Variables.  */
 
@@ -390,7 +393,7 @@ extern void btf_finalize (void);
 
 extern ctf_container_ref ctf_get_tu_ctfc (void);
 
-extern bool ctf_type_exists (ctf_container_ref, dw_die_ref, ctf_id_t *);
+extern bool ctf_type_exists (ctf_container_ref, dw_die_ref, ctf_dtdef_ref *);
 
 extern void ctf_add_cuname (ctf_container_ref, const char *);
 
@@ -404,41 +407,42 @@ extern bool ctf_dvd_ignore_lookup (const ctf_container_ref ctfc,
 extern const char * ctf_add_string (ctf_container_ref, const char *,
 				    uint32_t *, int);
 
-extern ctf_id_t ctf_add_reftype (ctf_container_ref, uint32_t, ctf_id_t,
-				 uint32_t, dw_die_ref);
-extern ctf_id_t ctf_add_enum (ctf_container_ref, uint32_t, const char *,
-			      HOST_WIDE_INT, bool, dw_die_ref);
-extern ctf_id_t ctf_add_slice (ctf_container_ref, uint32_t, ctf_id_t,
-			       uint32_t, uint32_t, dw_die_ref);
-extern ctf_id_t ctf_add_float (ctf_container_ref, uint32_t, const char *,
-			       const ctf_encoding_t *, dw_die_ref);
-extern ctf_id_t ctf_add_integer (ctf_container_ref, uint32_t, const char *,
-				 const ctf_encoding_t *, dw_die_ref);
-extern ctf_id_t ctf_add_unknown (ctf_container_ref, uint32_t, const char *,
-				 const ctf_encoding_t *, dw_die_ref);
-extern ctf_id_t ctf_add_pointer (ctf_container_ref, uint32_t, ctf_id_t,
-				 dw_die_ref);
-extern ctf_id_t ctf_add_array (ctf_container_ref, uint32_t,
-			       const ctf_arinfo_t *, dw_die_ref);
-extern ctf_id_t ctf_add_forward (ctf_container_ref, uint32_t, const char *,
-				 uint32_t, dw_die_ref);
-extern ctf_id_t ctf_add_typedef (ctf_container_ref, uint32_t, const char *,
-				 ctf_id_t, dw_die_ref);
-extern ctf_id_t ctf_add_function (ctf_container_ref, uint32_t, const char *,
-				  const ctf_funcinfo_t *, dw_die_ref, bool, int);
-extern ctf_id_t ctf_add_sou (ctf_container_ref, uint32_t, const char *,
-			     uint32_t, size_t, dw_die_ref);
-
-extern int ctf_add_enumerator (ctf_container_ref, ctf_id_t, const char *,
+extern ctf_dtdef_ref ctf_add_reftype (ctf_container_ref, uint32_t,
+				      ctf_dtdef_ref, uint32_t, dw_die_ref);
+extern ctf_dtdef_ref ctf_add_enum (ctf_container_ref, uint32_t, const char *,
+				   HOST_WIDE_INT, bool, dw_die_ref);
+extern ctf_dtdef_ref ctf_add_slice (ctf_container_ref, uint32_t, ctf_dtdef_ref,
+				    uint32_t, uint32_t, dw_die_ref);
+extern ctf_dtdef_ref ctf_add_float (ctf_container_ref, uint32_t, const char *,
+				    const ctf_encoding_t *, dw_die_ref);
+extern ctf_dtdef_ref ctf_add_integer (ctf_container_ref, uint32_t, const char *,
+				      const ctf_encoding_t *, dw_die_ref);
+extern ctf_dtdef_ref ctf_add_unknown (ctf_container_ref, uint32_t, const char *,
+				      const ctf_encoding_t *, dw_die_ref);
+extern ctf_dtdef_ref ctf_add_pointer (ctf_container_ref, uint32_t,
+				      ctf_dtdef_ref, dw_die_ref);
+extern ctf_dtdef_ref ctf_add_array (ctf_container_ref, uint32_t,
+				    const ctf_arinfo_t *, dw_die_ref);
+extern ctf_dtdef_ref ctf_add_forward (ctf_container_ref, uint32_t, const char *,
+				      uint32_t, dw_die_ref);
+extern ctf_dtdef_ref ctf_add_typedef (ctf_container_ref, uint32_t, const char *,
+				      ctf_dtdef_ref, dw_die_ref);
+extern ctf_dtdef_ref ctf_add_function (ctf_container_ref, uint32_t,
+				       const char *, const ctf_funcinfo_t *,
+				       dw_die_ref, bool, int);
+extern ctf_dtdef_ref ctf_add_sou (ctf_container_ref, uint32_t, const char *,
+				  uint32_t, size_t, dw_die_ref);
+
+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_id_t, uint64_t);
+				  ctf_dtdef_ref, uint64_t);
 extern int ctf_add_function_arg (ctf_container_ref, dw_die_ref,
-				 const char *, ctf_id_t);
-extern int ctf_add_variable (ctf_container_ref, const char *, ctf_id_t,
+				 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_id_t ctf_lookup_tree_type (ctf_container_ref, const tree);
+extern ctf_dtdef_ref ctf_lookup_tree_type (ctf_container_ref, const tree);
 extern ctf_id_t get_btf_id (ctf_id_t);
 
 typedef bool (*funcs_traverse_callback) (ctf_dtdef_ref, void *);
diff --git a/gcc/ctfout.cc b/gcc/ctfout.cc
index ee082b5fd016..157afbac4f48 100644
--- a/gcc/ctfout.cc
+++ b/gcc/ctfout.cc
@@ -380,7 +380,8 @@ ctf_asm_type (ctf_dtdef_ref type)
 static void
 ctf_asm_slice (ctf_dtdef_ref type)
 {
-  dw2_asm_output_data (4, type->dtd_u.dtu_slice.cts_type, "cts_type");
+  dw2_asm_output_data (4, ctf_type_id (type->dtd_u.dtu_slice.cts_type),
+		       "cts_type");
   dw2_asm_output_data (2, type->dtd_u.dtu_slice.cts_offset, "cts_offset");
   dw2_asm_output_data (2, type->dtd_u.dtu_slice.cts_bits, "cts_bits");
 }
@@ -390,8 +391,10 @@ ctf_asm_slice (ctf_dtdef_ref type)
 static void
 ctf_asm_array (ctf_dtdef_ref dtd)
 {
-  dw2_asm_output_data (4, dtd->dtd_u.dtu_arr.ctr_contents, "cta_contents");
-  dw2_asm_output_data (4, dtd->dtd_u.dtu_arr.ctr_index, "cta_index");
+  dw2_asm_output_data (4, ctf_type_id (dtd->dtd_u.dtu_arr.ctr_contents),
+		       "cta_contents");
+  dw2_asm_output_data (4, ctf_type_id (dtd->dtd_u.dtu_arr.ctr_index),
+		       "cta_index");
   dw2_asm_output_data (4, dtd->dtd_u.dtu_arr.ctr_nelems, "cta_nelems");
 }
 
@@ -403,7 +406,7 @@ ctf_asm_varent (ctf_dvdef_ref var)
   /* Output the reference to the name in the string table.  */
   dw2_asm_output_data (4, var->dvd_name_offset, "ctv_name");
   /* Output the type index.  */
-  dw2_asm_output_data (4, var->dvd_type, "ctv_typeidx");
+  dw2_asm_output_data (4, ctf_type_id (var->dvd_type), "ctv_typeidx");
 }
 
 /* Asm'out a member of CTF struct or union, represented by ctf_lmember_t.  */
@@ -414,7 +417,7 @@ ctf_asm_sou_lmember (ctf_dmdef_t * dmd)
   dw2_asm_output_data (4, dmd->dmd_name_offset, "ctlm_name");
   dw2_asm_output_data (4, CTF_OFFSET_TO_LMEMHI (dmd->dmd_offset),
 		       "ctlm_offsethi");
-  dw2_asm_output_data (4, dmd->dmd_type, "ctlm_type");
+  dw2_asm_output_data (4, ctf_type_id (dmd->dmd_type), "ctlm_type");
   dw2_asm_output_data (4, CTF_OFFSET_TO_LMEMLO (dmd->dmd_offset),
 		       "ctlm_offsetlo");
 }
@@ -426,7 +429,7 @@ ctf_asm_sou_member (ctf_dmdef_t * dmd)
 {
   dw2_asm_output_data (4, dmd->dmd_name_offset, "ctm_name");
   dw2_asm_output_data (4, dmd->dmd_offset, "ctm_offset");
-  dw2_asm_output_data (4, dmd->dmd_type, "ctm_type");
+  dw2_asm_output_data (4, ctf_type_id (dmd->dmd_type), "ctm_type");
 }
 
 /* Asm'out an enumerator constant.  */
@@ -443,7 +446,10 @@ ctf_asm_enum_const (ctf_dmdef_t * dmd)
 static void
 ctf_asm_func_arg (ctf_func_arg_t * farg)
 {
-  dw2_asm_output_data (4, farg->farg_type, "dtu_argv");
+  /* farg_type may be NULL, indicating varargs.  */
+  dw2_asm_output_data (4, farg->farg_type
+		       ? ctf_type_id (farg->farg_type)
+		       : 0, "dtu_argv");
 }
 
 /* CTF writeout to asm file.  */
@@ -537,7 +543,7 @@ output_ctf_obj_info (ctf_container_ref ctfc)
       var = ctfc->ctfc_gobjts_list[i];
 
       /* CTF type ID corresponding to the type of the variable.  */
-      dw2_asm_output_data (4, var->dvd_type, "objtinfo_var_type");
+      dw2_asm_output_data (4, ctf_type_id (var->dvd_type), "objtinfo_var_type");
     }
 
 }
diff --git a/gcc/dwarf2ctf.cc b/gcc/dwarf2ctf.cc
index 8f9e2fada9e3..a6c8541ef2b4 100644
--- a/gcc/dwarf2ctf.cc
+++ b/gcc/dwarf2ctf.cc
@@ -29,7 +29,7 @@ along with GCC; see the file COPYING3.  If not see
 
 /* Forward declarations for some routines defined in this file.  */
 
-static ctf_id_t
+static ctf_dtdef_ref
 gen_ctf_type (ctf_container_ref, dw_die_ref);
 
 /* All the DIE structures we handle come from the DWARF information
@@ -156,7 +156,7 @@ ctf_get_die_loc_col (dw_die_ref die)
 
 /* Generate CTF for the void type.  */
 
-static ctf_id_t
+static ctf_dtdef_ref
 gen_ctf_void_type (ctf_container_ref ctfc)
 {
   ctf_encoding_t ctf_encoding = {0, 0, 0};
@@ -174,10 +174,10 @@ gen_ctf_void_type (ctf_container_ref ctfc)
 
 /* Generate CTF type of unknown kind.  */
 
-static ctf_id_t
+static ctf_dtdef_ref
 gen_ctf_unknown_type (ctf_container_ref ctfc)
 {
-  ctf_id_t unknown_type_id;
+  ctf_dtdef_ref dtd;
 
   /* In CTF, the unknown type is encoded as a 0 byte sized type with kind
      CTF_K_UNKNOWN.  Create an encoding object merely to reuse the underlying
@@ -187,11 +187,11 @@ gen_ctf_unknown_type (ctf_container_ref ctfc)
 
   gcc_assert (ctf_unknown_die != NULL);
   /* Type de-duplication.  */
-  if (!ctf_type_exists (ctfc, ctf_unknown_die, &unknown_type_id))
-    unknown_type_id = ctf_add_unknown (ctfc, CTF_ADD_ROOT, "unknown",
-				       &ctf_encoding, ctf_unknown_die);
+  if (!ctf_type_exists (ctfc, ctf_unknown_die, &dtd))
+    dtd = ctf_add_unknown (ctfc, CTF_ADD_ROOT, "unknown",
+			   &ctf_encoding, ctf_unknown_die);
 
-  return unknown_type_id;
+  return dtd;
 }
 
 /* Sizes of entities can be given in bytes or bits.  This function
@@ -217,10 +217,10 @@ ctf_die_bitsize (dw_die_ref die)
    Important: the caller of this API must make sure that duplicate types are
    not added.  */
 
-static ctf_id_t
+static ctf_dtdef_ref
 gen_ctf_base_type (ctf_container_ref ctfc, dw_die_ref type)
 {
-  ctf_id_t type_id = CTF_NULL_TYPEID;
+  ctf_dtdef_ref dtd = NULL;
 
   ctf_encoding_t ctf_encoding = {0, 0, 0};
 
@@ -236,8 +236,8 @@ gen_ctf_base_type (ctf_container_ref ctfc, dw_die_ref type)
       ctf_encoding.cte_bits = 0;
 
       gcc_assert (name_string);
-      type_id = ctf_add_integer (ctfc, CTF_ADD_ROOT, name_string,
-				 &ctf_encoding, type);
+      dtd = ctf_add_integer (ctfc, CTF_ADD_ROOT, name_string,
+			     &ctf_encoding, type);
 
       break;
     case DW_ATE_boolean:
@@ -246,8 +246,8 @@ gen_ctf_base_type (ctf_container_ref ctfc, dw_die_ref type)
       ctf_encoding.cte_bits = bit_size;
 
       gcc_assert (name_string);
-      type_id = ctf_add_integer (ctfc, CTF_ADD_ROOT, name_string,
-				 &ctf_encoding, type);
+      dtd = ctf_add_integer (ctfc, CTF_ADD_ROOT, name_string,
+			     &ctf_encoding, type);
       break;
     case DW_ATE_float:
       {
@@ -269,7 +269,7 @@ gen_ctf_base_type (ctf_container_ref ctfc, dw_die_ref type)
 	  break;
 
 	ctf_encoding.cte_bits = bit_size;
-	type_id = ctf_add_float (ctfc, CTF_ADD_ROOT, name_string,
+	dtd = ctf_add_float (ctfc, CTF_ADD_ROOT, name_string,
 				 &ctf_encoding, type);
 
 	break;
@@ -291,7 +291,7 @@ gen_ctf_base_type (ctf_container_ref ctfc, dw_die_ref type)
 	ctf_encoding.cte_format |= CTF_INT_SIGNED;
 
       ctf_encoding.cte_bits = bit_size;
-      type_id = ctf_add_integer (ctfc, CTF_ADD_ROOT, name_string,
+      dtd = ctf_add_integer (ctfc, CTF_ADD_ROOT, name_string,
 				 &ctf_encoding, type);
       break;
 
@@ -315,7 +315,7 @@ gen_ctf_base_type (ctf_container_ref ctfc, dw_die_ref type)
 	  break;
 
 	ctf_encoding.cte_bits = bit_size;
-	type_id = ctf_add_float (ctfc, CTF_ADD_ROOT, name_string,
+	dtd = ctf_add_float (ctfc, CTF_ADD_ROOT, name_string,
 				 &ctf_encoding, type);
 	break;
       }
@@ -324,41 +324,39 @@ gen_ctf_base_type (ctf_container_ref ctfc, dw_die_ref type)
       break;
     }
 
-  return type_id;
+  return dtd;
 }
 
 /* Generate CTF for a pointer type.  */
 
-static ctf_id_t
+static ctf_dtdef_ref
 gen_ctf_pointer_type (ctf_container_ref ctfc, dw_die_ref ptr_type)
 {
-  ctf_id_t type_id = CTF_NULL_TYPEID;
-  ctf_id_t ptr_type_id = CTF_NULL_TYPEID;
+  ctf_dtdef_ref pointed_dtd, pointer_dtd;
   dw_die_ref pointed_type_die = ctf_get_AT_type (ptr_type);
 
-  type_id = gen_ctf_type (ctfc, pointed_type_die);
+  pointed_dtd = gen_ctf_type (ctfc, pointed_type_die);
 
   /* 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
      the gen_ctf_type call above.  */
-  if (ctf_type_exists (ctfc, ptr_type, &ptr_type_id))
-    return ptr_type_id;
+  if (!ctf_type_exists (ctfc, ptr_type, &pointer_dtd))
+    pointer_dtd = ctf_add_pointer (ctfc, CTF_ADD_ROOT, pointed_dtd, ptr_type);
 
-  ptr_type_id = ctf_add_pointer (ctfc, CTF_ADD_ROOT, type_id, ptr_type);
-  return ptr_type_id;
+  return pointer_dtd;
 }
 
 /* Recursively generate CTF for array dimensions starting at DIE C (of type
    DW_TAG_subrange_type) until DIE LAST (of type DW_TAG_subrange_type) is
    reached.  ARRAY_ELEMS_TYPE_ID is base type for the array.  */
 
-static ctf_id_t
-gen_ctf_subrange_type (ctf_container_ref ctfc, ctf_id_t array_elems_type_id,
+static ctf_dtdef_ref
+gen_ctf_subrange_type (ctf_container_ref ctfc, ctf_dtdef_ref array_elems_type,
 		       dw_die_ref c, dw_die_ref last)
 {
   ctf_arinfo_t arinfo;
-  ctf_id_t array_node_type_id = CTF_NULL_TYPEID;
+  ctf_dtdef_ref array_dtd;
 
   dw_attr_node *upper_bound_at;
   dw_die_ref array_index_type;
@@ -398,30 +396,29 @@ gen_ctf_subrange_type (ctf_container_ref ctfc, ctf_id_t array_elems_type_id,
   arinfo.ctr_index = gen_ctf_type (ctfc, array_index_type);
 
   if (c == last)
-    arinfo.ctr_contents = array_elems_type_id;
+    arinfo.ctr_contents = array_elems_type;
   else
-    arinfo.ctr_contents = gen_ctf_subrange_type (ctfc, array_elems_type_id,
+    arinfo.ctr_contents = gen_ctf_subrange_type (ctfc, array_elems_type,
 						 dw_get_die_sib (c), last);
 
-  if (!ctf_type_exists (ctfc, c, &array_node_type_id))
-    array_node_type_id = ctf_add_array (ctfc, CTF_ADD_ROOT, &arinfo, c);
+  if (!ctf_type_exists (ctfc, c, &array_dtd))
+    array_dtd = ctf_add_array (ctfc, CTF_ADD_ROOT, &arinfo, c);
 
-  return array_node_type_id;
+  return array_dtd;
 }
 
 /* Generate CTF for an ARRAY_TYPE.  */
 
-static ctf_id_t
+static ctf_dtdef_ref
 gen_ctf_array_type (ctf_container_ref ctfc,
 		    dw_die_ref array_type)
 {
   dw_die_ref first, last, array_elems_type;
-  ctf_id_t array_elems_type_id = CTF_NULL_TYPEID;
-  ctf_id_t array_type_id = CTF_NULL_TYPEID;
+  ctf_dtdef_ref array_dtd, elem_dtd;
 
   int vector_type_p = get_AT_flag (array_type, DW_AT_GNU_vector);
   if (vector_type_p)
-    return array_elems_type_id;
+    return NULL;
 
   /* Find the first and last array dimension DIEs.  */
   last = dw_get_die_child (array_type);
@@ -429,41 +426,36 @@ gen_ctf_array_type (ctf_container_ref ctfc,
 
   /* Type de-duplication.
      Consult the ctfc_types before adding CTF type for the first dimension.  */
-  if (!ctf_type_exists (ctfc, first, &array_type_id))
+  if (!ctf_type_exists (ctfc, first, &array_dtd))
     {
       array_elems_type = ctf_get_AT_type (array_type);
       /* First, register the type of the array elements if needed.  */
-      array_elems_type_id = gen_ctf_type (ctfc, array_elems_type);
+      elem_dtd = gen_ctf_type (ctfc, array_elems_type);
 
-      array_type_id = gen_ctf_subrange_type (ctfc, array_elems_type_id, first,
-					     last);
+      array_dtd = gen_ctf_subrange_type (ctfc, elem_dtd, first, last);
     }
 
-  return array_type_id;
+  return array_dtd;
 }
 
 /* Generate CTF for a typedef.  */
 
-static ctf_id_t
+static ctf_dtdef_ref
 gen_ctf_typedef (ctf_container_ref ctfc, dw_die_ref tdef)
 {
-  ctf_id_t tdef_type_id, tid;
+  ctf_dtdef_ref tdef_dtd, dtd;
   const char *tdef_name = get_AT_string (tdef, DW_AT_name);
   dw_die_ref tdef_type = ctf_get_AT_type (tdef);
 
-  tid = gen_ctf_type (ctfc, tdef_type);
+  dtd = gen_ctf_type (ctfc, tdef_type);
 
   /* Type de-duplication.
      This is necessary because the ctf for the typedef may have been already
      added due to the gen_ctf_type call above.  */
-  if (!ctf_type_exists (ctfc, tdef, &tdef_type_id))
-  {
-    tdef_type_id = ctf_add_typedef (ctfc, CTF_ADD_ROOT,
-				    tdef_name,
-				    tid,
-				    tdef);
-  }
-  return tdef_type_id;
+  if (!ctf_type_exists (ctfc, tdef, &tdef_dtd))
+    tdef_dtd = ctf_add_typedef (ctfc, CTF_ADD_ROOT, tdef_name, dtd, tdef);
+
+  return tdef_dtd;
 }
 
 /* Generate CTF for a type modifier.
@@ -475,11 +467,11 @@ gen_ctf_typedef (ctf_container_ref ctfc, dw_die_ref tdef)
    For all other cases, this function returns a CTF_NULL_TYPEID;
 */
 
-static ctf_id_t
+static ctf_dtdef_ref
 gen_ctf_modifier_type (ctf_container_ref ctfc, dw_die_ref modifier)
 {
   uint32_t kind = CTF_K_MAX;
-  ctf_id_t modifier_type_id, qual_type_id;
+  ctf_dtdef_ref dtd, modifier_dtd;
   dw_die_ref qual_type = ctf_get_AT_type (modifier);
 
   switch (dw_get_die_tag (modifier))
@@ -493,33 +485,31 @@ gen_ctf_modifier_type (ctf_container_ref ctfc, dw_die_ref modifier)
     }
 
   /* Register the type for which this modifier applies.  */
-  qual_type_id = gen_ctf_type (ctfc, qual_type);
+  dtd = gen_ctf_type (ctfc, qual_type);
 
   /* Skip generating a CTF modifier record for _Atomic as there is no
      representation for it.  */
   if (dw_get_die_tag (modifier) == DW_TAG_atomic_type)
-    return qual_type_id;
+    return dtd;
 
   gcc_assert (kind != CTF_K_MAX);
   /* Now register the modifier itself.  */
-  if (!ctf_type_exists (ctfc, modifier, &modifier_type_id))
-    modifier_type_id = ctf_add_reftype (ctfc, CTF_ADD_ROOT,
-					qual_type_id, kind,
-					modifier);
+  if (!ctf_type_exists (ctfc, modifier, &modifier_dtd))
+    modifier_dtd = ctf_add_reftype (ctfc, CTF_ADD_ROOT, dtd, kind, modifier);
 
-  return modifier_type_id;
+  return modifier_dtd;
 }
 
 /* Generate CTF for a struct type.  */
 
-static ctf_id_t
+static ctf_dtdef_ref
 gen_ctf_sou_type (ctf_container_ref ctfc, dw_die_ref sou, uint32_t kind)
 {
   uint32_t bit_size = ctf_die_bitsize (sou);
   int declaration_p = get_AT_flag (sou, DW_AT_declaration);
   const char *sou_name = get_AT_string (sou, DW_AT_name);
 
-  ctf_id_t sou_type_id;
+  ctf_dtdef_ref sou_dtd;
 
   /* An incomplete structure or union type is represented in DWARF by
      a structure or union DIE that does not have a size attribute and
@@ -531,10 +521,10 @@ gen_ctf_sou_type (ctf_container_ref ctfc, dw_die_ref sou, uint32_t kind)
 
   /* This is a complete struct or union type.  Generate a CTF type for
      it if it doesn't exist already.  */
-  if (!ctf_type_exists (ctfc, sou, &sou_type_id))
-    sou_type_id = ctf_add_sou (ctfc, CTF_ADD_ROOT,
-			       sou_name, kind, bit_size / 8,
-			       sou);
+  if (!ctf_type_exists (ctfc, sou, &sou_dtd))
+    sou_dtd = ctf_add_sou (ctfc, CTF_ADD_ROOT,
+			   sou_name, kind, bit_size / 8,
+			   sou);
 
   /* Now process the struct members.  */
   {
@@ -547,7 +537,7 @@ gen_ctf_sou_type (ctf_container_ref ctfc, dw_die_ref sou, uint32_t kind)
 	  const char *field_name;
 	  dw_die_ref field_type;
 	  HOST_WIDE_INT field_location;
-	  ctf_id_t field_type_id;
+	  ctf_dtdef_ref field_dtd;
 
 	  c = dw_get_die_sib (c);
 
@@ -556,7 +546,7 @@ gen_ctf_sou_type (ctf_container_ref ctfc, dw_die_ref sou, uint32_t kind)
 	  field_location = ctf_get_AT_data_member_location (c);
 
 	  /* Generate the field type.  */
-	  field_type_id = gen_ctf_type (ctfc, field_type);
+	  field_dtd = gen_ctf_type (ctfc, field_type);
 
 	  /* If this is a bit-field, then wrap the field type
 	     generated above with a CTF slice.  */
@@ -610,29 +600,29 @@ gen_ctf_sou_type (ctf_container_ref ctfc, dw_die_ref sou, uint32_t kind)
 		 surely something to look at for the next format version bump
 		 for CTF.  */
 	      if (bitsize <= 255 && (bitpos - field_location) <= 255)
-		field_type_id = ctf_add_slice (ctfc, CTF_ADD_NONROOT,
-					       field_type_id,
+		field_dtd = ctf_add_slice (ctfc, CTF_ADD_NONROOT,
+					       field_dtd,
 					       bitpos - field_location,
 					       bitsize, c);
 	      else
-		field_type_id = gen_ctf_unknown_type (ctfc);
+		field_dtd = gen_ctf_unknown_type (ctfc);
 	    }
 
 	  /* Add the field type to the struct or union type.  */
 	  ctf_add_member_offset (ctfc, sou,
 				 field_name,
-				 field_type_id,
+				 field_dtd,
 				 field_location);
 	}
       while (c != dw_get_die_child (sou));
   }
 
-  return sou_type_id;
+  return sou_dtd;
 }
 
 /* Generate CTF for a function type.  */
 
-static ctf_id_t
+static ctf_dtdef_ref
 gen_ctf_function_type (ctf_container_ref ctfc, dw_die_ref function,
 		       bool from_global_func)
 {
@@ -643,17 +633,16 @@ gen_ctf_function_type (ctf_container_ref ctfc, dw_die_ref function,
   uint32_t num_args = 0;
   int linkage = get_AT_flag (function, DW_AT_external);
 
-  ctf_id_t return_type_id;
-  ctf_id_t function_type_id;
+  ctf_dtdef_ref return_dtd, function_dtd;
 
   /* First, add the return type.  */
-  return_type_id = gen_ctf_type (ctfc, return_type);
-  func_info.ctc_return = return_type_id;
+  return_dtd = gen_ctf_type (ctfc, return_type);
+  func_info.ctc_return = return_dtd;
 
   /* Type de-duplication.
      Consult the ctfc_types hash before adding the CTF function type.  */
-  if (ctf_type_exists (ctfc, function, &function_type_id))
-    return function_type_id;
+  if (ctf_type_exists (ctfc, function, &function_dtd))
+    return function_dtd;
 
   /* Do a first pass on the formals to determine the number of
      arguments, and whether the function type gets a varargs.  */
@@ -681,12 +670,12 @@ gen_ctf_function_type (ctf_container_ref ctfc, dw_die_ref function,
   func_info.ctc_argc = num_args;
 
   /* Type de-duplication has already been performed by now.  */
-  function_type_id = ctf_add_function (ctfc, CTF_ADD_ROOT,
-				       function_name,
-				       (const ctf_funcinfo_t *)&func_info,
-				       function,
-				       from_global_func,
-                                       linkage);
+  function_dtd = ctf_add_function (ctfc, CTF_ADD_ROOT,
+				   function_name,
+				   (const ctf_funcinfo_t *)&func_info,
+				   function,
+				   from_global_func,
+				   linkage);
 
   /* Second pass on formals: generate the CTF types corresponding to
      them and add them as CTF function args.  */
@@ -694,7 +683,7 @@ gen_ctf_function_type (ctf_container_ref ctfc, dw_die_ref function,
     dw_die_ref c;
     unsigned int i = 0;
     const char *arg_name;
-    ctf_id_t arg_type;
+    ctf_dtdef_ref arg_type;
 
     c = dw_get_die_child (function);
     if (c)
@@ -706,7 +695,7 @@ 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, "", 0);
+	      ctf_add_function_arg (ctfc, function, "", NULL);
 	    }
 	  else if (dw_get_die_tag (c) == DW_TAG_formal_parameter)
 	    {
@@ -723,12 +712,12 @@ gen_ctf_function_type (ctf_container_ref ctfc, dw_die_ref function,
       while (c != dw_get_die_child (function));
   }
 
-  return function_type_id;
+  return function_dtd;
 }
 
 /* Generate CTF for an enumeration type.  */
 
-static ctf_id_t
+static ctf_dtdef_ref
 gen_ctf_enumeration_type (ctf_container_ref ctfc, dw_die_ref enumeration)
 {
   const char *enum_name = get_AT_string (enumeration, DW_AT_name);
@@ -736,7 +725,7 @@ gen_ctf_enumeration_type (ctf_container_ref ctfc, dw_die_ref enumeration)
   unsigned int signedness = get_AT_unsigned (enumeration, DW_AT_encoding);
   int declaration_p = get_AT_flag (enumeration, DW_AT_declaration);
 
-  ctf_id_t enumeration_type_id;
+  ctf_dtdef_ref enum_dtd;
 
   /* If this is an incomplete enum, generate a CTF forward for it and
      be done.  */
@@ -756,10 +745,10 @@ gen_ctf_enumeration_type (ctf_container_ref ctfc, dw_die_ref enumeration)
     }
 
   /* Generate a CTF type for the enumeration.  */
-  enumeration_type_id = ctf_add_enum (ctfc, CTF_ADD_ROOT,
-				      enum_name, bit_size / 8,
-				      (signedness == DW_ATE_unsigned),
-				      enumeration);
+  enum_dtd = ctf_add_enum (ctfc, CTF_ADD_ROOT,
+			   enum_name, bit_size / 8,
+			   (signedness == DW_ATE_unsigned),
+			   enumeration);
 
   /* Process the enumerators.  */
   {
@@ -787,13 +776,13 @@ gen_ctf_enumeration_type (ctf_container_ref ctfc, dw_die_ref enumeration)
 	  else
 	    value_wide_int = AT_int (enumerator_value);
 
-	  ctf_add_enumerator (ctfc, enumeration_type_id,
+	  ctf_add_enumerator (ctfc, enum_dtd,
 			      enumerator_name, value_wide_int, enumeration);
 	}
       while (c != dw_get_die_child (enumeration));
   }
 
-  return enumeration_type_id;
+  return enum_dtd;
 }
 
 /* Add a CTF variable record for the given input DWARF DIE.  */
@@ -804,7 +793,7 @@ gen_ctf_variable (ctf_container_ref ctfc, dw_die_ref die)
   const char *var_name = get_AT_string (die, DW_AT_name);
   dw_die_ref var_type = ctf_get_AT_type (die);
   unsigned int external_vis = get_AT_flag (die, DW_AT_external);
-  ctf_id_t var_type_id;
+  ctf_dtdef_ref var_dtd;
 
   /* Avoid duplicates.  */
   if (ctf_dvd_lookup (ctfc, die))
@@ -822,11 +811,10 @@ gen_ctf_variable (ctf_container_ref ctfc, dw_die_ref die)
   dw_die_ref decl = get_AT_ref (die, DW_AT_specification);
 
   /* Add the type of the variable.  */
-  var_type_id = gen_ctf_type (ctfc, var_type);
+  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_type_id, die, external_vis,
-			   decl);
+  (void) 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].  */
@@ -837,10 +825,10 @@ gen_ctf_variable (ctf_container_ref ctfc, dw_die_ref die)
 static void
 gen_ctf_function (ctf_container_ref ctfc, dw_die_ref die)
 {
-  ctf_id_t function_type_id;
+  ctf_dtdef_ref function_dtd;
   /* Type de-duplication.
      Consult the ctfc_types hash before adding the CTF function type.  */
-  if (ctf_type_exists (ctfc, die, &function_type_id))
+  if (ctf_type_exists (ctfc, die, &function_dtd))
     return;
 
   /* Add the type of the function and update the global functions
@@ -859,41 +847,41 @@ gen_ctf_function (ctf_container_ref ctfc, dw_die_ref die)
    If the given DIE is not recognized as a type, then this function
    returns CTF_NULL_TYPEID.  */
 
-static ctf_id_t
+static ctf_dtdef_ref
 gen_ctf_type (ctf_container_ref ctfc, dw_die_ref die)
 {
-  ctf_id_t type_id;
+  ctf_dtdef_ref dtd = NULL;
   int unrecog_die = false;
 
-  if (ctf_type_exists (ctfc, die, &type_id))
-    return type_id;
+  if (ctf_type_exists (ctfc, die, &dtd))
+    return dtd;
 
   switch (dw_get_die_tag (die))
     {
     case DW_TAG_base_type:
-      type_id = gen_ctf_base_type (ctfc, die);
+      dtd = gen_ctf_base_type (ctfc, die);
       break;
     case DW_TAG_pointer_type:
-      type_id = gen_ctf_pointer_type (ctfc, die);
+      dtd = gen_ctf_pointer_type (ctfc, die);
       break;
     case DW_TAG_typedef:
-      type_id = gen_ctf_typedef (ctfc, die);
+      dtd = gen_ctf_typedef (ctfc, die);
       break;
     case DW_TAG_array_type:
-      type_id = gen_ctf_array_type (ctfc, die);
+      dtd = gen_ctf_array_type (ctfc, die);
       break;
     case DW_TAG_structure_type:
-      type_id = gen_ctf_sou_type (ctfc, die, CTF_K_STRUCT);
+      dtd = gen_ctf_sou_type (ctfc, die, CTF_K_STRUCT);
       break;
     case DW_TAG_union_type:
-      type_id = gen_ctf_sou_type (ctfc, die, CTF_K_UNION);
+      dtd = gen_ctf_sou_type (ctfc, die, CTF_K_UNION);
       break;
     case DW_TAG_subroutine_type:
-      type_id = gen_ctf_function_type (ctfc, die,
-				       false /* from_global_func */);
+      dtd = gen_ctf_function_type (ctfc, die,
+				   false /* from_global_func */);
       break;
     case DW_TAG_enumeration_type:
-      type_id = gen_ctf_enumeration_type (ctfc, die);
+      dtd = gen_ctf_enumeration_type (ctfc, die);
       break;
     case DW_TAG_atomic_type:
       /* FALLTHROUGH */
@@ -902,35 +890,35 @@ gen_ctf_type (ctf_container_ref ctfc, dw_die_ref die)
     case DW_TAG_restrict_type:
       /* FALLTHROUGH */
     case DW_TAG_volatile_type:
-      type_id = gen_ctf_modifier_type (ctfc, die);
+      dtd = gen_ctf_modifier_type (ctfc, die);
       break;
     case DW_TAG_unspecified_type:
       {
 	const char *name = get_AT_string (die, DW_AT_name);
 
 	if (name && strcmp (name, "void") == 0)
-	  type_id = gen_ctf_void_type (ctfc);
+	  dtd = gen_ctf_void_type (ctfc);
 	else
-	  type_id = CTF_NULL_TYPEID;
+	  dtd = NULL;
 
 	break;
       }
     case DW_TAG_reference_type:
-      type_id = CTF_NULL_TYPEID;
+      dtd = NULL;
       break;
     default:
       /* Unrecognized DIE.  */
       unrecog_die = true;
-      type_id = CTF_NULL_TYPEID;
+      dtd = NULL;
       break;
     }
 
   /* For all types unrepresented in CTF, use an explicit CTF type of kind
      CTF_K_UNKNOWN.  */
-  if ((type_id == CTF_NULL_TYPEID) && (!unrecog_die))
-    type_id = gen_ctf_unknown_type (ctfc);
+  if ((dtd == NULL) && (!unrecog_die))
+    dtd = gen_ctf_unknown_type (ctfc);
 
-  return type_id;
+  return dtd;
 }
 
 bool
@@ -951,7 +939,7 @@ ctf_do_die (dw_die_ref die)
       return false;
     }
   else
-    return gen_ctf_type (tu_ctfc, die) == CTF_NULL_TYPEID;
+    return (gen_ctf_type (tu_ctfc, die) == NULL);
 }
 
 /* Initialize CTF subsystem for CTF debug info generation.  */
diff --git a/include/btf.h b/include/btf.h
index 24b38bace178..3f45ffb0b6bb 100644
--- a/include/btf.h
+++ b/include/btf.h
@@ -56,6 +56,11 @@ struct btf_header
 /* Maximum number of struct, union, enum members or func args.  */
 #define BTF_MAX_VLEN	0xffff
 
+/* Type ID 0 represents the void type.  */
+#define BTF_VOID_TYPEID 0
+/* Initial type ID for regular types.  */
+#define BTF_INIT_TYPEID 1
+
 struct btf_type
 {
   uint32_t name_off; 	/* Offset in string section of type name.  */
-- 
2.43.0


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

* [PATCH v3 3/6] btf: refactor and simplify implementation
  2024-05-30 21:32 [PATCH v3 0/6] btf: refactor and add pruning option David Faust
  2024-05-30 21:32 ` [PATCH v3 1/6] ctf, btf: restructure CTF/BTF emission David Faust
  2024-05-30 21:32 ` [PATCH v3 2/6] ctf: use pointers instead of IDs internally David Faust
@ 2024-05-30 21:32 ` David Faust
  2024-06-05  7:41   ` Indu Bhagat
  2024-05-30 21:32 ` [PATCH v3 4/6] btf: add -fprune-btf option David Faust
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 16+ messages in thread
From: David Faust @ 2024-05-30 21:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: indu.bhagat, jose.marchesi, cupertino.miranda

This patch heavily refactors btfout.cc to take advantage of the
structural changes in the prior commits.

Now that inter-type references are internally stored as simply pointers,
all the painful, brittle, confusing infrastructure that was used in the
process of converting CTF type IDs to BTF type IDs can be thrown out.
This greatly simplifies the entire process of converting from CTF to
BTF, making the code cleaner, easier to read, and easier to maintain.

In addition, we no longer need to worry about destructive changes in
internal data structures used commonly by CTF and BTF, which allows
deleting several ancillary data structures previously used in btfout.cc.

This is nearly transparent, but a few improvements have also been made:

 1) BTF_KIND_FUNC records are now _always_ constructed at early_finish,
    allowing us to construct records even for functions which are later
    inlined by optimizations. DATASEC entries for functions are only
    constructed at late_finish, to avoid incorrectly generating entries
    for functions which get inlined.

 2) BTF_KIND_VAR records and DATASEC entries for them are now always
    constructed at (late) finish, which avoids cases where we could
    incorrectly create records for variables which were completely
    optimized away.  This fixes PR debug/113566 for non-LTO builds.
    In LTO builds, BTF must be emitted at early_finish, so some VAR
    records may be emitted for variables which are later optimized away.

 3) Some additional assembler comments have been added with more
    information for debugging.

gcc/
	* btfout.cc (struct btf_datasec_entry): New.
	(struct btf_datasec): Add `id' member.  Change `entries' to use
	new struct btf_datasec_entry.
	(func_map): New hash_map.
	(max_translated_id): New.
	(btf_var_ids, btf_id_map, holes, voids, num_vars_added)
	(num_types_added, num_types_created): Delete.
	(btf_absolute_var_id, btf_relative_var_id, btf_absolute_func_id)
	(btf_relative_func_id, btf_absolute_datasec_id, init_btf_id_map)
	(get_btf_id, set_btf_id, btf_emit_id_p): Delete.
	(btf_removed_type_p): Delete.
	(btf_dtd_kind, btf_emit_type_p): New helpers.
	(btf_fwd_to_enum_p, btf_calc_num_vbytes): Use them.
	(btf_collect_datasec): Delete.
	(btf_dtd_postprocess_cb, btf_dvd_emit_preprocess_cb)
	(btf_dtd_emit_preprocess_cb, btf_emit_preprocess): Delete.
	(btf_dmd_representable_bitfield_p): Adapt to type reference changes
	and delete now-unused ctfc argument.
	(btf_asm_datasec_type_ref): Delete.
	(btf_asm_type_ref): Adapt to type reference changes, simplify.
	(btf_asm_type): Likewise. Mark struct/union types with bitfield
	members.
	(btf_asm_array): Adapt to data structure changes.
	(btf_asm_varent): Likewise.
	(btf_asm_sou_member): Likewise. Ensure non-bitfield members are
	correctly re-encoded if struct or union contains any bitfield.
	(btf_asm_func_arg, btf_asm_func_type, btf_asm_datasec_entry)
	(btf_asm_datasec_type): Adapt to data structure changes.
	(output_btf_header): Adapt to other changes, simplify type
	length calculation, add info to assembler comments.
	(output_btf_vars): Adapt to other changes.
	(output_btf_strs): Fix overlong lines.
	(output_asm_btf_sou_fields, output_asm_btf_enum_list)
	(output_asm_btf_func_args_list, output_asm_btf_vlen_bytes)
	(output_asm_btf_type, output_btf_types, output_btf_func_types)
	(output_btf_datasec_types): Adapt to other changes.
	(btf_init_postprocess): Delete.
	(btf_output): Change to only perform output.
	(btf_early_add_const_void, btf_early_add_func_records): New.
	(btf_early_finish): Use them here. New.
	(btf_datasec_push_entry): Adapt to data structure changes.
	(btf_datasec_add_func, btf_datasec_add_var): New.
	(btf_late_add_func_datasec_entries): New.
	(btf_emit_variable_p): New helper.
	(btf_late_add_vars): Use it here. New.
	(btf_type_list_cb, btf_late_collect_translated_types): New.
	(btf_late_assign_func_ids, btf_late_assign_var_ids)
	(btf_late_assign_datasec_ids): New.
	(btf_finish): Remove unused argument. Call new btf_late*
	functions and btf_output.
	(btf_finalize): Adapt to data structure changes.
	* ctfc.h (struct ctf_dtdef): Convert existing boolean flags to
	BOOL_BITFIELD and reorder.
	(struct ctf_dvdef): Add dvd_id member.
	(btf_finish): Remove argument from prototype.
	(get_btf_id): Delete prototype.
	(funcs_traverse_callback, traverse_btf_func_types): Add an
	explanatory comment.
	* dwarf2ctf.cc (ctf_debug_finish): Remove unused argument.
	* dwarf2ctf.h: Analogous change.
	* dwarf2out.cc: Likewise.
---
 gcc/btfout.cc    | 1254 +++++++++++++++++++---------------------------
 gcc/ctfc.h       |   17 +-
 gcc/dwarf2ctf.cc |    6 +-
 gcc/dwarf2ctf.h  |    2 +-
 gcc/dwarf2out.cc |    2 +-
 5 files changed, 539 insertions(+), 742 deletions(-)

diff --git a/gcc/btfout.cc b/gcc/btfout.cc
index 40e8d8c5c01b..32fda14f704b 100644
--- a/gcc/btfout.cc
+++ b/gcc/btfout.cc
@@ -63,53 +63,44 @@ static char btf_info_section_label[MAX_BTF_LABEL_BYTES];
 
 #define BTF_INVALID_TYPEID 0xFFFFFFFF
 
-/* Mapping of CTF variables to the IDs they will be assigned when they are
-   converted to BTF_KIND_VAR type records. Strictly accounts for the index
-   from the start of the variable type entries, does not include the number
-   of types emitted prior to the variable records.  */
-static GTY (()) hash_map <ctf_dvdef_ref, unsigned> *btf_var_ids;
-
-/* Mapping of type IDs from original CTF ID to BTF ID. Types do not map
-   1-to-1 from CTF to BTF. To avoid polluting the CTF container when updating
-   type references-by-ID, we use this map instead.  */
-static ctf_id_t * btf_id_map = NULL;
-
-/* Information for creating the BTF_KIND_DATASEC records.  */
+/* Internal representation of an entry in a BTF_KIND_DATASEC record.  */
+struct btf_datasec_entry
+{
+  union {
+    ctf_dvdef_ref dvd; /* Reference to the underlying variable represented.  */
+    ctf_dtdef_ref dtd; /* Reference to the underlying type represented.  */
+  };
+  bool is_var;	       /* True iff this entry represents a variable.  */
+  uint32_t size;       /* Size of variable or function, in bytes.
+			  For functions, always zero at compile time.  */
+};
+
+/* Internal representation of a BTF_KIND_DATASEC record.  */
 typedef struct btf_datasec
 {
-  const char *name;                    /* Section name, e.g. ".bss".  */
-  uint32_t name_offset;                /* Offset to name in string table.  */
-  vec<struct btf_var_secinfo> entries; /* Variable entries in this section.  */
+  ctf_id_t id;				 /* BTF type ID of this record.  */
+  const char *name;			 /* Section name, e.g. ".bss".  */
+  uint32_t name_offset;			 /* Offset to name in string table.  */
+  vec<struct btf_datasec_entry> entries; /* Entries in this section.  */
 } btf_datasec_t;
 
 /* One BTF_KIND_DATASEC record is created for each output data section which
    will hold at least one variable.  */
 static vec<btf_datasec_t> datasecs;
 
-/* Holes occur for types which are present in the CTF container, but are either
-   non-representable or redundant in BTF.  */
-static vec<ctf_id_t> holes;
-
-/* CTF definition(s) of void. Only one definition of void should be generated.
-   We should not encounter more than one definition of void, but use a vector
-   to be safe.  */
-static vec<ctf_id_t> voids;
-
 /* Functions in BTF have two separate type records - one for the prototype
    (BTF_KIND_FUNC_PROTO), as well as a BTF_KIND_FUNC. CTF_K_FUNCTION types
    map closely to BTF_KIND_FUNC_PROTO, but the BTF_KIND_FUNC records must be
    created. This vector holds them.  */
 static GTY (()) vec<ctf_dtdef_ref, va_gc> *funcs;
 
-/* The number of BTF variables added to the TU CTF container.  */
-static unsigned int num_vars_added = 0;
-
-/* The number of BTF types added to the TU CTF container.  */
-static unsigned int num_types_added = 0;
+/* Maps BTF_KIND_FUNC_PROTO to the BTF_KIND_FUNC record for it.  Used when
+   creating DATASEC entries.  */
+static GTY (()) hash_map<ctf_dtdef_ref, ctf_dtdef_ref> *func_map;
 
-/* The number of types synthesized for BTF that do not correspond to
-   CTF types.  */
-static unsigned int num_types_created = 0;
+/* Highest BTF ID assigned to any regular type translated from CTF.
+   Does not include BTF_KIND_{VAR,FUNC,DATASEC} types.  */
+static ctf_id_t max_translated_id = 0;
 
 /* Name strings for BTF kinds.
    Note: the indices here must match the type defines in btf.h.  */
@@ -155,6 +146,16 @@ get_btf_kind (uint32_t ctf_kind)
   return BTF_KIND_UNKN;
 }
 
+/* Convenience wrapper around get_btf_kind for the common case.  */
+
+static uint32_t
+btf_dtd_kind (ctf_dtdef_ref dtd)
+{
+  if (!dtd)
+    return BTF_KIND_UNKN;
+  return get_btf_kind (CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info));
+}
+
 /* Some BTF types, like BTF_KIND_FUNC_PROTO, are anonymous.  The machinery
    in btfout to emit BTF, may reset dtd_data->ctti_name, but does not update
    the name in the ctf_dtdef_ref type object (deliberate choice).  This
@@ -168,101 +169,20 @@ get_btf_type_name (ctf_dtdef_ref dtd)
   return (dtd->dtd_data.ctti_name) ? dtd->dtd_name : anon;
 }
 
-/* Helper routines to map between 'relative' and 'absolute' IDs.
-
-   In BTF all records (including variables) are output in one long list, and all
-   inter-type references are via index into that list.  But internally since we
-   a) translate from CTF, which separates variable records from regular types
-   and b) create some additional types after the fact, things like VAR and FUNC
-   records are stored in separate vectors with their own indices.  These
-   functions map between the 'relative' IDs (i.e.  indices in their respective
-   containers) and 'absolute' IDs (i.e.  indices in the final contiguous
-   output list), which goes in order:
-     all normal type records translated from CTF
-     all BTF_KIND_VAR records
-     all BTF_KIND_FUNC records (synthesized split function records)
-     all BTF_KIND_DATASEC records (synthesized)
-
-   The extra '+ 1's below are to account for the implicit "void" record, which
-   has index 0 but isn't actually contained in the type list.  */
-
-/* Return the final BTF ID of the variable at relative index REL.  */
-
-static ctf_id_t
-btf_absolute_var_id (ctf_id_t rel)
-{
-  return rel + (num_types_added + 1);
-}
-
-/* Return the relative index of the variable with final BTF ID ABS.  */
-
-static ctf_id_t
-btf_relative_var_id (ctf_id_t abs)
-{
-  return abs - (num_types_added + 1);
-}
-
-/* Return the final BTF ID of the func record at relative index REL.  */
-
-static ctf_id_t
-btf_absolute_func_id (ctf_id_t rel)
-{
-  return rel + (num_types_added + 1) + num_vars_added;
-}
-
-/* Return the relative index of the func record with final BTF ID ABS.  */
-
-static ctf_id_t
-btf_relative_func_id (ctf_id_t abs)
-{
-  return abs - ((num_types_added + 1) + num_vars_added);
-}
-
-/* Return the final BTF ID of the datasec record at relative index REL.  */
-
-static ctf_id_t
-btf_absolute_datasec_id (ctf_id_t rel)
-{
-  return rel + (num_types_added + 1) + num_vars_added + funcs->length ();
-}
-
-
-/* Allocate the btf_id_map, and initialize elements to BTF_INVALID_TYPEID.  */
-
-static void
-init_btf_id_map (size_t len)
+static bool
+btf_emit_type_p (ctf_dtdef_ref dtd)
 {
-  btf_id_map = XNEWVEC (ctf_id_t, len);
+  uint32_t kind = btf_dtd_kind (dtd);
 
-  btf_id_map[0] = BTF_VOID_TYPEID;
-  for (size_t i = 1; i < len; i++)
-    btf_id_map[i] = BTF_INVALID_TYPEID;
-}
+  if (kind == BTF_KIND_UNKN)
+    /* This type is not representable in BTF.  */
+    return false;
 
-/* Return the BTF type ID of CTF type ID KEY, or BTF_INVALID_TYPEID if the CTF
-   type with ID KEY does not map to a BTF type.  */
+  if (kind == BTF_KIND_INT && dtd->dtd_data.ctti_size == 0)
+    /* This is a (redundant) definition of void.  */
+    return false;
 
-ctf_id_t
-get_btf_id (ctf_id_t key)
-{
-  return btf_id_map[key];
-}
-
-/* Set the CTF type ID KEY to map to BTF type ID VAL.  */
-
-static inline void
-set_btf_id (ctf_id_t key, ctf_id_t val)
-{
-  btf_id_map[key] = val;
-}
-
-/* Return TRUE iff the given CTF type ID maps to a BTF type which will
-   be emitted.  */
-static inline bool
-btf_emit_id_p (ctf_id_t id)
-{
-  return ((btf_id_map[id] != BTF_VOID_TYPEID)
-	  && (btf_id_map[id] <= BTF_MAX_TYPE));
+  return true;
 }
 
 /* Return true if DTD is a forward-declared enum.  The BTF representation
@@ -271,9 +191,8 @@ btf_emit_id_p (ctf_id_t id)
 static bool
 btf_fwd_to_enum_p (ctf_dtdef_ref dtd)
 {
-  uint32_t btf_kind = get_btf_kind (CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info));
-
-  return (btf_kind == BTF_KIND_FWD && dtd->dtd_data.ctti_type == CTF_K_ENUM);
+  uint32_t kind = btf_dtd_kind (dtd);
+  return (kind == BTF_KIND_FWD && dtd->dtd_data.ctti_type == CTF_K_ENUM);
 }
 
 /* Each BTF type can be followed additional, variable-length information
@@ -285,7 +204,7 @@ btf_calc_num_vbytes (ctf_dtdef_ref dtd)
 {
   uint64_t vlen_bytes = 0;
 
-  uint32_t kind = get_btf_kind (CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info));
+  uint32_t kind = btf_dtd_kind (dtd);
   uint32_t vlen = CTF_V2_INFO_VLEN (dtd->dtd_data.ctti_info);
 
   switch (kind)
@@ -355,41 +274,6 @@ init_btf_sections (void)
 			       BTF_INFO_SECTION_LABEL, btf_label_num++);
 }
 
-/* Push a BTF datasec variable entry INFO into the datasec named SECNAME,
-   creating the datasec if it does not already exist.  */
-
-static void
-btf_datasec_push_entry (ctf_container_ref ctfc, const char *secname,
-			struct btf_var_secinfo info)
-{
-  if (secname == NULL)
-    return;
-
-  for (size_t i = 0; i < datasecs.length (); i++)
-    if (strcmp (datasecs[i].name, secname) == 0)
-      {
-	datasecs[i].entries.safe_push (info);
-	return;
-      }
-
-  /* If we don't already have a datasec record for secname, make one.  */
-
-  uint32_t str_off;
-  ctf_add_string (ctfc, secname, &str_off, CTF_AUX_STRTAB);
-  if (strcmp (secname, ""))
-    ctfc->ctfc_aux_strlen += strlen (secname) + 1;
-
-  btf_datasec_t ds;
-  ds.name = secname;
-  ds.name_offset = str_off;
-
-  ds.entries.create (0);
-  ds.entries.safe_push (info);
-
-  datasecs.safe_push (ds);
-}
-
-
 /* Return the section name, as of interest to btf_collect_datasec, for the
    given symtab node.  Note that this deliberately returns NULL for objects
    which do not go in a section btf_collect_datasec cares about.  */
@@ -418,301 +302,15 @@ get_section_name (symtab_node *node)
   return section_name;
 }
 
-/* Construct all BTF_KIND_DATASEC records for CTFC. One such record is created
-   for each non-empty data-containing section in the output. Each record is
-   followed by a variable number of entries describing the variables stored
-   in that section.  */
-
-static void
-btf_collect_datasec (ctf_container_ref ctfc)
-{
-  cgraph_node *func;
-  FOR_EACH_FUNCTION (func)
-    {
-      dw_die_ref die = lookup_decl_die (func->decl);
-      if (die == NULL)
-	continue;
-
-      ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, die);
-      if (dtd == NULL)
-	continue;
-
-      if (DECL_EXTERNAL (func->decl)
-	  && (lookup_attribute ("kernel_helper",
-				DECL_ATTRIBUTES (func->decl))) != NULL_TREE)
-	continue;
-
-      /* Functions actually get two types: a BTF_KIND_FUNC_PROTO, and
-	 also a BTF_KIND_FUNC.  But the CTF container only allocates one
-	 type per function, which matches closely with BTF_KIND_FUNC_PROTO.
-	 For each such function, also allocate a BTF_KIND_FUNC entry.
-	 These will be output later.  */
-      ctf_dtdef_ref func_dtd = ggc_cleared_alloc<ctf_dtdef_t> ();
-      func_dtd->dtd_data = dtd->dtd_data;
-      func_dtd->dtd_data.ctti_type = dtd->dtd_type;
-      func_dtd->linkage = dtd->linkage;
-      func_dtd->dtd_name = dtd->dtd_name;
-      /* +1 for the sentinel type not in the types map.  */
-      func_dtd->dtd_type = num_types_added + num_types_created + 1;
-
-      /* Only the BTF_KIND_FUNC type actually references the name. The
-	 BTF_KIND_FUNC_PROTO is always anonymous.  */
-      dtd->dtd_data.ctti_name = 0;
-
-      vec_safe_push (funcs, func_dtd);
-      num_types_created++;
-
-      /* Mark any 'extern' funcs and add DATASEC entries for them.  */
-      if (DECL_EXTERNAL (func->decl))
-	{
-	  func_dtd->linkage = BTF_FUNC_EXTERN;
-
-	  const char *section_name = get_section_name (func);
-	  /* Note: get_section_name () returns NULL for functions in text
-	     section.  This is intentional, since we do not want to generate
-	     DATASEC entries for them.  */
-	  if (section_name == NULL)
-	    continue;
-
-	  struct btf_var_secinfo info;
-
-	  info.type = func_dtd->dtd_type;
-
-	  /* Both zero at compile time.  */
-	  info.size = 0;
-	  info.offset = 0;
-
-	  btf_datasec_push_entry (ctfc, section_name, info);
-	}
-    }
-
-  varpool_node *node;
-  FOR_EACH_VARIABLE (node)
-    {
-      dw_die_ref die = lookup_decl_die (node->decl);
-      if (die == NULL)
-	continue;
-
-      ctf_dvdef_ref dvd = ctf_dvd_lookup (ctfc, die);
-      if (dvd == NULL)
-	continue;
-
-      /* Mark extern variables.  */
-      if (DECL_EXTERNAL (node->decl))
-	{
-	  dvd->dvd_visibility = BTF_VAR_GLOBAL_EXTERN;
-
-	  /* PR112849: avoid assuming a section for extern decls without
-	     an explicit section, which would result in incorrectly
-	     emitting a BTF_KIND_DATASEC entry for them.  */
-	  if (node->get_section () == NULL)
-	    continue;
-	}
-
-      const char *section_name = get_section_name (node);
-      if (section_name == NULL)
-	continue;
-
-      struct btf_var_secinfo info;
-
-      info.type = 0;
-      unsigned int *var_id = btf_var_ids->get (dvd);
-      if (var_id)
-	info.type = btf_absolute_var_id (*var_id);
-      else
-	continue;
-
-      info.size = 0;
-      tree size = DECL_SIZE_UNIT (node->decl);
-      if (tree_fits_uhwi_p (size))
-	info.size = tree_to_uhwi (size);
-      else if (VOID_TYPE_P (TREE_TYPE (node->decl)))
-	info.size = 1;
-
-      /* Offset is left as 0 at compile time, to be filled in by loaders such
-	 as libbpf.  */
-      info.offset = 0;
-
-      btf_datasec_push_entry (ctfc, section_name, info);
-    }
-
-  num_types_created += datasecs.length ();
-}
-
-/* Return true if the type ID is that of a type which will not be emitted (for
-   example, if it is not representable in BTF).  */
-
-static bool
-btf_removed_type_p (ctf_id_t id)
-{
-  return holes.contains (id);
-}
-
-/* Adjust the given type ID to account for holes and duplicate definitions of
-   void.  */
-
-static ctf_id_t
-btf_adjust_type_id (ctf_id_t id)
-{
-  size_t n;
-  ctf_id_t i = 0;
-
-  /* Do not adjust invalid type markers.  */
-  if (id == BTF_INVALID_TYPEID)
-    return id;
-
-  for (n = 0; n < voids.length (); n++)
-    if (id == voids[n])
-      return BTF_VOID_TYPEID;
-
-  for (n = 0; n < holes.length (); n++)
-    {
-      if (holes[n] < id)
-	i++;
-      else if (holes[n] == id)
-	return BTF_VOID_TYPEID;
-    }
-
-  return id - i;
-}
-
-/* Postprocessing callback routine for types.  */
-
-int
-btf_dtd_postprocess_cb (ctf_dtdef_ref *slot, ctf_container_ref arg_ctfc)
-{
-  ctf_dtdef_ref ctftype = (ctf_dtdef_ref) * slot;
-
-  size_t index = ctftype->dtd_type;
-  gcc_assert (index <= arg_ctfc->ctfc_types->elements ());
-
-  uint32_t ctf_kind, btf_kind;
-
-  ctf_kind = CTF_V2_INFO_KIND (ctftype->dtd_data.ctti_info);
-  btf_kind = get_btf_kind (ctf_kind);
-
-  if (btf_kind == BTF_KIND_UNKN)
-    /* This type is not representable in BTF. Create a hole.  */
-    holes.safe_push (ctftype->dtd_type);
-
-  else if (btf_kind == BTF_KIND_INT && ctftype->dtd_data.ctti_size == 0)
-    {
-      /* This is a (redundant) definition of void.  */
-      voids.safe_push (ctftype->dtd_type);
-      holes.safe_push (ctftype->dtd_type);
-    }
-
-  arg_ctfc->ctfc_types_list[index] = ctftype;
-
-  return 1;
-}
-
-/* Preprocessing callback routine for variables.  */
-
-int
-btf_dvd_emit_preprocess_cb (ctf_dvdef_ref *slot, ctf_container_ref arg_ctfc)
-{
-  ctf_dvdef_ref var = (ctf_dvdef_ref) * slot;
-
-  /* If this is an extern variable declaration with a defining declaration
-     later, skip it so that only the defining declaration is emitted.
-     This is the same case, fix and reasoning as in CTF; see PR105089.  */
-  if (ctf_dvd_ignore_lookup (arg_ctfc, var->dvd_key))
-    return 1;
-
-  /* Do not add variables which refer to unsupported types.  */
-  if (!voids.contains (var->dvd_type->dtd_type)
-      && btf_removed_type_p (var->dvd_type->dtd_type))
-    return 1;
-
-  arg_ctfc->ctfc_vars_list[num_vars_added] = var;
-  btf_var_ids->put (var, num_vars_added);
-
-  num_vars_added++;
-  num_types_created++;
-
-  return 1;
-}
-
-/* Preprocessing callback routine for types.  */
-
-static void
-btf_dtd_emit_preprocess_cb (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
-{
-  if (!btf_emit_id_p (dtd->dtd_type))
-    return;
-
-  ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (dtd);
-}
-
-/* Preprocess the CTF information to prepare for BTF output.  BTF is almost a
-   subset of CTF, with many small differences in encoding, and lacking support
-   for some types (notably floating point formats).
-
-   During the preprocessing pass:
-   - Ascertain that the sorted list of types has been prepared.  For the BTF
-     generation process, this is taken care of by the btf_init_postprocess ().
-
-   - BTF_KIND_FUNC and BTF_KIND_DATASEC records are constructed. These types do
-     not have analogues in CTF (the analogous type to CTF_K_FUNCTION is
-     BTF_KIND_FUNC_PROTO), but can be relatively easily deduced from CTF
-     information.
-
-   - Construct BTF_KIND_VAR records, representing variables.
-
-   - Calculate the total size in bytes of variable-length information following
-     BTF type records. This is used for outputting the BTF header.
-
-   After preprocessing, all BTF information is ready to be output:
-   - ctfc->ctfc_types_list holdstypes converted from CTF types. This does not
-     include KIND_VAR, KIND_FUNC, nor KIND_DATASEC types. These types have been
-     re-encoded to the appropriate representation in BTF.
-   - ctfc->ctfc_vars_list holds all variables which should be output.
-     Variables of unsupported types are not present in this list.
-   - Vector 'funcs' holds all BTF_KIND_FUNC types, one to match each
-     BTF_KIND_FUNC_PROTO.
-   - Vector 'datasecs' holds all BTF_KIND_DATASEC types.  */
-
-static void
-btf_emit_preprocess (ctf_container_ref ctfc)
-{
-  size_t num_ctf_types = ctfc->ctfc_types->elements ();
-  size_t num_ctf_vars = ctfc->ctfc_vars->elements ();
-  size_t i;
-
-  if (num_ctf_types)
-    {
-      gcc_assert (ctfc->ctfc_types_list);
-      /* Preprocess the types.  */
-      for (i = 1; i <= num_ctf_types; i++)
-	btf_dtd_emit_preprocess_cb (ctfc, ctfc->ctfc_types_list[i]);
-    }
-
-  btf_var_ids = hash_map<ctf_dvdef_ref, unsigned int>::create_ggc (100);
-
-  if (num_ctf_vars)
-    {
-      /* Allocate and construct the list of variables. While BTF variables are
-	 not distinct from types (in that variables are simply types with
-	 BTF_KIND_VAR), it is simpler to maintain a separate list of variables
-	 and append them to the types list during output.  */
-      ctfc->ctfc_vars_list = ggc_vec_alloc<ctf_dvdef_ref>(num_ctf_vars);
-      ctfc->ctfc_vars->traverse<ctf_container_ref, btf_dvd_emit_preprocess_cb>
-	(ctfc);
-
-      ctfc->ctfc_num_vlen_bytes += (num_vars_added * sizeof (struct btf_var));
-    }
-
-  btf_collect_datasec (ctfc);
-}
-
 /* Return true iff DMD is a member description of a bit-field which can be
    validly represented in BTF.  */
 
 static bool
-btf_dmd_representable_bitfield_p (ctf_container_ref ctfc, ctf_dmdef_t *dmd)
+btf_dmd_representable_bitfield_p (ctf_dmdef_t *dmd)
 {
-  ctf_dtdef_ref ref_type = ctfc->ctfc_types_list[dmd->dmd_type->dtd_type];
+  ctf_dtdef_ref ref_type = dmd->dmd_type;
+  if (!ref_type)
+    return false;
 
   if (CTF_V2_INFO_KIND (ref_type->dtd_data.ctti_info) == CTF_K_SLICE)
     {
@@ -734,76 +332,34 @@ btf_dmd_representable_bitfield_p (ctf_container_ref ctfc, ctf_dmdef_t *dmd)
 /* Asm'out a reference to another BTF type.  */
 
 static void
-btf_asm_type_ref (const char *prefix, ctf_container_ref ctfc, ctf_id_t ctf_id)
+btf_asm_type_ref (const char *prefix, ctf_dtdef_ref dtd)
 {
-  ctf_id_t btf_id = get_btf_id (ctf_id);
-  if (btf_id == BTF_VOID_TYPEID || btf_id == BTF_INVALID_TYPEID)
-    {
-      /* There is no explicit void type.
-	 Also handle any invalid refs that made it this far, just in case.  */
-      dw2_asm_output_data (4, btf_id, "%s: void", prefix);
-    }
+  if (!dtd || !btf_emit_type_p (dtd))
+    dw2_asm_output_data (4, BTF_VOID_TYPEID, "%s: void", prefix);
   else
     {
-      gcc_assert (btf_id <= num_types_added);
-
-      /* Ref to a standard type in the types list.  Note: take care that we
-	 must index the type list by the original CTF id, not the BTF id.  */
-      ctf_dtdef_ref ref_type = ctfc->ctfc_types_list[ctf_id];
-      uint32_t ref_kind
-	= get_btf_kind (CTF_V2_INFO_KIND (ref_type->dtd_data.ctti_info));
-
-      const char *kind_name = btf_fwd_to_enum_p (ref_type)
-	? btf_kind_name (BTF_KIND_ENUM)
-	: btf_kind_name (ref_kind);
-
-      dw2_asm_output_data (4, btf_id, "%s: (BTF_KIND_%s '%s')",
-			   prefix, kind_name,
-			   get_btf_type_name (ref_type));
-    }
-}
-
-/* Asm'out a reference to a BTF_KIND_VAR or BTF_KIND_FUNC type.  These type
-   kinds are BTF-specific, and should only be referred to by entries in
-   BTF_KIND_DATASEC records.  */
-
-static void
-btf_asm_datasec_type_ref (const char *prefix, ctf_container_ref ctfc,
-			  ctf_id_t btf_id)
-{
-  if (btf_id >= num_types_added + 1
-      && btf_id < num_types_added + num_vars_added + 1)
-    {
-      /* Ref to a variable.  Should only appear in DATASEC entries.  */
-      ctf_id_t var_id = btf_relative_var_id (btf_id);
-      ctf_dvdef_ref dvd = ctfc->ctfc_vars_list[var_id];
-      dw2_asm_output_data (4, btf_id, "%s: (BTF_KIND_VAR '%s')",
-			   prefix, dvd->dvd_name);
-
-    }
-  else if (btf_id >= num_types_added + num_vars_added + 1)
-    {
-      /* Ref to a FUNC record.  */
-      size_t func_id = btf_relative_func_id (btf_id);
-      ctf_dtdef_ref ref_type = (*funcs)[func_id];
-      dw2_asm_output_data (4, btf_id, "%s: (BTF_KIND_FUNC '%s')",
-			   prefix, get_btf_type_name (ref_type));
+      uint32_t kind = btf_dtd_kind (dtd);
+      if (btf_fwd_to_enum_p (dtd))
+	kind = BTF_KIND_ENUM;
+      else if (kind == BTF_KIND_FUNC_PROTO && dtd->dtd_type > max_translated_id)
+	kind = BTF_KIND_FUNC;
+
+      dw2_asm_output_data (4, dtd->dtd_type, "%s: (BTF_KIND_%s '%s')",
+			   prefix, btf_kind_name (kind),
+			   get_btf_type_name (dtd));
     }
-  else
-    /* The caller should not be calling this.  */
-    gcc_unreachable ();
 }
 
 /* Asm'out a BTF type. This routine is responsible for the bulk of the task
    of converting CTF types to their BTF representation.  */
 
 static void
-btf_asm_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
+btf_asm_type (ctf_dtdef_ref dtd)
 {
   uint32_t btf_kind, btf_kflag, btf_vlen, btf_size;
   uint32_t ctf_info = dtd->dtd_data.ctti_info;
 
-  btf_kind = get_btf_kind (CTF_V2_INFO_KIND (ctf_info));
+  btf_kind = btf_dtd_kind (dtd);
   btf_size = dtd->dtd_data.ctti_size;
   btf_vlen = CTF_V2_INFO_VLEN (ctf_info);
 
@@ -822,17 +378,17 @@ btf_asm_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
 
   if (btf_kind == BTF_KIND_STRUCT || btf_kind == BTF_KIND_UNION)
     {
-      /* If a struct/union has ANY bitfield members, set kflag=1.
-	 Note that we must also change the encoding of every member to encode
-	 both member bitfield size (stealing most-significant 8 bits) and bit
-	 offset (LS 24 bits). This is done during preprocessing.  */
+      /* If a struct/union has ANY bitfield members, set kflag=1.  */
       ctf_dmdef_t *dmd;
       for (dmd = dtd->dtd_u.dtu_members;
 	   dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
 	{
 	  /* Set kflag if this member is a representable bitfield.  */
-	  if (btf_dmd_representable_bitfield_p (ctfc, dmd))
-	    btf_kflag = 1;
+	  if (btf_dmd_representable_bitfield_p (dmd))
+	    {
+	      btf_kflag = 1;
+	      break;
+	    }
 	}
     }
 
@@ -866,7 +422,7 @@ btf_asm_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
 		    : BTF_KF_ENUM_SIGNED;
       if (dtd->dtd_data.ctti_size == 0x8)
 	btf_kind = BTF_KIND_ENUM64;
-   }
+    }
 
   /* PR debug/112656.  BTF_KIND_FUNC_PROTO is always anonymous.  */
   else if (btf_kind == BTF_KIND_FUNC_PROTO)
@@ -874,7 +430,7 @@ btf_asm_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
 
   dw2_asm_output_data (4, dtd->dtd_data.ctti_name,
 		       "TYPE %" PRIu64 " BTF_KIND_%s '%s'",
-		       get_btf_id (dtd->dtd_type), btf_kind_name (btf_kind),
+		       dtd->dtd_type, btf_kind_name (btf_kind),
 		       get_btf_type_name (dtd));
   dw2_asm_output_data (4, BTF_TYPE_INFO (btf_kind, btf_kflag, btf_vlen),
 		       "btt_info: kind=%u, kflag=%u, vlen=%u",
@@ -900,30 +456,29 @@ btf_asm_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
       break;
     }
 
-  ctf_id_t ref_id = dtd->dtd_data.ctti_type;
-  btf_asm_type_ref ("btt_type", ctfc, ref_id);
+  btf_asm_type_ref ("btt_type", dtd->ref_type);
 }
 
 /* Asm'out the variable information following a BTF_KIND_ARRAY.  */
 
 static void
-btf_asm_array (ctf_container_ref ctfc, ctf_arinfo_t arr)
+btf_asm_array (ctf_arinfo_t arr)
 {
-  btf_asm_type_ref ("bta_elem_type", ctfc, arr.ctr_contents->dtd_type);
-  btf_asm_type_ref ("bta_index_type", ctfc, arr.ctr_index->dtd_type);
+  btf_asm_type_ref ("bta_elem_type", arr.ctr_contents);
+  btf_asm_type_ref ("bta_index_type", arr.ctr_index);
   dw2_asm_output_data (4, arr.ctr_nelems, "bta_nelems");
 }
 
 /* Asm'out a BTF_KIND_VAR.  */
 
 static void
-btf_asm_varent (ctf_container_ref ctfc, ctf_dvdef_ref var)
+btf_asm_varent (ctf_dvdef_ref var)
 {
-  dw2_asm_output_data (4, var->dvd_name_offset, "TYPE %u BTF_KIND_VAR '%s'",
-		       (*(btf_var_ids->get (var)) + num_types_added + 1),
-		       var->dvd_name);
+  dw2_asm_output_data (4, var->dvd_name_offset,
+		       "TYPE %" PRIu64 " BTF_KIND_VAR '%s'",
+		       var->dvd_id, var->dvd_name);
   dw2_asm_output_data (4, BTF_TYPE_INFO (BTF_KIND_VAR, 0, 0), "btv_info");
-  btf_asm_type_ref ("btv_type", ctfc, var->dvd_type->dtd_type);
+  btf_asm_type_ref ("btv_type", var->dvd_type);
   dw2_asm_output_data (4, var->dvd_visibility, "btv_linkage");
 }
 
@@ -931,23 +486,22 @@ btf_asm_varent (ctf_container_ref ctfc, ctf_dvdef_ref var)
    BTF_KIND_UNION.  */
 
 static void
-btf_asm_sou_member (ctf_container_ref ctfc, ctf_dmdef_t * dmd, unsigned int idx)
+btf_asm_sou_member (ctf_dmdef_t * dmd, unsigned int idx)
 {
-  ctf_dtdef_ref ref_type = ctfc->ctfc_types_list[dmd->dmd_type->dtd_type];
-  ctf_id_t base_type = dmd->dmd_type->dtd_type;
+  ctf_dtdef_ref base_type = dmd->dmd_type;
   uint64_t sou_offset = dmd->dmd_offset;
 
   dw2_asm_output_data (4, dmd->dmd_name_offset,
 		       "MEMBER '%s' idx=%u",
 		       dmd->dmd_name, idx);
 
-  /* Re-encode bitfields to BTF representation.  */
-  if (CTF_V2_INFO_KIND (ref_type->dtd_data.ctti_info) == CTF_K_SLICE)
+  if (base_type
+      && CTF_V2_INFO_KIND (base_type->dtd_data.ctti_info) == CTF_K_SLICE)
     {
-      if (btf_dmd_representable_bitfield_p (ctfc, dmd))
+      if (btf_dmd_representable_bitfield_p (dmd))
 	{
-	  unsigned short word_offset = ref_type->dtd_u.dtu_slice.cts_offset;
-	  unsigned short bits = ref_type->dtd_u.dtu_slice.cts_bits;
+	  unsigned short word_offset = base_type->dtd_u.dtu_slice.cts_offset;
+	  unsigned short bits = base_type->dtd_u.dtu_slice.cts_bits;
 
 	  /* Pack the bit offset and bitfield size together.  */
 	  sou_offset += word_offset;
@@ -955,17 +509,17 @@ btf_asm_sou_member (ctf_container_ref ctfc, ctf_dmdef_t * dmd, unsigned int idx)
 	  sou_offset |= ((bits & 0xff) << 24);
 
 	  /* Refer to the base type of the slice.  */
-	  base_type = ref_type->dtd_u.dtu_slice.cts_type->dtd_type;
+	  base_type = base_type->dtd_u.dtu_slice.cts_type;
 	}
       else
 	{
 	  /* Bitfield cannot be represented in BTF.  Emit the member as having
 	     'void' type.  */
-	  base_type = BTF_VOID_TYPEID;
+	  base_type = NULL;
 	}
     }
 
-  btf_asm_type_ref ("btm_type", ctfc, base_type);
+  btf_asm_type_ref ("btm_type", base_type);
   dw2_asm_output_data (4, sou_offset, "btm_offset");
 }
 
@@ -988,86 +542,68 @@ btf_asm_enum_const (unsigned int size, ctf_dmdef_t * dmd, unsigned int idx)
 /* Asm'out a function parameter description following a BTF_KIND_FUNC_PROTO.  */
 
 static void
-btf_asm_func_arg (ctf_container_ref ctfc, ctf_func_arg_t * farg,
-		  size_t stroffset)
+btf_asm_func_arg (ctf_func_arg_t * farg, size_t stroffset)
 {
   /* If the function arg does not have a name, refer to the null string at
      the start of the string table. This ensures correct encoding for varargs
      '...' arguments.  */
   if ((farg->farg_name != NULL) && strcmp (farg->farg_name, ""))
-    dw2_asm_output_data (4, farg->farg_name_offset + stroffset, "farg_name");
+    dw2_asm_output_data (4, farg->farg_name_offset + stroffset,
+			 "farg_name '%s'", farg->farg_name);
   else
-    dw2_asm_output_data (4, 0, "farg_name");
-
-  ctf_id_t ref_id = BTF_VOID_TYPEID;
-  if (farg->farg_type && !btf_removed_type_p (farg->farg_type->dtd_type))
-    ref_id = farg->farg_type->dtd_type;
+    dw2_asm_output_data (4, 0, "farg_name ''");
 
-  btf_asm_type_ref ("farg_type", ctfc, ref_id);
+  btf_asm_type_ref ("farg_type", farg->farg_type);
 }
 
 /* Asm'out a BTF_KIND_FUNC type.  */
 
 static void
-btf_asm_func_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd, ctf_id_t id)
+btf_asm_func_type (ctf_dtdef_ref dtd)
 {
-  ctf_id_t ref_id = dtd->dtd_data.ctti_type;
   dw2_asm_output_data (4, dtd->dtd_data.ctti_name,
 		       "TYPE %" PRIu64 " BTF_KIND_FUNC '%s'",
-		       btf_absolute_func_id (id), get_btf_type_name (dtd));
+		       dtd->dtd_type, get_btf_type_name (dtd));
   dw2_asm_output_data (4, BTF_TYPE_INFO (BTF_KIND_FUNC, 0, dtd->linkage),
 		       "btt_info: kind=%u, kflag=%u, linkage=%u",
 		       BTF_KIND_FUNC, 0, dtd->linkage);
-  btf_asm_type_ref ("btt_type", ctfc, ref_id);
+  btf_asm_type_ref ("btt_type", dtd->ref_type);
 }
 
-/* Collect the name for the DATASEC reference required to be output as a
-   symbol. */
+/* Asm'out a variable entry following a BTF_KIND_DATASEC.  */
 
-static const char *
-get_name_for_datasec_entry (ctf_container_ref ctfc, ctf_id_t ref_id)
+static void
+btf_asm_datasec_entry (struct btf_datasec_entry entry)
 {
-  if (ref_id >= num_types_added + 1
-      && ref_id < num_types_added + num_vars_added + 1)
+  const char *symbol_name = NULL;
+  if (entry.is_var)
     {
-      /* Ref to a variable.  Should only appear in DATASEC entries.  */
-      ctf_id_t var_id = btf_relative_var_id (ref_id);
-      ctf_dvdef_ref dvd = ctfc->ctfc_vars_list[var_id];
-      return dvd->dvd_name;
+      symbol_name = entry.dvd->dvd_name;
+      dw2_asm_output_data (4, entry.dvd->dvd_id,
+			   "bts_type: (BTF_KIND_VAR '%s')", symbol_name);
     }
-  else if (ref_id >= num_types_added + num_vars_added + 1)
+  else
     {
-      /* Ref to a FUNC record.  */
-      size_t func_id = btf_relative_func_id (ref_id);
-      ctf_dtdef_ref ref_type = (*funcs)[func_id];
-      return get_btf_type_name (ref_type);
+      symbol_name = entry.dtd->dtd_name;
+      btf_asm_type_ref ("bts_type", entry.dtd);
     }
-  return NULL;
-}
-
-/* Asm'out a variable entry following a BTF_KIND_DATASEC.  */
 
-static void
-btf_asm_datasec_entry (ctf_container_ref ctfc, struct btf_var_secinfo info)
-{
-  const char *symbol_name = get_name_for_datasec_entry (ctfc, info.type);
-  btf_asm_datasec_type_ref ("bts_type", ctfc, info.type);
   if (!btf_with_core_debuginfo_p () || symbol_name == NULL)
-    dw2_asm_output_data (4, info.offset, "bts_offset");
+    dw2_asm_output_data (4, 0, "bts_offset");
   else
     dw2_asm_output_offset (4, symbol_name, NULL, "bts_offset");
-  dw2_asm_output_data (4, info.size, "bts_size");
+
+  dw2_asm_output_data (4, entry.size, "bts_size");
 }
 
 /* Asm'out a whole BTF_KIND_DATASEC, including its variable entries.  */
 
 static void
-btf_asm_datasec_type (ctf_container_ref ctfc, btf_datasec_t ds, ctf_id_t id,
-		      size_t stroffset)
+btf_asm_datasec_type (btf_datasec_t ds)
 {
-  dw2_asm_output_data (4, ds.name_offset + stroffset,
+  dw2_asm_output_data (4, ds.name_offset,
 		       "TYPE %" PRIu64 " BTF_KIND_DATASEC '%s'",
-		       btf_absolute_datasec_id (id), ds.name);
+		       ds.id, ds.name);
   dw2_asm_output_data (4, BTF_TYPE_INFO (BTF_KIND_DATASEC, 0,
 					 ds.entries.length ()),
 		       "btt_info: n_entries=%u", ds.entries.length ());
@@ -1075,7 +611,7 @@ btf_asm_datasec_type (ctf_container_ref ctfc, btf_datasec_t ds, ctf_id_t id,
      loaders such as libbpf.  */
   dw2_asm_output_data (4, 0, "btt_size");
   for (size_t i = 0; i < ds.entries.length (); i++)
-    btf_asm_datasec_entry (ctfc, ds.entries[i]);
+    btf_asm_datasec_entry (ds.entries[i]);
 }
 
 /* Compute and output the header information for a .BTF section.  */
@@ -1094,20 +630,11 @@ output_btf_header (ctf_container_ref ctfc)
 
    uint32_t type_off = 0, type_len = 0;
    uint32_t str_off = 0, str_len = 0;
-   uint32_t datasec_vlen_bytes = 0;
 
    if (!ctfc_is_empty_container (ctfc))
      {
-       for (size_t i = 0; i < datasecs.length (); i++)
-	 {
-	   datasec_vlen_bytes += ((datasecs[i].entries.length ())
-				  * sizeof (struct btf_var_secinfo));
-	 }
-
        /* Total length (bytes) of the types section.  */
-       type_len = (num_types_added * sizeof (struct btf_type))
-	 + (num_types_created * sizeof (struct btf_type))
-	 + datasec_vlen_bytes
+       type_len = ctfc->ctfc_num_types * sizeof (struct btf_type)
 	 + ctfc->ctfc_num_vlen_bytes;
 
        str_off = type_off + type_len;
@@ -1119,7 +646,9 @@ output_btf_header (ctf_container_ref ctfc)
    /* Offset of type section.  */
    dw2_asm_output_data (4, type_off, "type_off");
    /* Length of type section in bytes.  */
-   dw2_asm_output_data (4, type_len, "type_len");
+   dw2_asm_output_data (4, type_len, "type_len: ntypes=%u, vlen=%u",
+			(uint32_t) ctfc->ctfc_num_types,
+			(uint32_t) ctfc->ctfc_num_vlen_bytes);
     /* Offset of string section.  */
    dw2_asm_output_data (4, str_off, "str_off");
     /* Length of string section in bytes.  */
@@ -1132,11 +661,11 @@ static void
 output_btf_vars (ctf_container_ref ctfc)
 {
   size_t i;
-  size_t num_ctf_vars = num_vars_added;
+  size_t num_ctf_vars = ctfc->ctfc_vars_list_count;
   if (num_ctf_vars)
     {
       for (i = 0; i < num_ctf_vars; i++)
-	btf_asm_varent (ctfc, ctfc->ctfc_vars_list[i]);
+	btf_asm_varent (ctfc->ctfc_vars_list[i]);
     }
 }
 
@@ -1151,7 +680,8 @@ output_btf_strs (ctf_container_ref ctfc)
 
   while (ctf_string)
     {
-      dw2_asm_output_nstring (ctf_string->cts_str, -1, "btf_string, str_pos = 0x%x", str_pos);
+      dw2_asm_output_nstring (ctf_string->cts_str, -1,
+			      "btf_string, str_pos = 0x%x", str_pos);
       str_pos += strlen(ctf_string->cts_str) + 1;
       ctf_string = ctf_string->cts_next;
     }
@@ -1159,7 +689,8 @@ output_btf_strs (ctf_container_ref ctfc)
   ctf_string = ctfc->ctfc_aux_strtable.ctstab_head;
   while (ctf_string)
     {
-      dw2_asm_output_nstring (ctf_string->cts_str, -1, "btf_aux_string, str_pos = 0x%x", str_pos);
+      dw2_asm_output_nstring (ctf_string->cts_str, -1,
+			      "btf_aux_string, str_pos = 0x%x", str_pos);
       str_pos += strlen(ctf_string->cts_str) + 1;
       ctf_string = ctf_string->cts_next;
     }
@@ -1169,7 +700,7 @@ output_btf_strs (ctf_container_ref ctfc)
    BTF_KIND_UNION type.  */
 
 static void
-output_asm_btf_sou_fields (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
+output_asm_btf_sou_fields (ctf_dtdef_ref dtd)
 {
   ctf_dmdef_t * dmd;
 
@@ -1177,7 +708,7 @@ output_asm_btf_sou_fields (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
   for (dmd = dtd->dtd_u.dtu_members;
        dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
     {
-      btf_asm_sou_member (ctfc, dmd, idx);
+      btf_asm_sou_member (dmd, idx);
       idx++;
     }
 }
@@ -1185,8 +716,7 @@ output_asm_btf_sou_fields (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
 /* Output all enumerator constants following a BTF_KIND_ENUM{,64}.  */
 
 static void
-output_asm_btf_enum_list (ctf_container_ref ARG_UNUSED (ctfc),
-			  ctf_dtdef_ref dtd)
+output_asm_btf_enum_list (ctf_dtdef_ref dtd)
 {
   ctf_dmdef_t * dmd;
 
@@ -1209,7 +739,7 @@ output_asm_btf_func_args_list (ctf_container_ref ctfc,
   ctf_func_arg_t * farg;
   for (farg = dtd->dtd_u.dtu_argv;
        farg != NULL; farg = (ctf_func_arg_t *) ctf_farg_list_next (farg))
-    btf_asm_func_arg (ctfc, farg, farg_name_offset);
+    btf_asm_func_arg (farg, farg_name_offset);
 }
 
 /* Output the variable portion of a BTF type record. The information depends
@@ -1220,7 +750,7 @@ output_asm_btf_vlen_bytes (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
 {
   uint32_t btf_kind, encoding;
 
-  btf_kind = get_btf_kind (CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info));
+  btf_kind = btf_dtd_kind (dtd);
 
   if (btf_kind == BTF_KIND_UNKN)
     return;
@@ -1233,8 +763,7 @@ output_asm_btf_vlen_bytes (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
       if (dtd->dtd_data.ctti_size < 1)
 	break;
 
-      /* In BTF the CHAR `encoding' seems to not be used, so clear it
-         here.  */
+      /* In BTF the CHAR `encoding' seems to not be used, so clear it here.  */
       dtd->dtd_u.dtu_enc.cte_format &= ~BTF_INT_CHAR;
 
       encoding = BTF_INT_DATA (dtd->dtd_u.dtu_enc.cte_format,
@@ -1245,16 +774,16 @@ output_asm_btf_vlen_bytes (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
       break;
 
     case BTF_KIND_ARRAY:
-      btf_asm_array (ctfc, dtd->dtd_u.dtu_arr);
+      btf_asm_array (dtd->dtd_u.dtu_arr);
       break;
 
     case BTF_KIND_STRUCT:
     case BTF_KIND_UNION:
-      output_asm_btf_sou_fields (ctfc, dtd);
+      output_asm_btf_sou_fields (dtd);
       break;
 
     case BTF_KIND_ENUM:
-      output_asm_btf_enum_list (ctfc, dtd);
+      output_asm_btf_enum_list (dtd);
       break;
 
     case BTF_KIND_FUNC_PROTO:
@@ -1284,9 +813,9 @@ output_asm_btf_vlen_bytes (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
 static void
 output_asm_btf_type (ctf_container_ref ctfc, ctf_dtdef_ref type)
 {
-  if (btf_emit_id_p (type->dtd_type))
+  if (btf_emit_type_p (type))
     {
-      btf_asm_type (ctfc, type);
+      btf_asm_type (type);
       output_asm_btf_vlen_bytes (ctfc, type);
     }
 }
@@ -1298,7 +827,9 @@ static void
 output_btf_types (ctf_container_ref ctfc)
 {
   size_t i;
-  size_t num_types = ctfc->ctfc_types->elements ();
+  size_t num_types;
+  num_types = ctfc->ctfc_types->elements ();
+
   if (num_types)
     {
       for (i = 1; i <= num_types; i++)
@@ -1309,76 +840,45 @@ output_btf_types (ctf_container_ref ctfc)
 /* Output all BTF_KIND_FUNC type records.  */
 
 static void
-output_btf_func_types (ctf_container_ref ctfc)
+output_btf_func_types (void)
 {
   ctf_dtdef_ref ref;
   unsigned i;
   FOR_EACH_VEC_ELT (*funcs, i, ref)
-    btf_asm_func_type (ctfc, ref, i);
+    btf_asm_func_type (ref);
 }
 
 /* Output all BTF_KIND_DATASEC records.  */
 
 static void
-output_btf_datasec_types (ctf_container_ref ctfc)
+output_btf_datasec_types (void)
 {
-  size_t name_offset = ctfc_get_strtab_len (ctfc, CTF_STRTAB);
-
-  for (size_t i = 0; i < datasecs.length(); i++)
-    btf_asm_datasec_type (ctfc, datasecs[i], i, name_offset);
+  for (size_t i = 0; i < datasecs.length (); i++)
+    btf_asm_datasec_type (datasecs[i]);
 }
 
-/* Postprocess the CTF debug data post initialization.
-
-   During the postprocess pass:
-
-   - Prepare the sorted list of BTF types.
-
-     The sorted list of BTF types is, firstly, used for lookup (during the BTF
-     generation process) of CTF/BTF types given a typeID.
-
-     Secondly, in the emitted BTF section, BTF Types need to be in the sorted
-     order of their type IDs.  The BTF types section is viewed as an array,
-     with type IDs used to index into that array.  It is essential that every
-     type be placed at the exact index corresponding to its ID, or else
-     references to that type from other types will no longer be correct.
-
-   - References to void types are converted to reference BTF_VOID_TYPEID. In
-     CTF, a distinct type is used to encode void.
-
-   - Bitfield struct/union members are converted to BTF encoding. CTF uses
-     slices to encode bitfields, but BTF does not have slices and encodes
-     bitfield information directly in the variable-length btf_member
-     descriptions following the struct or union type.
-
-   - Unrepresentable types are removed. We cannot have any invalid BTF types
-     appearing in the output so they must be removed, and type ids of other
-     types and references adjust accordingly. This also involves ensuring that
-     BTF descriptions of struct members referring to unrepresentable types are
-     not emitted, as they would be nonsensical.
-
-   - Adjust inner- and inter-type references-by-ID to account for removed
-     types, and construct the types list.  */
+/* Write out all BTF debug info.  */
 
 void
-btf_init_postprocess (void)
+btf_output (ctf_container_ref ctfc)
 {
-  ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
-
-  holes.create (0);
-  voids.create (0);
+  output_btf_header (ctfc);
+  output_btf_types (ctfc);
+  output_btf_vars (ctfc);
+  output_btf_func_types ();
+  output_btf_datasec_types ();
+  output_btf_strs (ctfc);
+}
 
-  num_types_added = 0;
-  num_types_created = 0;
+/* Workaround for 'const void' variables.  These variables are sometimes used
+   in eBPF programs to address kernel symbols.  DWARF does not generate const
+   qualifier on void type, so we would incorrectly emit these variables
+   without the const qualifier.  Find any such variables, and update them to
+   refer to a new 'const' modifier type for void.  */
 
-  /* Workaround for 'const void' variables.  These variables are sometimes used
-     in eBPF programs to address kernel symbols.  DWARF does not generate const
-     qualifier on void type, so we would incorrectly emit these variables
-     without the const qualifier.
-     Unfortunately we need the TREE node to know it was const, and we need
-     to create the const modifier type (if needed) now, before making the types
-     list.  So we can't avoid iterating with FOR_EACH_VARIABLE here, and then
-     again when creating the DATASEC entries.  */
+static void
+btf_early_add_const_void (ctf_container_ref ctfc)
+{
   ctf_dtdef_ref constvoid_dtd = NULL;
   varpool_node *var;
   FOR_EACH_VARIABLE (var)
@@ -1393,120 +893,389 @@ btf_init_postprocess (void)
 	  if (die == NULL)
 	    continue;
 
-	  ctf_dvdef_ref dvd = ctf_dvd_lookup (tu_ctfc, die);
+	  ctf_dvdef_ref dvd = ctf_dvd_lookup (ctfc, die);
 	  if (dvd == NULL)
 	    continue;
 
 	  /* Create the 'const' modifier type for void.  */
 	  if (constvoid_dtd == NULL)
-	    constvoid_dtd = ctf_add_reftype (tu_ctfc, CTF_ADD_ROOT,
+	    constvoid_dtd = ctf_add_reftype (ctfc, CTF_ADD_ROOT,
 					     dvd->dvd_type, CTF_K_CONST, NULL);
 	  dvd->dvd_type = constvoid_dtd;
 	}
     }
+}
 
-  size_t i;
-  size_t num_ctf_types = tu_ctfc->ctfc_types->elements ();
+/* Functions actually get two type records: a BTF_KIND_FUNC_PROTO, and also a
+   BTF_KIND_FUNC.  But the CTF container only allocates one type per function,
+   which matches closely with BTF_KIND_FUNC_PROTO.  For each such function,
+   construct a BTF_KIND_FUNC entry.  This is done early, because we want FUNC
+   records even for functions which are later inlined by optimizations.  */
 
-  if (num_ctf_types)
+static void
+btf_early_add_func_records (ctf_container_ref ctfc)
+{
+  cgraph_node *func;
+  FOR_EACH_FUNCTION (func)
     {
-      init_btf_id_map (num_ctf_types + 1);
-
-      /* Allocate the types list and traverse all types, placing each type
-	 at the index according to its ID.  Add 1 because type ID 0 always
-	 represents VOID.  */
-      tu_ctfc->ctfc_types_list
-	= ggc_vec_alloc<ctf_dtdef_ref>(num_ctf_types + 1);
-      tu_ctfc->ctfc_types->traverse<ctf_container_ref, btf_dtd_postprocess_cb>
-	(tu_ctfc);
-
-      /* Build mapping of CTF type ID -> BTF type ID, and count total number
-	 of valid BTF types added.  */
-      for (i = 1; i <= num_ctf_types; i++)
-	{
-	  ctf_dtdef_ref dtd = tu_ctfc->ctfc_types_list[i];
-	  ctf_id_t btfid = btf_adjust_type_id (dtd->dtd_type);
-	  set_btf_id (dtd->dtd_type, btfid);
-	  if (btfid < BTF_MAX_TYPE && (btfid != BTF_VOID_TYPEID))
-	    num_types_added ++;
-	}
+      dw_die_ref die = lookup_decl_die (func->decl);
+      if (die == NULL)
+	continue;
+
+      ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, die);
+      if (dtd == NULL)
+	continue;
+
+      /* Do not add FUNC records for kernel helpers.  */
+      if (DECL_EXTERNAL (func->decl)
+	  && (lookup_attribute ("kernel_helper",
+				DECL_ATTRIBUTES (func->decl))) != NULL_TREE)
+	continue;
+
+      ctf_dtdef_ref func_dtd = ggc_cleared_alloc<ctf_dtdef_t> ();
+      func_dtd->dtd_data = dtd->dtd_data;
+      func_dtd->dtd_data.ctti_type = dtd->dtd_type;
+      func_dtd->ref_type = dtd;
+      func_dtd->linkage = dtd->linkage;
+      func_dtd->dtd_name = dtd->dtd_name;
+      /* Type ID will be assigned just before output.  */
+
+      /* Only the BTF_KIND_FUNC type actually references the name.
+	 The BTF_KIND_FUNC_PROTO is always anonymous.  */
+      dtd->dtd_data.ctti_name = 0;
+
+      /* Mark 'extern' funcs.  */
+      if (DECL_EXTERNAL (func->decl))
+	func_dtd->linkage = BTF_FUNC_EXTERN;
+
+      /* Buffer newly created FUNC records.  We cannot simply insert them
+	 into the types map, because types are keyed by their DWARF DIE,
+	 and we have no unique DIE to use as a key since the FUNC_PROTOs
+	 are already present in the map.  */
+      vec_safe_push (funcs, func_dtd);
+      func_map->put (dtd, func_dtd);
     }
 }
 
-/* Process and output all BTF data. Entry point of btfout.  */
+/* 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
+   BTF_KIND_FUNC records.  */
 
 void
-btf_output (const char * filename)
+btf_early_finish (void)
 {
   ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
 
-  init_btf_sections ();
-
-  datasecs.create (0);
   vec_alloc (funcs, 16);
+  func_map = hash_map<ctf_dtdef_ref, ctf_dtdef_ref>::create_ggc (16);
 
-  ctf_add_cuname (tu_ctfc, filename);
+  btf_early_add_const_void (tu_ctfc);
+  btf_early_add_func_records (tu_ctfc);
+}
 
-  btf_emit_preprocess (tu_ctfc);
+/* Push a BTF datasec entry ENTRY into the datasec named SECNAME,
+   creating the datasec record if it does not already exist.  */
 
-  output_btf_header (tu_ctfc);
-  output_btf_types (tu_ctfc);
-  output_btf_vars (tu_ctfc);
-  output_btf_func_types (tu_ctfc);
-  output_btf_datasec_types (tu_ctfc);
-  output_btf_strs (tu_ctfc);
+static void
+btf_datasec_push_entry (ctf_container_ref ctfc, const char *secname,
+			struct btf_datasec_entry entry)
+{
+  if (secname == NULL)
+    return;
+
+  /* If we already have a datasec record for the appropriate section,
+     append the new entry to it.  */
+  for (size_t i = 0; i < datasecs.length (); i++)
+    if (strcmp (datasecs[i].name, secname) == 0)
+      {
+	datasecs[i].entries.safe_push (entry);
+	return;
+      }
+
+  /* If we don't already have a datasec record for secname, make one.  */
+  uint32_t str_off;
+  ctf_add_string (ctfc, secname, &str_off, CTF_AUX_STRTAB);
+  if (strcmp (secname, ""))
+    ctfc->ctfc_aux_strlen += strlen (secname) + 1;
+
+  /* Note: ID will be assigned just before output.  */
+  btf_datasec_t ds;
+  ds.name = secname;
+  ds.name_offset = str_off;
+
+  /* Insert the entry into the new datasec record.  */
+  ds.entries.create (1);
+  ds.entries.quick_push (entry);
+
+  /* Insert the datasec record itself.  */
+  datasecs.safe_push (ds);
 }
 
-/* Reset all state for BTF generation so that we can rerun the compiler within
-   the same process.  */
+/* Create a datasec entry for a function, and insert it into the datasec
+   record for the appropriate section.  Create the record if it does not
+   yet exist.  */
 
-void
-btf_finalize (void)
+static void
+btf_datasec_add_func (ctf_container_ref ctfc, cgraph_node *func,
+		      ctf_dtdef_ref func_dtd)
 {
-  btf_info_section = NULL;
+  const char *section_name = get_section_name (func);
 
-  /* Clear preprocessing state.  */
-  num_vars_added = 0;
-  num_types_added = 0;
-  num_types_created = 0;
+  /* Note: get_section_name () returns NULL for functions in text
+     section.  This is intentional, since we do not want to generate
+     DATASEC entries for them.  */
+  if (section_name == NULL)
+    return;
 
-  holes.release ();
-  voids.release ();
-  for (size_t i = 0; i < datasecs.length (); i++)
-    datasecs[i].entries.release ();
-  datasecs.release ();
+  struct btf_datasec_entry entry;
+  gcc_assert (func_dtd);
+  entry.dtd = func_dtd;
+  entry.is_var = false;
 
-  funcs = NULL;
+  /* Size is left as zero at compile time, to be filled in by loaders
+     such as libbpf.  */
+  entry.size = 0;
 
-  btf_var_ids->empty ();
-  btf_var_ids = NULL;
+  btf_datasec_push_entry (ctfc, section_name, entry);
+}
 
-  free (btf_id_map);
-  btf_id_map = NULL;
+/* Create a datasec entry for a variable, and insert it into the datasec
+   record for the appropriate section.  Create the record if it does not
+   yet exist.  */
 
-  ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
-  ctfc_delete_container (tu_ctfc);
-  tu_ctfc = NULL;
+static void
+btf_datasec_add_var (ctf_container_ref ctfc, varpool_node *var,
+		     ctf_dvdef_ref dvd)
+{
+  /* PR112849: avoid assuming a section for extern decls without
+     an explicit section, which would result in incorrectly
+     emitting a BTF_KIND_DATASEC entry for them.  */
+  if (DECL_EXTERNAL (var->decl) && var->get_section () == NULL)
+    return;
+
+  const char *section_name = get_section_name (var);
+  if (section_name == NULL)
+    return;
+
+  gcc_assert (dvd);
+  struct btf_datasec_entry entry;
+  entry.dvd = dvd;
+  entry.is_var = true;
+  entry.size = 0;
+
+  tree size = DECL_SIZE_UNIT (var->decl);
+  if (tree_fits_uhwi_p (size))
+    entry.size = tree_to_uhwi (size);
+  else if (VOID_TYPE_P (TREE_TYPE (var->decl)))
+    entry.size = 1;
+
+  btf_datasec_push_entry (ctfc, section_name, entry);
 }
 
-/* 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
-   BTF_KIND_FUNC records.  */
+/* Add datasec entries for functions to CTFC.  */
 
-void
-btf_early_finish (void)
+static void
+btf_late_add_func_datasec_entries (ctf_container_ref ctfc)
+{
+  /* We need to create FUNC records at early_finish, so that we have them
+     even for functions which are later inlined by optimization passes.
+     But on the other hand, we do not want datasec entries for such functions,
+     so only create the datasec entries for them late.  This loop will not
+     hit functions which have already been inlined.  */
+  cgraph_node *func;
+  FOR_EACH_FUNCTION (func)
+    {
+      dw_die_ref die = lookup_decl_die (func->decl);
+      if (die == NULL)
+	continue;
+
+      ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, die);
+      if (dtd == NULL)
+	continue;
+
+      ctf_dtdef_ref *pdtd = func_map->get (dtd);
+      if (pdtd && DECL_EXTERNAL (func->decl))
+	btf_datasec_add_func (ctfc, func, *pdtd);
+    }
+}
+
+/* Helper function used to determine whether or not a BTF_KIND_VAR record
+   for the variable VAR shall be emitted.  */
+
+static bool
+btf_emit_variable_p (ctf_container_ref ctfc, varpool_node *var,
+		     ctf_dvdef_ref *pdvd)
+{
+  dw_die_ref die = lookup_decl_die (var->decl);
+  if (die == NULL)
+    return false;
+
+  ctf_dvdef_ref dvd = ctf_dvd_lookup (ctfc, die);
+  if (dvd == NULL)
+    return false;
+
+  /* If this is an extern variable declaration with a defining declaration
+     later, skip it so that only the defining declaration is emitted.
+     This is the same case, fix and reasoning as in CTF; see PR105089.  */
+  if (ctf_dvd_ignore_lookup (ctfc, dvd->dvd_key))
+    return false;
+
+  /* Skip variables with unrepresentable types.  */
+  if (!btf_emit_type_p (dvd->dvd_type))
+    return false;
+
+  *pdvd = dvd;
+  return true;
+}
+
+/* Add BTF_KIND_VAR records for variables.  */
+
+static void
+btf_late_add_vars (ctf_container_ref ctfc)
 {
-  btf_init_postprocess ();
+  size_t num_ctf_vars = ctfc->ctfc_vars->elements ();
+
+  ctfc->ctfc_vars_list = ggc_vec_alloc<ctf_dvdef_ref>(num_ctf_vars);
+
+  varpool_node *var;
+  ctf_dvdef_ref dvd;
+  FOR_EACH_VARIABLE (var)
+    {
+      if (!btf_emit_variable_p (ctfc, var, &dvd))
+	continue;
+
+      /* Mark 'extern' variables.  */
+      if (DECL_EXTERNAL (var->decl))
+	dvd->dvd_visibility = BTF_VAR_GLOBAL_EXTERN;
+
+      /* Add the variable to the vars list.  */
+      ctfc->ctfc_vars_list[ctfc->ctfc_vars_list_count++] = dvd;
+
+      /* Add a BTF_KIND_DATASEC entry for the variable.  */
+      btf_datasec_add_var (ctfc, var, dvd);
+    }
+}
+
+/* Callback used by btf_late_assign_type_ids to insert types into their initial
+   positions in the type list.  */
+
+static int
+btf_type_list_cb (ctf_dtdef_ref *slot, ctf_container_ref ctfc)
+{
+  ctf_dtdef_ref dtd = *slot;
+  ctfc->ctfc_types_list[dtd->dtd_type] = dtd;
+  return 1;
+}
+
+/* Construct the initial type list and assign BTF IDs for all types translated
+   from CTF.  */
+
+static void
+btf_late_collect_translated_types (ctf_container_ref ctfc)
+{
+  size_t num_ctf_types = ctfc->ctfc_types->elements ();
+
+  /* First, place each type at its CTF-assigned index in the list.
+     The '+1' here and below is to account for the implicit void type with
+     ID 0.  There is no real type at index 0 in the list.  */
+  ctfc->ctfc_types_list = ggc_vec_alloc<ctf_dtdef_ref>(num_ctf_types + 1);
+  ctfc->ctfc_types->traverse<ctf_container_ref, btf_type_list_cb> (ctfc);
+
+  /* Now, pass through the list and adjust IDs to account for types which will
+     not be emitted.  This results in each type that will be emitted in BTF
+     being assigned an appropriate ID.  Note that types which will not be
+     emitted remain in the list; they are skipped at output time.  */
+  unsigned int skip = 0;
+  for (size_t i = 1; i <= num_ctf_types; i++)
+    {
+      ctf_dtdef_ref dtd = ctfc->ctfc_types_list[i];
+      if (!btf_emit_type_p (dtd))
+	{
+	  dtd->dtd_type = BTF_INVALID_TYPEID;
+	  skip += 1;
+	  continue;
+	}
+
+      dtd->dtd_type -= skip;
+      ctfc->ctfc_num_types++;
+      ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (dtd);
+    }
+
+  max_translated_id = ctfc->ctfc_num_types;
+  ctfc->ctfc_nextid = ctfc->ctfc_num_types + 1;
+}
+
+/* Assign BTF IDs for FUNC records and account for their size.  */
+
+static void
+btf_late_assign_func_ids (ctf_container_ref ctfc)
+{
+  ctf_dtdef_ref dtd;
+  unsigned int i;
+  FOR_EACH_VEC_ELT (*funcs, i, dtd)
+    {
+      dtd->dtd_type = ctfc->ctfc_nextid++;
+      ctfc->ctfc_num_types++;
+    }
+}
+
+/* Assign BTF IDs for variables and account for their size.  */
+
+static void
+btf_late_assign_var_ids (ctf_container_ref ctfc)
+{
+  for (size_t i = 0; i < ctfc->ctfc_vars_list_count; i++)
+    {
+      ctf_dvdef_ref dvd = ctfc->ctfc_vars_list[i];
+      ctf_id_t id = ctfc->ctfc_nextid++;
+      gcc_assert (id <= BTF_MAX_TYPE);
+      dvd->dvd_id = id;
+
+      ctfc->ctfc_num_types++;
+      ctfc->ctfc_num_vlen_bytes += sizeof (struct btf_var);
+    }
+}
+
+/* Assign BTF IDs for datasec records and account for their size.  */
+
+static void
+btf_late_assign_datasec_ids (ctf_container_ref ctfc)
+{
+  for (size_t i = 0; i < datasecs.length (); i++)
+    {
+      datasecs[i].id = ctfc->ctfc_nextid++;
+      datasecs[i].name_offset += ctfc_get_strtab_len (ctfc, CTF_STRTAB);
+      ctfc->ctfc_num_types++;
+      ctfc->ctfc_num_vlen_bytes += (datasecs[i].entries.length ()
+				    * sizeof (struct btf_var_secinfo));
+    }
 }
 
 /* Late entry point for BTF generation, called from dwarf2out_finish ().
    Complete and emit BTF information.  */
 
 void
-btf_finish (const char * filename)
+btf_finish (void)
 {
-  btf_output (filename);
+  ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
+  init_btf_sections ();
+
+  datasecs.create (0);
+
+  tu_ctfc->ctfc_num_types = 0;
+  tu_ctfc->ctfc_num_vlen_bytes = 0;
+  tu_ctfc->ctfc_vars_list_count = 0;
+
+  btf_late_add_vars (tu_ctfc);
+  btf_late_collect_translated_types (tu_ctfc);
+  btf_late_add_func_datasec_entries (tu_ctfc);
+  btf_late_assign_var_ids (tu_ctfc);
+  btf_late_assign_func_ids (tu_ctfc);
+  btf_late_assign_datasec_ids (tu_ctfc);
+
+  /* Finally, write out the complete .BTF section.  */
+  btf_output (tu_ctfc);
 
   /* If compiling for BPF with CO-RE info, we cannot deallocate until after
      CO-RE information is created, which happens very late in BPF backend.
@@ -1516,6 +1285,27 @@ btf_finish (const char * filename)
     btf_finalize ();
 }
 
+/* Reset all state for BTF generation so that we can rerun the compiler within
+   the same process.  */
+
+void
+btf_finalize (void)
+{
+  btf_info_section = NULL;
+  max_translated_id = 0;
+
+  for (size_t i = 0; i < datasecs.length (); i++)
+    datasecs[i].entries.release ();
+  datasecs.release ();
+
+  funcs = NULL;
+  func_map->empty ();
+  func_map = NULL;
+
+  ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
+  ctfc_delete_container (tu_ctfc);
+  tu_ctfc = NULL;
+}
 
 /* Traversal function for all BTF_KIND_FUNC type records.  */
 
diff --git a/gcc/ctfc.h b/gcc/ctfc.h
index b2060eaf7e9b..d0b724817a7f 100644
--- a/gcc/ctfc.h
+++ b/gcc/ctfc.h
@@ -164,10 +164,14 @@ struct GTY ((for_user)) ctf_dtdef
   ctf_id_t dtd_type;	      /* Type identifier for this definition.  */
   ctf_dtdef_ref ref_type;     /* Type referred to by this type (if any).  */
   ctf_itype_t dtd_data;	      /* Type node.  */
-  bool from_global_func; /* Whether this type was added from a global
-			    function.  */
   uint32_t linkage;           /* Used in function types.  0=local, 1=global.  */
-  bool dtd_enum_unsigned;     /* Enum signedness.  */
+
+  /* Whether this type was added from a global function.  */
+  BOOL_BITFIELD from_global_func : 1;
+  /* Enum signedness.  */
+  BOOL_BITFIELD dtd_enum_unsigned : 1;
+  /* Lots of spare bits.  */
+
   union GTY ((desc ("ctf_dtu_d_union_selector (&%1)")))
   {
     /* struct, union, or enum.  */
@@ -194,6 +198,7 @@ struct GTY ((for_user)) ctf_dvdef
   uint32_t dvd_name_offset;	/* Offset of the name in str table.  */
   unsigned int dvd_visibility;	/* External visibility.  0=static,1=global.  */
   ctf_dtdef_ref dvd_type;	/* Type of variable.  */
+  ctf_id_t dvd_id;		/* ID of this variable.  Only used for BTF.  */
 };
 
 typedef struct ctf_dvdef ctf_dvdef_t;
@@ -388,7 +393,7 @@ extern void ctf_output (const char * filename);
 extern void ctf_finalize (void);
 
 extern void btf_early_finish (void);
-extern void btf_finish (const char * filename);
+extern void btf_finish (void);
 extern void btf_finalize (void);
 
 extern ctf_container_ref ctf_get_tu_ctfc (void);
@@ -443,7 +448,9 @@ extern int 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);
-extern ctf_id_t get_btf_id (ctf_id_t);
+
+/* Callback and traversal function for BTF_KIND_FUNC records.  Used by BPF
+   target for BPF CO-RE implementation.  */
 
 typedef bool (*funcs_traverse_callback) (ctf_dtdef_ref, void *);
 bool traverse_btf_func_types (funcs_traverse_callback, void *);
diff --git a/gcc/dwarf2ctf.cc b/gcc/dwarf2ctf.cc
index a6c8541ef2b4..910f523f9c5c 100644
--- a/gcc/dwarf2ctf.cc
+++ b/gcc/dwarf2ctf.cc
@@ -976,7 +976,7 @@ ctf_debug_early_finish (const char * filename)
 
       /* For LTO builds, also emit BTF now.  */
       if (flag_lto && !in_lto_p)
-	btf_finish (filename);
+	btf_finish ();
     }
   else
     /* Otherwise, done with the CTF container.  */
@@ -986,12 +986,12 @@ ctf_debug_early_finish (const char * filename)
 /* Finish CTF/BTF debug info emission.  */
 
 void
-ctf_debug_finish (const char * filename)
+ctf_debug_finish ()
 {
   /* Emit BTF late, unless this is an LTO build in which case it was
      already done early.  */
   if (btf_debuginfo_p () && !flag_lto)
-    btf_finish (filename);
+    btf_finish ();
 }
 
 #include "gt-dwarf2ctf.h"
diff --git a/gcc/dwarf2ctf.h b/gcc/dwarf2ctf.h
index 46184325bae6..f8a181a97625 100644
--- a/gcc/dwarf2ctf.h
+++ b/gcc/dwarf2ctf.h
@@ -32,7 +32,7 @@ extern void ctf_debug_init (void);
 extern void ctf_debug_init_postprocess (bool);
 extern bool ctf_do_die (dw_die_ref);
 extern void ctf_debug_early_finish (const char *);
-extern void ctf_debug_finish (const char *);
+extern void ctf_debug_finish (void);
 
 /* Wrappers for CTF/BTF to fetch information from GCC DWARF DIE.  Used in
    ctfc.cc.
diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
index e406df8673ce..47e73629e3aa 100644
--- a/gcc/dwarf2out.cc
+++ b/gcc/dwarf2out.cc
@@ -32305,7 +32305,7 @@ dwarf2out_finish (const char *filename)
   /* Generate CTF/BTF debug info.  */
   if ((ctf_debug_info_level > CTFINFO_LEVEL_NONE
        || btf_debuginfo_p ()) && lang_GNU_C ())
-    ctf_debug_finish (filename);
+    ctf_debug_finish ();
 
 #ifdef CODEVIEW_DEBUGGING_INFO
   if (codeview_debuginfo_p ())
-- 
2.43.0


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

* [PATCH v3 4/6] btf: add -fprune-btf option
  2024-05-30 21:32 [PATCH v3 0/6] btf: refactor and add pruning option David Faust
                   ` (2 preceding siblings ...)
  2024-05-30 21:32 ` [PATCH v3 3/6] btf: refactor and simplify implementation David Faust
@ 2024-05-30 21:32 ` David Faust
  2024-05-31  7:07   ` Richard Biener
  2024-06-05  7:56   ` Indu Bhagat
  2024-05-30 21:32 ` [PATCH v3 5/6] bpf,btf: enable BTF pruning by default for BPF David Faust
  2024-05-30 21:32 ` [PATCH v3 6/6] opts: allow any combination of DWARF, CTF, BTF David Faust
  5 siblings, 2 replies; 16+ messages in thread
From: David Faust @ 2024-05-30 21:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: indu.bhagat, jose.marchesi, cupertino.miranda

This patch adds a new option, -fprune-btf, to control BTF debug info
generation.

As the name implies, this option enables a kind of "pruning" of the BTF
information before it is emitted.  When enabled, rather than emitting
all type information translated from DWARF, only information for types
directly used in the source program is emitted.

The primary purpose of this pruning is to reduce the amount of
unnecessary BTF information emitted, especially for BPF programs.  It is
very common for BPF programs to incldue Linux kernel internal headers in
order to have access to kernel data structures.  However, doing so often
has the side effect of also adding type definitions for a large number
of types which are not actually used by nor relevant to the program.
In these cases, -fprune-btf commonly reduces the size of the resulting
BTF information by 10x or more, as seen on average when compiling Linux
kernel BPF selftests.  This both slims down the size of the resulting
object and reduces the time required by the BPF loader to verify the
program and its BTF information.

Note that the pruning implemented in this patch follows the same rules
as the BTF pruning performed unconditionally by LLVM's BPF backend when
generating BTF.  In particular, the main sources of pruning are:

  1) Only generate BTF for types used by variables and functions at
     the file scope.  Note that with or without pruning, BTF_KIND_VAR
     entries are only generated for variables present in the final
     object - unused static variables or variables completely optimized
     away must not have VAR entries in BTF.

  2) Avoid emitting full BTF for struct and union types which are only
     pointed-to by members of other struct/union types.  In these cases,
     the full BTF_KIND_STRUCT or BTF_KIND_UNION which would normally
     be emitted is replaced with a BTF_KIND_FWD, as though the
     underlying type was a forward-declared struct or union type.

gcc/
	* btfout.cc (btf_used_types): New hash set.
	(struct btf_fixup): New.
	(fixups, forwards): New vecs.
	(btf_output): Calculate num_types depending on flag_prune_btf.
	(btf_early_finsih): New initialization for flag_prune_btf.
	(btf_add_used_type): New function.
	(btf_used_type_list_cb): Likewise.
	(btf_late_collect_pruned_types): Likewise.
	(btf_late_add_vars): Handle special case for variables in ".maps"
	section when generating BTF for BPF CO-RE target.
	(btf_late_finish): Use btf_late_collect_pruned_types when
	flag_prune_btf in effect.  Move some initialization to btf_early_finish.
	(btf_finalize): Additional deallocation for flag_prune_btf.
	* common.opt (fprune-btf): New flag.
	* ctfc.cc (init_ctf_strtable): Make non-static.
	* ctfc.h (struct ctf_dtdef): Add visited_children_p boolean flag.
	(init_ctf_strtable, ctfc_delete_strtab): Make extern.
	* doc/invoke.texi (Debugging Options): Document -fprune-btf.

gcc/testsuite/
	* gcc.dg/debug/btf/btf-prune-1.c: New test.
	* gcc.dg/debug/btf/btf-prune-2.c: Likewise.
	* gcc.dg/debug/btf/btf-prune-3.c: Likewise.
	* gcc.dg/debug/btf/btf-prune-maps.c: Likewise.
---
 gcc/btfout.cc                                 | 359 +++++++++++++++++-
 gcc/common.opt                                |   4 +
 gcc/ctfc.cc                                   |   2 +-
 gcc/ctfc.h                                    |   3 +
 gcc/doc/invoke.texi                           |  20 +
 gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c  |  25 ++
 gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c  |  33 ++
 gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c  |  35 ++
 .../gcc.dg/debug/btf/btf-prune-maps.c         |  20 +
 9 files changed, 494 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c
 create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c
 create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c
 create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c

diff --git a/gcc/btfout.cc b/gcc/btfout.cc
index 32fda14f704b..a7da164f6b31 100644
--- a/gcc/btfout.cc
+++ b/gcc/btfout.cc
@@ -828,7 +828,10 @@ output_btf_types (ctf_container_ref ctfc)
 {
   size_t i;
   size_t num_types;
-  num_types = ctfc->ctfc_types->elements ();
+  if (flag_prune_btf)
+    num_types = max_translated_id;
+  else
+    num_types = ctfc->ctfc_types->elements ();
 
   if (num_types)
     {
@@ -957,6 +960,212 @@ btf_early_add_func_records (ctf_container_ref ctfc)
     }
 }
 
+/* The set of types used directly in the source program, and any types manually
+   marked as used.  This is the set of types which will be emitted when
+   flag_prune_btf is set.  */
+static GTY (()) hash_set<ctf_dtdef_ref> *btf_used_types;
+
+/* Fixup used to avoid unnecessary pointer chasing for types.  A fixup is
+   created when a structure or union member is a pointer to another struct
+   or union type.  In such cases, avoid emitting full type information for
+   the pointee struct or union type (which may be quite large), unless that
+   type is used directly elsewhere.  */
+struct btf_fixup
+{
+  ctf_dtdef_ref pointer_dtd; /* Type node to which the fixup is applied.  */
+  ctf_dtdef_ref pointee_dtd; /* Original type node referred to by pointer_dtd.
+				If this concrete type is not otherwise used,
+				then a forward is created.  */
+};
+
+/* Stores fixups while processing types.  */
+static vec<struct btf_fixup> fixups;
+
+/* For fixups where the underlying type is not used in the end, a BTF_KIND_FWD
+   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.
+   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
+   or union member.  SEEN_PTR is true if CHECK_PTR is true AND one of the
+   predecessors was a pointer type.  These two flags are used to avoid chasing
+   pointers to struct/union only used from pointer members.  For such types, we
+   will emit a forward instead of the full type information, unless
+   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)
+{
+  if (dtd == NULL)
+    return NULL;
+
+  uint32_t ctf_kind = CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info);
+  uint32_t kind = get_btf_kind (ctf_kind);
+
+  /* Check whether the type has already been added.  */
+  if (btf_used_types->contains (dtd))
+    {
+      /* It's possible the type was already added as a fixup, but that we now
+	 have a concrete use of it.  */
+      switch (kind)
+	{
+	case BTF_KIND_PTR:
+	case BTF_KIND_TYPEDEF:
+	case BTF_KIND_CONST:
+	case BTF_KIND_VOLATILE:
+	case BTF_KIND_RESTRICT:
+	  if (check_ptr)
+	    /* Type was previously added as a fixup, and that's OK.  */
+	    return dtd;
+	  else
+	    {
+	      /* The type was previously added as a fixup, but now we have
+		 a concrete use of it.  Remove the fixup.  */
+	      for (size_t i = 0; i < fixups.length (); i++)
+		if (fixups[i].pointer_dtd == 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);
+	      return dtd;
+	    }
+	default:
+	  return dtd;
+	}
+    }
+
+  if (ctf_kind == CTF_K_SLICE)
+    {
+      /* Bitfield.  Add the underlying type to the used set, but leave
+	 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);
+      return dtd;
+    }
+
+  /* Skip redundant definitions of void and types with no BTF encoding.  */
+  if ((kind == BTF_KIND_INT && dtd->dtd_data.ctti_size == 0)
+      || (kind == BTF_KIND_UNKN))
+    return NULL;
+
+  /* Add the type itself, and assign its id.
+     Do this before recursing to handle things like linked list structures.  */
+  gcc_assert (ctfc->ctfc_nextid <= BTF_MAX_TYPE);
+  dtd->dtd_type = ctfc->ctfc_nextid++;
+  btf_used_types->add (dtd);
+  ctf_add_string (ctfc, dtd->dtd_name, &(dtd->dtd_data.ctti_name), CTF_STRTAB);
+  ctfc->ctfc_num_types++;
+  ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (dtd);
+
+  /* Recursively add types referenced by this type.  */
+  switch (kind)
+    {
+    case BTF_KIND_INT:
+    case BTF_KIND_FLOAT:
+    case BTF_KIND_FWD:
+      /* Leaf kinds which do not refer to any other types.  */
+      break;
+
+    case BTF_KIND_FUNC:
+    case BTF_KIND_VAR:
+      /* Root kinds; no type we are visiting may refer to these.  */
+      gcc_unreachable ();
+
+    case BTF_KIND_PTR:
+    case BTF_KIND_TYPEDEF:
+    case BTF_KIND_CONST:
+    case BTF_KIND_VOLATILE:
+    case BTF_KIND_RESTRICT:
+      {
+	/* These type kinds refer to exactly one other type.  */
+	if (check_ptr && !seen_ptr)
+	  seen_ptr = (kind == BTF_KIND_PTR);
+
+	/* Try to avoid chasing pointers to struct/union types if the
+	   underlying type isn't used.  */
+	if (check_ptr && seen_ptr && create_fixups)
+	  {
+	    ctf_dtdef_ref ref = dtd->ref_type;
+	    uint32_t ref_kind = btf_dtd_kind (ref);
+
+	    if ((ref_kind == BTF_KIND_STRUCT || ref_kind == BTF_KIND_UNION)
+		&& !btf_used_types->contains (ref))
+	      {
+		struct btf_fixup fixup;
+		fixup.pointer_dtd = dtd;
+		fixup.pointee_dtd = ref;
+		fixups.safe_push (fixup);
+		break;
+	      }
+	  }
+
+	/* 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);
+	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);
+	break;
+      }
+    case BTF_KIND_STRUCT:
+    case BTF_KIND_UNION:
+    case BTF_KIND_ENUM:
+    case BTF_KIND_ENUM64:
+      {
+	/* Add members.  */
+	ctf_dmdef_t *dmd;
+	for (dmd = dtd->dtd_u.dtu_members;
+	     dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
+	  {
+	    /* 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);
+	    ctf_add_string (ctfc, dmd->dmd_name, &(dmd->dmd_name_offset),
+			    CTF_STRTAB);
+	  }
+	break;
+      }
+    case BTF_KIND_FUNC_PROTO:
+      {
+	/* Add return type.  */
+	dtd->ref_type = btf_add_used_type (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);
+	    /* 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.  */
+	  }
+	break;
+      }
+    default:
+      return NULL;
+    }
+
+  return dtd;
+}
+
 /* 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
@@ -972,6 +1181,27 @@ btf_early_finish (void)
 
   btf_early_add_const_void (tu_ctfc);
   btf_early_add_func_records (tu_ctfc);
+
+  /* Note: from here on, destructive changes are made to the TU CTFC to
+     translate CTF to BTF.  These fields are reset to count BTF types etc.  */
+  tu_ctfc->ctfc_num_types = 0;
+  tu_ctfc->ctfc_num_vlen_bytes = 0;
+  tu_ctfc->ctfc_vars_list_count = 0;
+
+  if (flag_prune_btf)
+    {
+      btf_used_types
+	= hash_set<ctf_dtdef_ref>::create_ggc (tu_ctfc->ctfc_types->elements ());
+      tu_ctfc->ctfc_nextid = 1;
+      fixups.create (1);
+
+      /* Empty the string table, which was already populated with strings for
+	 all types translated from DWARF.  We may only need a very small subset
+	 of these strings; those will be re-added below.  */
+      ctfc_delete_strtab (&tu_ctfc->ctfc_strtable);
+      init_ctf_strtable (&tu_ctfc->ctfc_strtable);
+      tu_ctfc->ctfc_strlen++;
+    }
 }
 
 /* Push a BTF datasec entry ENTRY into the datasec named SECNAME,
@@ -1154,6 +1384,25 @@ btf_late_add_vars (ctf_container_ref ctfc)
 
       /* Add a BTF_KIND_DATASEC entry for the variable.  */
       btf_datasec_add_var (ctfc, var, dvd);
+
+      const char *section = var->get_section ();
+      if (section && (strcmp (section, ".maps") == 0) && flag_prune_btf)
+	{
+	  /* The .maps section has special meaning in BTF: it is used for BPF
+	     map definitions.  These definitions should be structs.  We must
+	     collect pointee types used in map members as though they are used
+	     directly, effectively ignoring (from the pruning perspective) that
+	     they are struct members.  */
+	  ctf_dtdef_ref dtd = dvd->dvd_type;
+	  uint32_t kind = btf_dtd_kind (dvd->dvd_type);
+	  if (kind == BTF_KIND_STRUCT)
+	    {
+	      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);
+	    }
+	}
     }
 }
 
@@ -1252,6 +1501,86 @@ btf_late_assign_datasec_ids (ctf_container_ref ctfc)
     }
 }
 
+/* Callback used for assembling the only-used-types list.  Note that this is
+   the same as btf_type_list_cb above, but the hash_set traverse requires a
+   different function signature.  */
+
+static bool
+btf_used_type_list_cb (const ctf_dtdef_ref& dtd, ctf_container_ref ctfc)
+{
+  ctfc->ctfc_types_list[dtd->dtd_type] = dtd;
+  return true;
+}
+
+/* Collect the set of types reachable from global variables and functions.
+   This is the minimal set of types, used when generating pruned BTF.  */
+
+static void
+btf_late_collect_pruned_types (ctf_container_ref ctfc)
+{
+  vec_alloc (forwards, 1);
+
+  /* Add types used from functions.  */
+  ctf_dtdef_ref dtd;
+  size_t i;
+  FOR_EACH_VEC_ELT (*funcs, i, dtd)
+    {
+      btf_add_used_type (ctfc, dtd->ref_type, false, false, true);
+      ctf_add_string (ctfc, dtd->dtd_name, &(dtd->dtd_data.ctti_name),
+		      CTF_STRTAB);
+    }
+
+  /* Add types used from global variables.  */
+  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);
+      ctf_add_string (ctfc, dvd->dvd_name, &(dvd->dvd_name_offset), CTF_STRTAB);
+    }
+
+  /* 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.  */
+  for (i = 0; i < fixups.length (); i++)
+    {
+      struct btf_fixup *fx = &fixups[i];
+      if (!btf_used_types->contains (fx->pointee_dtd))
+	{
+	  /* The underlying type is not used.  Create a forward.  */
+	  ctf_dtdef_ref fwd = ggc_cleared_alloc<ctf_dtdef_t> ();
+	  ctf_id_t id = ctfc->ctfc_nextid++;
+	  gcc_assert (id <= BTF_MAX_TYPE);
+
+	  bool union_p = (btf_dtd_kind (fx->pointee_dtd) == BTF_KIND_UNION);
+
+	  fwd->dtd_name = fx->pointee_dtd->dtd_name;
+	  fwd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_FORWARD, union_p, 0);
+	  fwd->dtd_type = id;
+	  ctfc->ctfc_num_types++;
+	  ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (fwd);
+	  ctf_add_string (ctfc, fwd->dtd_name, &(fwd->dtd_data.ctti_name),
+			  CTF_STRTAB);
+
+	  /* Update the pointer to point to the forward.  */
+	  fx->pointer_dtd->ref_type = fwd;
+	  vec_safe_push (forwards, fwd);
+	}
+    }
+
+  /* Construct the resulting pruned type list.  */
+  ctfc->ctfc_types_list
+    = ggc_vec_alloc<ctf_dtdef_ref> (btf_used_types->elements () + 1
+				    + vec_safe_length (forwards));
+
+  btf_used_types->traverse<ctf_container_ref, btf_used_type_list_cb> (ctfc);
+
+  /* Insert the newly created forwards into the regular types list too.  */
+  FOR_EACH_VEC_ELT (*forwards, i, dtd)
+    ctfc->ctfc_types_list[dtd->dtd_type] = dtd;
+
+  max_translated_id = btf_used_types->elements () + vec_safe_length (forwards);
+}
+
 /* Late entry point for BTF generation, called from dwarf2out_finish ().
    Complete and emit BTF information.  */
 
@@ -1263,13 +1592,22 @@ btf_finish (void)
 
   datasecs.create (0);
 
-  tu_ctfc->ctfc_num_types = 0;
-  tu_ctfc->ctfc_num_vlen_bytes = 0;
-  tu_ctfc->ctfc_vars_list_count = 0;
-
   btf_late_add_vars (tu_ctfc);
-  btf_late_collect_translated_types (tu_ctfc);
+  if (flag_prune_btf)
+    {
+      /* Collect pruned set of BTF types and prepare for emission.
+	 This includes only types directly used in file-scope variables and
+	 function return/argument types.  */
+      btf_late_collect_pruned_types (tu_ctfc);
+    }
+  else
+    {
+      /* Collect all BTF types and prepare for emission.
+	 This includes all types translated from DWARF.  */
+      btf_late_collect_translated_types (tu_ctfc);
+    }
   btf_late_add_func_datasec_entries (tu_ctfc);
+
   btf_late_assign_var_ids (tu_ctfc);
   btf_late_assign_func_ids (tu_ctfc);
   btf_late_assign_datasec_ids (tu_ctfc);
@@ -1302,6 +1640,15 @@ btf_finalize (void)
   func_map->empty ();
   func_map = NULL;
 
+  if (flag_prune_btf)
+    {
+      btf_used_types->empty ();
+      btf_used_types = NULL;
+
+      fixups.release ();
+      forwards = NULL;
+    }
+
   ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
   ctfc_delete_container (tu_ctfc);
   tu_ctfc = NULL;
diff --git a/gcc/common.opt b/gcc/common.opt
index 2c078fdd1f86..3d4a55d02c26 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -2588,6 +2588,10 @@ fpatchable-function-entry=
 Common Var(flag_patchable_function_entry) Joined Optimization
 Insert NOP instructions at each function entry.
 
+fprune-btf
+Common Var(flag_prune_btf) Init(0)
+Generate minimal BTF debug info.
+
 frandom-seed
 Common Var(common_deferred_options) Defer
 
diff --git a/gcc/ctfc.cc b/gcc/ctfc.cc
index 678ca2a072cf..27fc3a6d5851 100644
--- a/gcc/ctfc.cc
+++ b/gcc/ctfc.cc
@@ -913,7 +913,7 @@ ctfc_get_dvd_srcloc (ctf_dvdef_ref dvd, ctf_srcloc_ref loc)
 /* Initialize the CTF string table.
    The first entry in the CTF string table (empty string) is added.  */
 
-static void
+void
 init_ctf_strtable (ctf_strtable_t * strtab)
 {
   strtab->ctstab_head = NULL;
diff --git a/gcc/ctfc.h b/gcc/ctfc.h
index d0b724817a7f..29267dc036d1 100644
--- a/gcc/ctfc.h
+++ b/gcc/ctfc.h
@@ -369,6 +369,9 @@ extern unsigned int ctfc_get_num_ctf_vars (ctf_container_ref);
 
 extern ctf_strtable_t * ctfc_get_strtab (ctf_container_ref, int);
 
+extern void init_ctf_strtable (ctf_strtable_t *);
+extern void ctfc_delete_strtab (ctf_strtable_t *);
+
 /* Get the length of the specified string table in the CTF container.  */
 
 extern size_t ctfc_get_strtab_len (ctf_container_ref, int);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 517a782987d9..6dac00d14381 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -534,6 +534,7 @@ Objective-C and Objective-C++ Dialects}.
 -gvms -gz@r{[}=@var{type}@r{]}
 -gsplit-dwarf  -gdescribe-dies  -gno-describe-dies
 -fdebug-prefix-map=@var{old}=@var{new}  -fdebug-types-section
+-fprune-btf -fno-prune-btf
 -fno-eliminate-unused-debug-types
 -femit-struct-debug-baseonly  -femit-struct-debug-reduced
 -femit-struct-debug-detailed@r{[}=@var{spec-list}@r{]}
@@ -12292,6 +12293,25 @@ compressed debug sections, the option is rejected.  Otherwise, if the
 assembler does not support them, @option{-gz} is silently ignored when
 producing object files.
 
+@opindex fprune-btf
+@opindex fno-prune-btf
+@item -fprune-btf
+@itemx -fno-prune-btf
+Prune BTF information before emission.  When pruning, only type
+information for types used by global variables and file-scope functions
+will be emitted.  If compiling for the BPF target with BPF CO-RE
+enabled, type information will also be emitted for types used in BPF
+CO-RE relocations.  In addition, struct and union types which are only
+referred to via pointers from members of other struct or union types
+shall be pruned and replaced with BTF_KIND_FWD, as though those types
+were only present in the input as forward declarations.
+
+This option substantially reduces the size of produced BTF information,
+but at significant loss in the amount of detailed type information.
+It is primarily useful when compiling for the BPF target, to minimize
+the size of the resulting object, and to eliminate BTF information
+which is not immediately relevant to the BPF program loading process.
+
 @opindex femit-struct-debug-baseonly
 @item -femit-struct-debug-baseonly
 Emit debug information for struct-like types
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c
new file mode 100644
index 000000000000..3c9b59a07ecf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c
@@ -0,0 +1,25 @@
+/* Simple test of -fprune-btf option operation.
+   Since 'struct foo' is not used, no BTF shall be emitted for it.  */
+
+/* { dg-do compile } */
+/* { dg-options "-gbtf -fprune-btf -dA" } */
+
+/* No BTF info for 'struct foo' nor types used only by it.  */
+/* { dg-final { scan-assembler-not "BTF_KIND_STRUCT 'foo'" } } */
+/* { dg-final { scan-assembler-not "BTF_KIND_INT 'char'" } } */
+
+/* We should get BTF info for 'struct bar' since it is used.  */
+/* { dg-final { scan-assembler "BTF_KIND_STRUCT 'bar'"} } */
+
+struct foo {
+  int a;
+  char c;
+};
+
+struct bar {
+  int x;
+  long z[4];
+};
+
+struct bar a_bar;
+
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c
new file mode 100644
index 000000000000..20183dffcc7f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c
@@ -0,0 +1,33 @@
+/* Test that -fprune-btf does not chase pointer-to-struct members.  */
+
+/* { dg-do compile } */
+/* { dg-options "-gbtf -fprune-btf -dA" } */
+
+/* Only use of B is via a pointer member of C.
+   Full BTF for B is replaced with a forward.  */
+/* { dg-final { scan-assembler-not "BTF_KIND_STRUCT 'B'" } } */
+/* { dg-final { scan-assembler-times "TYPE \[0-9\]+ BTF_KIND_FWD 'B'" 1 } } */
+
+/* Detailed info for B is omitted, and A is otherwise unused.  */
+/* { dg-final { scan-assembler-not "BTF_KIND_\[A-Z\]+ 'A'" } } */
+
+/* { dg-final { scan-assembler "BTF_KIND_STRUCT 'C'" } } */
+
+struct A;
+
+struct B {
+  int x;
+  int (*do_A_thing) (int, int);
+  struct A *other;
+};
+
+struct C {
+  unsigned int x;
+  struct B * a;
+};
+
+int
+foo (struct C *c)
+{
+  return c->x;
+}
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c
new file mode 100644
index 000000000000..57a079cf0b4d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c
@@ -0,0 +1,35 @@
+/* Test that -fprune-btf */
+
+/* { dg-do compile } */
+/* { dg-options "-gbtf -fprune-btf -dA" } */
+
+/* We expect full BTF information each struct.  */
+/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_FWD 'file'" } } */
+/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_STRUCT 'A'" } } */
+/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_STRUCT 'B'" } } */
+/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_STRUCT 'C'" } } */
+
+struct file;
+
+struct A {
+  void *private;
+  long (*read)(struct file *, char *, unsigned long);
+  long (*write)(struct file *, const char *, unsigned long);
+};
+
+struct B {
+  unsigned int x;
+  struct A **as;
+};
+
+struct C {
+  struct A *arr_a[4];
+  struct A *lone_a;
+  unsigned int z;
+};
+
+unsigned int
+foo (struct B *b, struct C *c)
+{
+  return b->x + c->z;
+}
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c
new file mode 100644
index 000000000000..bf3a25e984ff
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c
@@ -0,0 +1,20 @@
+/* Test special meaning of .maps section for BTF when pruning.  For global
+   variables of struct type placed in this section, we must treat members as
+   though they are used directly, always collecting pointee types.
+   Therefore, full type information for struct keep_me should be emitted.  */
+
+/* { dg-do compile } */
+/* { dg-options "-gbtf -fprune-btf -dA" } */
+
+/* { dg-final { scan-assembler-not "BTF_KIND_FWD 'keep_me'" } } */
+/* { dg-final { scan-assembler "BTF_KIND_STRUCT 'keep_me'" } } */
+
+struct keep_me {
+  int a;
+  char c;
+};
+
+struct {
+  int *key;
+  struct keep_me *value;
+} my_map __attribute__((section (".maps")));
-- 
2.43.0


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

* [PATCH v3 5/6] bpf,btf: enable BTF pruning by default for BPF
  2024-05-30 21:32 [PATCH v3 0/6] btf: refactor and add pruning option David Faust
                   ` (3 preceding siblings ...)
  2024-05-30 21:32 ` [PATCH v3 4/6] btf: add -fprune-btf option David Faust
@ 2024-05-30 21:32 ` David Faust
  2024-06-05 20:49   ` Indu Bhagat
  2024-05-30 21:32 ` [PATCH v3 6/6] opts: allow any combination of DWARF, CTF, BTF David Faust
  5 siblings, 1 reply; 16+ messages in thread
From: David Faust @ 2024-05-30 21:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: indu.bhagat, jose.marchesi, cupertino.miranda

This patch enables -fprune-btf by default in the BPF backend when
generating BTF information, and fixes BPF CO-RE generation when using
-fprune-btf.

When generating BPF CO-RE information, we must ensure that types used
in CO-RE relocations always have sufficient BTF information emited so
that the CO-RE relocations can be processed by a BPF loader.  The BTF
pruning algorithm on its own does not have sufficient information to
determine which types are used in a BPF CO-RE relocation, so this
information must be supplied by the BPF backend, using a new
btf_mark_type_used function.

Co-authored-by: Cupertino Miranda <cupertino.miranda@oracle.com>

gcc/
	* btfout.cc (btf_mark_type_used): New.
	* ctfc.h (btf_mark_type_used): Declare it here.
	* config/bpf/bpf.cc (bpf_option_override): Enable -fprune-btf
	by default if -gbtf is enabled.
	* config/bpf/core-builtins.cc (extra_fn): New typedef.
	(compute_field_expr): Add callback parameter, and call it if supplied.
	Fix computation for MEM_REF.
	(mark_component_type_as_used): New.
	(bpf_mark_types_as_used): Likewise.
	(bpf_expand_core_builtin): Call here.
	* doc/invoke.texi (Debugging Options): Note that -fprune-btf is
	enabled by default for BPF target when generating BTF.

gcc/testsuite/
	* gcc.dg/debug/btf/btf-variables-5.c: Add -fno-prune-btf to dg-options.
---
 gcc/btfout.cc                                 | 22 ++++++
 gcc/config/bpf/bpf.cc                         |  5 ++
 gcc/config/bpf/core-builtins.cc               | 71 +++++++++++++++++--
 gcc/ctfc.h                                    |  1 +
 gcc/doc/invoke.texi                           |  3 +
 .../gcc.dg/debug/btf/btf-variables-5.c        |  6 +-
 6 files changed, 100 insertions(+), 8 deletions(-)

diff --git a/gcc/btfout.cc b/gcc/btfout.cc
index a7da164f6b31..35d2875e3f61 100644
--- a/gcc/btfout.cc
+++ b/gcc/btfout.cc
@@ -1501,6 +1501,28 @@ btf_late_assign_datasec_ids (ctf_container_ref ctfc)
     }
 }
 
+
+/* Manually mark that type T is used to ensure it will not be pruned.
+   Used by the BPF backend when generating BPF CO-RE to mark types used
+   in CO-RE relocations.  */
+
+void
+btf_mark_type_used (tree t)
+{
+  /* If we are not going to prune anyway, this is a no-op.  */
+  if (!flag_prune_btf)
+    return;
+
+  gcc_assert (TYPE_P (t));
+  ctf_container_ref ctfc = ctf_get_tu_ctfc ();
+  ctf_dtdef_ref dtd = ctf_lookup_tree_type (ctfc, t);
+
+  if (!dtd)
+    return;
+
+  btf_add_used_type (ctfc, dtd, false, false, true);
+}
+
 /* Callback used for assembling the only-used-types list.  Note that this is
    the same as btf_type_list_cb above, but the hash_set traverse requires a
    different function signature.  */
diff --git a/gcc/config/bpf/bpf.cc b/gcc/config/bpf/bpf.cc
index dd1bfe38d29b..775730700eba 100644
--- a/gcc/config/bpf/bpf.cc
+++ b/gcc/config/bpf/bpf.cc
@@ -221,6 +221,11 @@ bpf_option_override (void)
       && !(target_flags_explicit & MASK_BPF_CORE))
     target_flags |= MASK_BPF_CORE;
 
+  /* -gbtf implies -fprune-btf for BPF target.  */
+  if (btf_debuginfo_p ())
+    SET_OPTION_IF_UNSET (&global_options, &global_options_set,
+			 flag_prune_btf, true);
+
   /* Determine available features from ISA setting (-mcpu=).  */
   if (bpf_has_jmpext == -1)
     bpf_has_jmpext = (bpf_isa >= ISA_V2);
diff --git a/gcc/config/bpf/core-builtins.cc b/gcc/config/bpf/core-builtins.cc
index 232bebcadbd5..86e2e9d6e39f 100644
--- a/gcc/config/bpf/core-builtins.cc
+++ b/gcc/config/bpf/core-builtins.cc
@@ -624,13 +624,20 @@ bpf_core_get_index (const tree node, bool *valid)
 
    ALLOW_ENTRY_CAST is an input arguments and specifies if the function should
    consider as valid expressions in which NODE entry is a cast expression (or
-   tree code nop_expr).  */
+   tree code nop_expr).
+
+   EXTRA_FN is a callback function to allow extra functionality with this
+   function traversal.  Currently used for marking used type during expand
+   pass.  */
+
+typedef void (*extra_fn) (tree);
 
 static unsigned char
 compute_field_expr (tree node, unsigned int *accessors,
 		    bool *valid,
 		    tree *access_node,
-		    bool allow_entry_cast = true)
+		    bool allow_entry_cast = true,
+		    extra_fn callback = NULL)
 {
   unsigned char n = 0;
   unsigned int fake_accessors[MAX_NR_ACCESSORS];
@@ -647,6 +654,9 @@ compute_field_expr (tree node, unsigned int *accessors,
 
   *access_node = node;
 
+  if (callback != NULL)
+    callback (node);
+
   switch (TREE_CODE (node))
     {
     case INDIRECT_REF:
@@ -664,17 +674,19 @@ compute_field_expr (tree node, unsigned int *accessors,
     case COMPONENT_REF:
       n = compute_field_expr (TREE_OPERAND (node, 0), accessors,
 			      valid,
-			      access_node, false);
+			      access_node, false, callback);
       accessors[n] = bpf_core_get_index (TREE_OPERAND (node, 1), valid);
       return n + 1;
     case ARRAY_REF:
     case ARRAY_RANGE_REF:
-    case MEM_REF:
       n = compute_field_expr (TREE_OPERAND (node, 0), accessors,
 			      valid,
-			      access_node, false);
+			      access_node, false, callback);
       accessors[n++] = bpf_core_get_index (node, valid);
       return n;
+    case MEM_REF:
+      accessors[0] = bpf_core_get_index (node, valid);
+      return 1;
     case NOP_EXPR:
       if (allow_entry_cast == true)
 	{
@@ -683,7 +695,7 @@ compute_field_expr (tree node, unsigned int *accessors,
 	}
       n = compute_field_expr (TREE_OPERAND (node, 0), accessors,
 			      valid,
-			      access_node, false);
+			      access_node, false, callback);
       return n;
 
     case ADDR_EXPR:
@@ -1549,6 +1561,51 @@ bpf_resolve_overloaded_core_builtin (location_t loc, tree fndecl,
   return construct_builtin_core_reloc (loc, fndecl, args, argsvec->length ());
 }
 
+/* Callback function for bpf_mark_field_expr_types_as_used.  */
+
+static void
+mark_component_type_as_used (tree node)
+{
+  if (TREE_CODE (node) == COMPONENT_REF)
+    btf_mark_type_used (TREE_TYPE (TREE_OPERAND (node, 0)));
+}
+
+/* Mark types needed for BPF CO-RE relocations as used.  Doing so ensures that
+   these types do not get pruned from the BTF information.  */
+
+static void
+bpf_mark_types_as_used (struct cr_builtins *data)
+{
+  tree expr = data->expr;
+  switch (data->kind)
+    {
+    case BPF_RELO_FIELD_BYTE_OFFSET:
+    case BPF_RELO_FIELD_BYTE_SIZE:
+    case BPF_RELO_FIELD_EXISTS:
+    case BPF_RELO_FIELD_SIGNED:
+    case BPF_RELO_FIELD_LSHIFT_U64:
+    case BPF_RELO_FIELD_RSHIFT_U64:
+      if (TREE_CODE (expr) == ADDR_EXPR)
+	expr = TREE_OPERAND (expr, 0);
+
+      expr = root_for_core_field_info (expr);
+      compute_field_expr (data->expr, NULL, NULL, NULL, false,
+			  mark_component_type_as_used);
+      break;
+    case BPF_RELO_TYPE_ID_LOCAL:
+    case BPF_RELO_TYPE_ID_TARGET:
+    case BPF_RELO_TYPE_EXISTS:
+    case BPF_RELO_TYPE_SIZE:
+    case BPF_RELO_ENUMVAL_EXISTS:
+    case BPF_RELO_ENUMVAL_VALUE:
+    case BPF_RELO_TYPE_MATCHES:
+      btf_mark_type_used (data->type);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+}
+
 /* Used in bpf_expand_builtin.  This function is called in RTL expand stage to
    convert the internal __builtin_core_reloc in unspec:UNSPEC_CORE_RELOC RTL,
    which will contain a third argument that is the index in the vec collected
@@ -1567,6 +1624,8 @@ bpf_expand_core_builtin (tree exp, enum bpf_builtins code)
 	tree index = CALL_EXPR_ARG (exp, 0);
 	struct cr_builtins *data = get_builtin_data (TREE_INT_CST_LOW (index));
 
+	bpf_mark_types_as_used (data);
+
 	rtx v = expand_normal (data->default_value);
 	rtx i = expand_normal (index);
 	  return gen_rtx_UNSPEC (DImode,
diff --git a/gcc/ctfc.h b/gcc/ctfc.h
index 29267dc036d1..41e1169f271d 100644
--- a/gcc/ctfc.h
+++ b/gcc/ctfc.h
@@ -457,6 +457,7 @@ extern ctf_dtdef_ref ctf_lookup_tree_type (ctf_container_ref, const tree);
 
 typedef bool (*funcs_traverse_callback) (ctf_dtdef_ref, void *);
 bool traverse_btf_func_types (funcs_traverse_callback, void *);
+extern void btf_mark_type_used (tree);
 
 /* CTF section does not emit location information; at this time, location
    information is needed for BTF CO-RE use-cases.  */
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 6dac00d14381..8edf0914fddb 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -12312,6 +12312,9 @@ It is primarily useful when compiling for the BPF target, to minimize
 the size of the resulting object, and to eliminate BTF information
 which is not immediately relevant to the BPF program loading process.
 
+This option is enabled by default for the BPF target when generating
+BTF information.
+
 @opindex femit-struct-debug-baseonly
 @item -femit-struct-debug-baseonly
 Emit debug information for struct-like types
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-variables-5.c b/gcc/testsuite/gcc.dg/debug/btf/btf-variables-5.c
index 8aae76cacabd..7cd8728ecaf6 100644
--- a/gcc/testsuite/gcc.dg/debug/btf/btf-variables-5.c
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-variables-5.c
@@ -11,9 +11,11 @@
 /* { dg-final { scan-assembler-times "\[\t \]0xe000000\[\t \]+\[^\n\]*btv_info" 1 } } */
 /* { dg-final { scan-assembler-times "\[\t \]0x1\[\t \]+\[^\n\]*btv_linkage" 1 } } */
 
-/* Expect 2 array types, one of which is unsized.  */
+/* Expect 2 array types, one of which is unsized.  For BPF target, -fprune-btf
+   is the default and will remove the unsized array type.  */
 /* { dg-final { scan-assembler-times "\[\t \]0x4\[\t \]+\[^\n\]*bta_nelems" 1 } } */
-/* { dg-final { scan-assembler-times "\[\t \]0\[\t \]+\[^\n\]*bta_nelems" 1 } } */
+/* { dg-final { scan-assembler-times "\[\t \]0\[\t \]+\[^\n\]*bta_nelems" 1 { target { !bpf-*-* } } } } */
+/* { dg-final { scan-assembler-times "\[\t \]0\[\t \]+\[^\n\]*bta_nelems" 0 { target { bpf-*-* } } } } */
 
 extern const char FOO[];
 const char FOO[] = "foo";
-- 
2.43.0


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

* [PATCH v3 6/6] opts: allow any combination of DWARF, CTF, BTF
  2024-05-30 21:32 [PATCH v3 0/6] btf: refactor and add pruning option David Faust
                   ` (4 preceding siblings ...)
  2024-05-30 21:32 ` [PATCH v3 5/6] bpf,btf: enable BTF pruning by default for BPF David Faust
@ 2024-05-30 21:32 ` David Faust
  2024-06-05 22:16   ` Indu Bhagat
  5 siblings, 1 reply; 16+ messages in thread
From: David Faust @ 2024-05-30 21:32 UTC (permalink / raw)
  To: gcc-patches; +Cc: indu.bhagat, jose.marchesi, cupertino.miranda

Previously it was not supported to generate both CTF and BTF debug info
in the same compiler run, as both formats made incompatible changes to
the same internal data structures.

With the structural change in the prior patches, in particular the
guarantee that CTF will always be fully emitted before any BTF
translation occurs, there is no longer anything preventing generation
of both CTF and BTF at the same time.  This patch changes option parsing
to allow any combination of -gdwarf, -gctf, and -gbtf at the same time.

gcc/
	* opts.cc (set_debug_level): Allow any combination of -gdwarf,
	-gctf and -gbtf at the same time.
---
 gcc/opts.cc | 20 +++++++-------------
 1 file changed, 7 insertions(+), 13 deletions(-)

diff --git a/gcc/opts.cc b/gcc/opts.cc
index f80d5d4ba8f9..d58bea096a5f 100644
--- a/gcc/opts.cc
+++ b/gcc/opts.cc
@@ -3505,21 +3505,15 @@ set_debug_level (uint32_t dinfo, int extended, const char *arg,
     }
   else
     {
-      /* Make and retain the choice if both CTF and DWARF debug info are to
-	 be generated.  */
-      if (((dinfo == DWARF2_DEBUG) || (dinfo == CTF_DEBUG))
+      /* Any combination of DWARF, CTF and BTF is allowed together.  */
+      if (((dinfo == DWARF2_DEBUG) || (dinfo == CTF_DEBUG)
+	   || (dinfo == BTF_DEBUG))
 	  && ((opts->x_write_symbols == (DWARF2_DEBUG|CTF_DEBUG))
+	      || (opts->x_write_symbols == (DWARF2_DEBUG|BTF_DEBUG))
+	      || (opts->x_write_symbols == (CTF_DEBUG|BTF_DEBUG))
 	      || (opts->x_write_symbols == DWARF2_DEBUG)
-	      || (opts->x_write_symbols == CTF_DEBUG)))
-	{
-	  opts->x_write_symbols |= dinfo;
-	  opts_set->x_write_symbols |= dinfo;
-	}
-      /* However, CTF and BTF are not allowed together at this time.  */
-      else if (((dinfo == DWARF2_DEBUG) || (dinfo == BTF_DEBUG))
-	       && ((opts->x_write_symbols == (DWARF2_DEBUG|BTF_DEBUG))
-		   || (opts->x_write_symbols == DWARF2_DEBUG)
-		   || (opts->x_write_symbols == BTF_DEBUG)))
+	      || (opts->x_write_symbols == CTF_DEBUG)
+	      || (opts->x_write_symbols == BTF_DEBUG)))
 	{
 	  opts->x_write_symbols |= dinfo;
 	  opts_set->x_write_symbols |= dinfo;
-- 
2.43.0


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

* Re: [PATCH v3 4/6] btf: add -fprune-btf option
  2024-05-30 21:32 ` [PATCH v3 4/6] btf: add -fprune-btf option David Faust
@ 2024-05-31  7:07   ` Richard Biener
  2024-05-31 15:57     ` David Faust
  2024-06-05  7:56   ` Indu Bhagat
  1 sibling, 1 reply; 16+ messages in thread
From: Richard Biener @ 2024-05-31  7:07 UTC (permalink / raw)
  To: David Faust; +Cc: gcc-patches, indu.bhagat, jose.marchesi, cupertino.miranda

On Thu, May 30, 2024 at 11:34 PM David Faust <david.faust@oracle.com> wrote:
>
> This patch adds a new option, -fprune-btf, to control BTF debug info
> generation.

Can you name it -gprune-btf instead?

> As the name implies, this option enables a kind of "pruning" of the BTF
> information before it is emitted.  When enabled, rather than emitting
> all type information translated from DWARF, only information for types
> directly used in the source program is emitted.
>
> The primary purpose of this pruning is to reduce the amount of
> unnecessary BTF information emitted, especially for BPF programs.  It is
> very common for BPF programs to incldue Linux kernel internal headers in
> order to have access to kernel data structures.  However, doing so often
> has the side effect of also adding type definitions for a large number
> of types which are not actually used by nor relevant to the program.
> In these cases, -fprune-btf commonly reduces the size of the resulting
> BTF information by 10x or more, as seen on average when compiling Linux
> kernel BPF selftests.  This both slims down the size of the resulting
> object and reduces the time required by the BPF loader to verify the
> program and its BTF information.
>
> Note that the pruning implemented in this patch follows the same rules
> as the BTF pruning performed unconditionally by LLVM's BPF backend when
> generating BTF.  In particular, the main sources of pruning are:
>
>   1) Only generate BTF for types used by variables and functions at
>      the file scope.  Note that with or without pruning, BTF_KIND_VAR
>      entries are only generated for variables present in the final
>      object - unused static variables or variables completely optimized
>      away must not have VAR entries in BTF.
>
>   2) Avoid emitting full BTF for struct and union types which are only
>      pointed-to by members of other struct/union types.  In these cases,
>      the full BTF_KIND_STRUCT or BTF_KIND_UNION which would normally
>      be emitted is replaced with a BTF_KIND_FWD, as though the
>      underlying type was a forward-declared struct or union type.
>
> gcc/
>         * btfout.cc (btf_used_types): New hash set.
>         (struct btf_fixup): New.
>         (fixups, forwards): New vecs.
>         (btf_output): Calculate num_types depending on flag_prune_btf.
>         (btf_early_finsih): New initialization for flag_prune_btf.
>         (btf_add_used_type): New function.
>         (btf_used_type_list_cb): Likewise.
>         (btf_late_collect_pruned_types): Likewise.
>         (btf_late_add_vars): Handle special case for variables in ".maps"
>         section when generating BTF for BPF CO-RE target.
>         (btf_late_finish): Use btf_late_collect_pruned_types when
>         flag_prune_btf in effect.  Move some initialization to btf_early_finish.
>         (btf_finalize): Additional deallocation for flag_prune_btf.
>         * common.opt (fprune-btf): New flag.
>         * ctfc.cc (init_ctf_strtable): Make non-static.
>         * ctfc.h (struct ctf_dtdef): Add visited_children_p boolean flag.
>         (init_ctf_strtable, ctfc_delete_strtab): Make extern.
>         * doc/invoke.texi (Debugging Options): Document -fprune-btf.
>
> gcc/testsuite/
>         * gcc.dg/debug/btf/btf-prune-1.c: New test.
>         * gcc.dg/debug/btf/btf-prune-2.c: Likewise.
>         * gcc.dg/debug/btf/btf-prune-3.c: Likewise.
>         * gcc.dg/debug/btf/btf-prune-maps.c: Likewise.
> ---
>  gcc/btfout.cc                                 | 359 +++++++++++++++++-
>  gcc/common.opt                                |   4 +
>  gcc/ctfc.cc                                   |   2 +-
>  gcc/ctfc.h                                    |   3 +
>  gcc/doc/invoke.texi                           |  20 +
>  gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c  |  25 ++
>  gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c  |  33 ++
>  gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c  |  35 ++
>  .../gcc.dg/debug/btf/btf-prune-maps.c         |  20 +
>  9 files changed, 494 insertions(+), 7 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c
>  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c
>  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c
>
> diff --git a/gcc/btfout.cc b/gcc/btfout.cc
> index 32fda14f704b..a7da164f6b31 100644
> --- a/gcc/btfout.cc
> +++ b/gcc/btfout.cc
> @@ -828,7 +828,10 @@ output_btf_types (ctf_container_ref ctfc)
>  {
>    size_t i;
>    size_t num_types;
> -  num_types = ctfc->ctfc_types->elements ();
> +  if (flag_prune_btf)
> +    num_types = max_translated_id;
> +  else
> +    num_types = ctfc->ctfc_types->elements ();
>
>    if (num_types)
>      {
> @@ -957,6 +960,212 @@ btf_early_add_func_records (ctf_container_ref ctfc)
>      }
>  }
>
> +/* The set of types used directly in the source program, and any types manually
> +   marked as used.  This is the set of types which will be emitted when
> +   flag_prune_btf is set.  */
> +static GTY (()) hash_set<ctf_dtdef_ref> *btf_used_types;
> +
> +/* Fixup used to avoid unnecessary pointer chasing for types.  A fixup is
> +   created when a structure or union member is a pointer to another struct
> +   or union type.  In such cases, avoid emitting full type information for
> +   the pointee struct or union type (which may be quite large), unless that
> +   type is used directly elsewhere.  */
> +struct btf_fixup
> +{
> +  ctf_dtdef_ref pointer_dtd; /* Type node to which the fixup is applied.  */
> +  ctf_dtdef_ref pointee_dtd; /* Original type node referred to by pointer_dtd.
> +                               If this concrete type is not otherwise used,
> +                               then a forward is created.  */
> +};
> +
> +/* Stores fixups while processing types.  */
> +static vec<struct btf_fixup> fixups;
> +
> +/* For fixups where the underlying type is not used in the end, a BTF_KIND_FWD
> +   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.
> +   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
> +   or union member.  SEEN_PTR is true if CHECK_PTR is true AND one of the
> +   predecessors was a pointer type.  These two flags are used to avoid chasing
> +   pointers to struct/union only used from pointer members.  For such types, we
> +   will emit a forward instead of the full type information, unless
> +   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)
> +{
> +  if (dtd == NULL)
> +    return NULL;
> +
> +  uint32_t ctf_kind = CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info);
> +  uint32_t kind = get_btf_kind (ctf_kind);
> +
> +  /* Check whether the type has already been added.  */
> +  if (btf_used_types->contains (dtd))
> +    {
> +      /* It's possible the type was already added as a fixup, but that we now
> +        have a concrete use of it.  */
> +      switch (kind)
> +       {
> +       case BTF_KIND_PTR:
> +       case BTF_KIND_TYPEDEF:
> +       case BTF_KIND_CONST:
> +       case BTF_KIND_VOLATILE:
> +       case BTF_KIND_RESTRICT:
> +         if (check_ptr)
> +           /* Type was previously added as a fixup, and that's OK.  */
> +           return dtd;
> +         else
> +           {
> +             /* The type was previously added as a fixup, but now we have
> +                a concrete use of it.  Remove the fixup.  */
> +             for (size_t i = 0; i < fixups.length (); i++)
> +               if (fixups[i].pointer_dtd == 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);
> +             return dtd;
> +           }
> +       default:
> +         return dtd;
> +       }
> +    }
> +
> +  if (ctf_kind == CTF_K_SLICE)
> +    {
> +      /* Bitfield.  Add the underlying type to the used set, but leave
> +        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);
> +      return dtd;
> +    }
> +
> +  /* Skip redundant definitions of void and types with no BTF encoding.  */
> +  if ((kind == BTF_KIND_INT && dtd->dtd_data.ctti_size == 0)
> +      || (kind == BTF_KIND_UNKN))
> +    return NULL;
> +
> +  /* Add the type itself, and assign its id.
> +     Do this before recursing to handle things like linked list structures.  */
> +  gcc_assert (ctfc->ctfc_nextid <= BTF_MAX_TYPE);
> +  dtd->dtd_type = ctfc->ctfc_nextid++;
> +  btf_used_types->add (dtd);
> +  ctf_add_string (ctfc, dtd->dtd_name, &(dtd->dtd_data.ctti_name), CTF_STRTAB);
> +  ctfc->ctfc_num_types++;
> +  ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (dtd);
> +
> +  /* Recursively add types referenced by this type.  */
> +  switch (kind)
> +    {
> +    case BTF_KIND_INT:
> +    case BTF_KIND_FLOAT:
> +    case BTF_KIND_FWD:
> +      /* Leaf kinds which do not refer to any other types.  */
> +      break;
> +
> +    case BTF_KIND_FUNC:
> +    case BTF_KIND_VAR:
> +      /* Root kinds; no type we are visiting may refer to these.  */
> +      gcc_unreachable ();
> +
> +    case BTF_KIND_PTR:
> +    case BTF_KIND_TYPEDEF:
> +    case BTF_KIND_CONST:
> +    case BTF_KIND_VOLATILE:
> +    case BTF_KIND_RESTRICT:
> +      {
> +       /* These type kinds refer to exactly one other type.  */
> +       if (check_ptr && !seen_ptr)
> +         seen_ptr = (kind == BTF_KIND_PTR);
> +
> +       /* Try to avoid chasing pointers to struct/union types if the
> +          underlying type isn't used.  */
> +       if (check_ptr && seen_ptr && create_fixups)
> +         {
> +           ctf_dtdef_ref ref = dtd->ref_type;
> +           uint32_t ref_kind = btf_dtd_kind (ref);
> +
> +           if ((ref_kind == BTF_KIND_STRUCT || ref_kind == BTF_KIND_UNION)
> +               && !btf_used_types->contains (ref))
> +             {
> +               struct btf_fixup fixup;
> +               fixup.pointer_dtd = dtd;
> +               fixup.pointee_dtd = ref;
> +               fixups.safe_push (fixup);
> +               break;
> +             }
> +         }
> +
> +       /* 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);
> +       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);
> +       break;
> +      }
> +    case BTF_KIND_STRUCT:
> +    case BTF_KIND_UNION:
> +    case BTF_KIND_ENUM:
> +    case BTF_KIND_ENUM64:
> +      {
> +       /* Add members.  */
> +       ctf_dmdef_t *dmd;
> +       for (dmd = dtd->dtd_u.dtu_members;
> +            dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
> +         {
> +           /* 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);
> +           ctf_add_string (ctfc, dmd->dmd_name, &(dmd->dmd_name_offset),
> +                           CTF_STRTAB);
> +         }
> +       break;
> +      }
> +    case BTF_KIND_FUNC_PROTO:
> +      {
> +       /* Add return type.  */
> +       dtd->ref_type = btf_add_used_type (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);
> +           /* 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.  */
> +         }
> +       break;
> +      }
> +    default:
> +      return NULL;
> +    }
> +
> +  return dtd;
> +}
> +
>  /* 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
> @@ -972,6 +1181,27 @@ btf_early_finish (void)
>
>    btf_early_add_const_void (tu_ctfc);
>    btf_early_add_func_records (tu_ctfc);
> +
> +  /* Note: from here on, destructive changes are made to the TU CTFC to
> +     translate CTF to BTF.  These fields are reset to count BTF types etc.  */
> +  tu_ctfc->ctfc_num_types = 0;
> +  tu_ctfc->ctfc_num_vlen_bytes = 0;
> +  tu_ctfc->ctfc_vars_list_count = 0;
> +
> +  if (flag_prune_btf)
> +    {
> +      btf_used_types
> +       = hash_set<ctf_dtdef_ref>::create_ggc (tu_ctfc->ctfc_types->elements ());
> +      tu_ctfc->ctfc_nextid = 1;
> +      fixups.create (1);
> +
> +      /* Empty the string table, which was already populated with strings for
> +        all types translated from DWARF.  We may only need a very small subset
> +        of these strings; those will be re-added below.  */
> +      ctfc_delete_strtab (&tu_ctfc->ctfc_strtable);
> +      init_ctf_strtable (&tu_ctfc->ctfc_strtable);
> +      tu_ctfc->ctfc_strlen++;
> +    }
>  }
>
>  /* Push a BTF datasec entry ENTRY into the datasec named SECNAME,
> @@ -1154,6 +1384,25 @@ btf_late_add_vars (ctf_container_ref ctfc)
>
>        /* Add a BTF_KIND_DATASEC entry for the variable.  */
>        btf_datasec_add_var (ctfc, var, dvd);
> +
> +      const char *section = var->get_section ();
> +      if (section && (strcmp (section, ".maps") == 0) && flag_prune_btf)
> +       {
> +         /* The .maps section has special meaning in BTF: it is used for BPF
> +            map definitions.  These definitions should be structs.  We must
> +            collect pointee types used in map members as though they are used
> +            directly, effectively ignoring (from the pruning perspective) that
> +            they are struct members.  */
> +         ctf_dtdef_ref dtd = dvd->dvd_type;
> +         uint32_t kind = btf_dtd_kind (dvd->dvd_type);
> +         if (kind == BTF_KIND_STRUCT)
> +           {
> +             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);
> +           }
> +       }
>      }
>  }
>
> @@ -1252,6 +1501,86 @@ btf_late_assign_datasec_ids (ctf_container_ref ctfc)
>      }
>  }
>
> +/* Callback used for assembling the only-used-types list.  Note that this is
> +   the same as btf_type_list_cb above, but the hash_set traverse requires a
> +   different function signature.  */
> +
> +static bool
> +btf_used_type_list_cb (const ctf_dtdef_ref& dtd, ctf_container_ref ctfc)
> +{
> +  ctfc->ctfc_types_list[dtd->dtd_type] = dtd;
> +  return true;
> +}
> +
> +/* Collect the set of types reachable from global variables and functions.
> +   This is the minimal set of types, used when generating pruned BTF.  */
> +
> +static void
> +btf_late_collect_pruned_types (ctf_container_ref ctfc)
> +{
> +  vec_alloc (forwards, 1);
> +
> +  /* Add types used from functions.  */
> +  ctf_dtdef_ref dtd;
> +  size_t i;
> +  FOR_EACH_VEC_ELT (*funcs, i, dtd)
> +    {
> +      btf_add_used_type (ctfc, dtd->ref_type, false, false, true);
> +      ctf_add_string (ctfc, dtd->dtd_name, &(dtd->dtd_data.ctti_name),
> +                     CTF_STRTAB);
> +    }
> +
> +  /* Add types used from global variables.  */
> +  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);
> +      ctf_add_string (ctfc, dvd->dvd_name, &(dvd->dvd_name_offset), CTF_STRTAB);
> +    }
> +
> +  /* 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.  */
> +  for (i = 0; i < fixups.length (); i++)
> +    {
> +      struct btf_fixup *fx = &fixups[i];
> +      if (!btf_used_types->contains (fx->pointee_dtd))
> +       {
> +         /* The underlying type is not used.  Create a forward.  */
> +         ctf_dtdef_ref fwd = ggc_cleared_alloc<ctf_dtdef_t> ();
> +         ctf_id_t id = ctfc->ctfc_nextid++;
> +         gcc_assert (id <= BTF_MAX_TYPE);
> +
> +         bool union_p = (btf_dtd_kind (fx->pointee_dtd) == BTF_KIND_UNION);
> +
> +         fwd->dtd_name = fx->pointee_dtd->dtd_name;
> +         fwd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_FORWARD, union_p, 0);
> +         fwd->dtd_type = id;
> +         ctfc->ctfc_num_types++;
> +         ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (fwd);
> +         ctf_add_string (ctfc, fwd->dtd_name, &(fwd->dtd_data.ctti_name),
> +                         CTF_STRTAB);
> +
> +         /* Update the pointer to point to the forward.  */
> +         fx->pointer_dtd->ref_type = fwd;
> +         vec_safe_push (forwards, fwd);
> +       }
> +    }
> +
> +  /* Construct the resulting pruned type list.  */
> +  ctfc->ctfc_types_list
> +    = ggc_vec_alloc<ctf_dtdef_ref> (btf_used_types->elements () + 1
> +                                   + vec_safe_length (forwards));
> +
> +  btf_used_types->traverse<ctf_container_ref, btf_used_type_list_cb> (ctfc);
> +
> +  /* Insert the newly created forwards into the regular types list too.  */
> +  FOR_EACH_VEC_ELT (*forwards, i, dtd)
> +    ctfc->ctfc_types_list[dtd->dtd_type] = dtd;
> +
> +  max_translated_id = btf_used_types->elements () + vec_safe_length (forwards);
> +}
> +
>  /* Late entry point for BTF generation, called from dwarf2out_finish ().
>     Complete and emit BTF information.  */
>
> @@ -1263,13 +1592,22 @@ btf_finish (void)
>
>    datasecs.create (0);
>
> -  tu_ctfc->ctfc_num_types = 0;
> -  tu_ctfc->ctfc_num_vlen_bytes = 0;
> -  tu_ctfc->ctfc_vars_list_count = 0;
> -
>    btf_late_add_vars (tu_ctfc);
> -  btf_late_collect_translated_types (tu_ctfc);
> +  if (flag_prune_btf)
> +    {
> +      /* Collect pruned set of BTF types and prepare for emission.
> +        This includes only types directly used in file-scope variables and
> +        function return/argument types.  */
> +      btf_late_collect_pruned_types (tu_ctfc);
> +    }
> +  else
> +    {
> +      /* Collect all BTF types and prepare for emission.
> +        This includes all types translated from DWARF.  */
> +      btf_late_collect_translated_types (tu_ctfc);
> +    }
>    btf_late_add_func_datasec_entries (tu_ctfc);
> +
>    btf_late_assign_var_ids (tu_ctfc);
>    btf_late_assign_func_ids (tu_ctfc);
>    btf_late_assign_datasec_ids (tu_ctfc);
> @@ -1302,6 +1640,15 @@ btf_finalize (void)
>    func_map->empty ();
>    func_map = NULL;
>
> +  if (flag_prune_btf)
> +    {
> +      btf_used_types->empty ();
> +      btf_used_types = NULL;
> +
> +      fixups.release ();
> +      forwards = NULL;
> +    }
> +
>    ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
>    ctfc_delete_container (tu_ctfc);
>    tu_ctfc = NULL;
> diff --git a/gcc/common.opt b/gcc/common.opt
> index 2c078fdd1f86..3d4a55d02c26 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -2588,6 +2588,10 @@ fpatchable-function-entry=
>  Common Var(flag_patchable_function_entry) Joined Optimization
>  Insert NOP instructions at each function entry.
>
> +fprune-btf
> +Common Var(flag_prune_btf) Init(0)
> +Generate minimal BTF debug info.
> +
>  frandom-seed
>  Common Var(common_deferred_options) Defer
>
> diff --git a/gcc/ctfc.cc b/gcc/ctfc.cc
> index 678ca2a072cf..27fc3a6d5851 100644
> --- a/gcc/ctfc.cc
> +++ b/gcc/ctfc.cc
> @@ -913,7 +913,7 @@ ctfc_get_dvd_srcloc (ctf_dvdef_ref dvd, ctf_srcloc_ref loc)
>  /* Initialize the CTF string table.
>     The first entry in the CTF string table (empty string) is added.  */
>
> -static void
> +void
>  init_ctf_strtable (ctf_strtable_t * strtab)
>  {
>    strtab->ctstab_head = NULL;
> diff --git a/gcc/ctfc.h b/gcc/ctfc.h
> index d0b724817a7f..29267dc036d1 100644
> --- a/gcc/ctfc.h
> +++ b/gcc/ctfc.h
> @@ -369,6 +369,9 @@ extern unsigned int ctfc_get_num_ctf_vars (ctf_container_ref);
>
>  extern ctf_strtable_t * ctfc_get_strtab (ctf_container_ref, int);
>
> +extern void init_ctf_strtable (ctf_strtable_t *);
> +extern void ctfc_delete_strtab (ctf_strtable_t *);
> +
>  /* Get the length of the specified string table in the CTF container.  */
>
>  extern size_t ctfc_get_strtab_len (ctf_container_ref, int);
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 517a782987d9..6dac00d14381 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -534,6 +534,7 @@ Objective-C and Objective-C++ Dialects}.
>  -gvms -gz@r{[}=@var{type}@r{]}
>  -gsplit-dwarf  -gdescribe-dies  -gno-describe-dies
>  -fdebug-prefix-map=@var{old}=@var{new}  -fdebug-types-section
> +-fprune-btf -fno-prune-btf
>  -fno-eliminate-unused-debug-types
>  -femit-struct-debug-baseonly  -femit-struct-debug-reduced
>  -femit-struct-debug-detailed@r{[}=@var{spec-list}@r{]}
> @@ -12292,6 +12293,25 @@ compressed debug sections, the option is rejected.  Otherwise, if the
>  assembler does not support them, @option{-gz} is silently ignored when
>  producing object files.
>
> +@opindex fprune-btf
> +@opindex fno-prune-btf
> +@item -fprune-btf
> +@itemx -fno-prune-btf
> +Prune BTF information before emission.  When pruning, only type
> +information for types used by global variables and file-scope functions
> +will be emitted.  If compiling for the BPF target with BPF CO-RE
> +enabled, type information will also be emitted for types used in BPF
> +CO-RE relocations.  In addition, struct and union types which are only
> +referred to via pointers from members of other struct or union types
> +shall be pruned and replaced with BTF_KIND_FWD, as though those types
> +were only present in the input as forward declarations.
> +
> +This option substantially reduces the size of produced BTF information,
> +but at significant loss in the amount of detailed type information.
> +It is primarily useful when compiling for the BPF target, to minimize
> +the size of the resulting object, and to eliminate BTF information
> +which is not immediately relevant to the BPF program loading process.
> +
>  @opindex femit-struct-debug-baseonly
>  @item -femit-struct-debug-baseonly
>  Emit debug information for struct-like types
> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c
> new file mode 100644
> index 000000000000..3c9b59a07ecf
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c
> @@ -0,0 +1,25 @@
> +/* Simple test of -fprune-btf option operation.
> +   Since 'struct foo' is not used, no BTF shall be emitted for it.  */
> +
> +/* { dg-do compile } */
> +/* { dg-options "-gbtf -fprune-btf -dA" } */
> +
> +/* No BTF info for 'struct foo' nor types used only by it.  */
> +/* { dg-final { scan-assembler-not "BTF_KIND_STRUCT 'foo'" } } */
> +/* { dg-final { scan-assembler-not "BTF_KIND_INT 'char'" } } */
> +
> +/* We should get BTF info for 'struct bar' since it is used.  */
> +/* { dg-final { scan-assembler "BTF_KIND_STRUCT 'bar'"} } */
> +
> +struct foo {
> +  int a;
> +  char c;
> +};
> +
> +struct bar {
> +  int x;
> +  long z[4];
> +};
> +
> +struct bar a_bar;
> +
> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c
> new file mode 100644
> index 000000000000..20183dffcc7f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c
> @@ -0,0 +1,33 @@
> +/* Test that -fprune-btf does not chase pointer-to-struct members.  */
> +
> +/* { dg-do compile } */
> +/* { dg-options "-gbtf -fprune-btf -dA" } */
> +
> +/* Only use of B is via a pointer member of C.
> +   Full BTF for B is replaced with a forward.  */
> +/* { dg-final { scan-assembler-not "BTF_KIND_STRUCT 'B'" } } */
> +/* { dg-final { scan-assembler-times "TYPE \[0-9\]+ BTF_KIND_FWD 'B'" 1 } } */
> +
> +/* Detailed info for B is omitted, and A is otherwise unused.  */
> +/* { dg-final { scan-assembler-not "BTF_KIND_\[A-Z\]+ 'A'" } } */
> +
> +/* { dg-final { scan-assembler "BTF_KIND_STRUCT 'C'" } } */
> +
> +struct A;
> +
> +struct B {
> +  int x;
> +  int (*do_A_thing) (int, int);
> +  struct A *other;
> +};
> +
> +struct C {
> +  unsigned int x;
> +  struct B * a;
> +};
> +
> +int
> +foo (struct C *c)
> +{
> +  return c->x;
> +}
> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c
> new file mode 100644
> index 000000000000..57a079cf0b4d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c
> @@ -0,0 +1,35 @@
> +/* Test that -fprune-btf */
> +
> +/* { dg-do compile } */
> +/* { dg-options "-gbtf -fprune-btf -dA" } */
> +
> +/* We expect full BTF information each struct.  */
> +/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_FWD 'file'" } } */
> +/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_STRUCT 'A'" } } */
> +/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_STRUCT 'B'" } } */
> +/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_STRUCT 'C'" } } */
> +
> +struct file;
> +
> +struct A {
> +  void *private;
> +  long (*read)(struct file *, char *, unsigned long);
> +  long (*write)(struct file *, const char *, unsigned long);
> +};
> +
> +struct B {
> +  unsigned int x;
> +  struct A **as;
> +};
> +
> +struct C {
> +  struct A *arr_a[4];
> +  struct A *lone_a;
> +  unsigned int z;
> +};
> +
> +unsigned int
> +foo (struct B *b, struct C *c)
> +{
> +  return b->x + c->z;
> +}
> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c
> new file mode 100644
> index 000000000000..bf3a25e984ff
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c
> @@ -0,0 +1,20 @@
> +/* Test special meaning of .maps section for BTF when pruning.  For global
> +   variables of struct type placed in this section, we must treat members as
> +   though they are used directly, always collecting pointee types.
> +   Therefore, full type information for struct keep_me should be emitted.  */
> +
> +/* { dg-do compile } */
> +/* { dg-options "-gbtf -fprune-btf -dA" } */
> +
> +/* { dg-final { scan-assembler-not "BTF_KIND_FWD 'keep_me'" } } */
> +/* { dg-final { scan-assembler "BTF_KIND_STRUCT 'keep_me'" } } */
> +
> +struct keep_me {
> +  int a;
> +  char c;
> +};
> +
> +struct {
> +  int *key;
> +  struct keep_me *value;
> +} my_map __attribute__((section (".maps")));
> --
> 2.43.0
>

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

* Re: [PATCH v3 4/6] btf: add -fprune-btf option
  2024-05-31  7:07   ` Richard Biener
@ 2024-05-31 15:57     ` David Faust
  2024-05-31 16:24       ` Richard Biener
  0 siblings, 1 reply; 16+ messages in thread
From: David Faust @ 2024-05-31 15:57 UTC (permalink / raw)
  To: Richard Biener; +Cc: gcc-patches, indu.bhagat, jose.marchesi, cupertino.miranda



On 5/31/24 00:07, Richard Biener wrote:
> On Thu, May 30, 2024 at 11:34 PM David Faust <david.faust@oracle.com> wrote:
>>
>> This patch adds a new option, -fprune-btf, to control BTF debug info
>> generation.
> 
> Can you name it -gprune-btf instead?

Yes, sure.

I think I followed -feliminate-unused-debug-types, but I see the large
majority of options controlling e.g. DWARF use -g instead.

Happy to change it to -gprune-btf.

> 
>> As the name implies, this option enables a kind of "pruning" of the BTF
>> information before it is emitted.  When enabled, rather than emitting
>> all type information translated from DWARF, only information for types
>> directly used in the source program is emitted.
>>
>> The primary purpose of this pruning is to reduce the amount of
>> unnecessary BTF information emitted, especially for BPF programs.  It is
>> very common for BPF programs to incldue Linux kernel internal headers in
>> order to have access to kernel data structures.  However, doing so often
>> has the side effect of also adding type definitions for a large number
>> of types which are not actually used by nor relevant to the program.
>> In these cases, -fprune-btf commonly reduces the size of the resulting
>> BTF information by 10x or more, as seen on average when compiling Linux
>> kernel BPF selftests.  This both slims down the size of the resulting
>> object and reduces the time required by the BPF loader to verify the
>> program and its BTF information.
>>
>> Note that the pruning implemented in this patch follows the same rules
>> as the BTF pruning performed unconditionally by LLVM's BPF backend when
>> generating BTF.  In particular, the main sources of pruning are:
>>
>>   1) Only generate BTF for types used by variables and functions at
>>      the file scope.  Note that with or without pruning, BTF_KIND_VAR
>>      entries are only generated for variables present in the final
>>      object - unused static variables or variables completely optimized
>>      away must not have VAR entries in BTF.
>>
>>   2) Avoid emitting full BTF for struct and union types which are only
>>      pointed-to by members of other struct/union types.  In these cases,
>>      the full BTF_KIND_STRUCT or BTF_KIND_UNION which would normally
>>      be emitted is replaced with a BTF_KIND_FWD, as though the
>>      underlying type was a forward-declared struct or union type.
>>
>> gcc/
>>         * btfout.cc (btf_used_types): New hash set.
>>         (struct btf_fixup): New.
>>         (fixups, forwards): New vecs.
>>         (btf_output): Calculate num_types depending on flag_prune_btf.
>>         (btf_early_finsih): New initialization for flag_prune_btf.
>>         (btf_add_used_type): New function.
>>         (btf_used_type_list_cb): Likewise.
>>         (btf_late_collect_pruned_types): Likewise.
>>         (btf_late_add_vars): Handle special case for variables in ".maps"
>>         section when generating BTF for BPF CO-RE target.
>>         (btf_late_finish): Use btf_late_collect_pruned_types when
>>         flag_prune_btf in effect.  Move some initialization to btf_early_finish.
>>         (btf_finalize): Additional deallocation for flag_prune_btf.
>>         * common.opt (fprune-btf): New flag.
>>         * ctfc.cc (init_ctf_strtable): Make non-static.
>>         * ctfc.h (struct ctf_dtdef): Add visited_children_p boolean flag.
>>         (init_ctf_strtable, ctfc_delete_strtab): Make extern.
>>         * doc/invoke.texi (Debugging Options): Document -fprune-btf.
>>
>> gcc/testsuite/
>>         * gcc.dg/debug/btf/btf-prune-1.c: New test.
>>         * gcc.dg/debug/btf/btf-prune-2.c: Likewise.
>>         * gcc.dg/debug/btf/btf-prune-3.c: Likewise.
>>         * gcc.dg/debug/btf/btf-prune-maps.c: Likewise.
>> ---
>>  gcc/btfout.cc                                 | 359 +++++++++++++++++-
>>  gcc/common.opt                                |   4 +
>>  gcc/ctfc.cc                                   |   2 +-
>>  gcc/ctfc.h                                    |   3 +
>>  gcc/doc/invoke.texi                           |  20 +
>>  gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c  |  25 ++
>>  gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c  |  33 ++
>>  gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c  |  35 ++
>>  .../gcc.dg/debug/btf/btf-prune-maps.c         |  20 +
>>  9 files changed, 494 insertions(+), 7 deletions(-)
>>  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c
>>  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c
>>  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c
>>  create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c
>>
>> diff --git a/gcc/btfout.cc b/gcc/btfout.cc
>> index 32fda14f704b..a7da164f6b31 100644
>> --- a/gcc/btfout.cc
>> +++ b/gcc/btfout.cc
>> @@ -828,7 +828,10 @@ output_btf_types (ctf_container_ref ctfc)
>>  {
>>    size_t i;
>>    size_t num_types;
>> -  num_types = ctfc->ctfc_types->elements ();
>> +  if (flag_prune_btf)
>> +    num_types = max_translated_id;
>> +  else
>> +    num_types = ctfc->ctfc_types->elements ();
>>
>>    if (num_types)
>>      {
>> @@ -957,6 +960,212 @@ btf_early_add_func_records (ctf_container_ref ctfc)
>>      }
>>  }
>>
>> +/* The set of types used directly in the source program, and any types manually
>> +   marked as used.  This is the set of types which will be emitted when
>> +   flag_prune_btf is set.  */
>> +static GTY (()) hash_set<ctf_dtdef_ref> *btf_used_types;
>> +
>> +/* Fixup used to avoid unnecessary pointer chasing for types.  A fixup is
>> +   created when a structure or union member is a pointer to another struct
>> +   or union type.  In such cases, avoid emitting full type information for
>> +   the pointee struct or union type (which may be quite large), unless that
>> +   type is used directly elsewhere.  */
>> +struct btf_fixup
>> +{
>> +  ctf_dtdef_ref pointer_dtd; /* Type node to which the fixup is applied.  */
>> +  ctf_dtdef_ref pointee_dtd; /* Original type node referred to by pointer_dtd.
>> +                               If this concrete type is not otherwise used,
>> +                               then a forward is created.  */
>> +};
>> +
>> +/* Stores fixups while processing types.  */
>> +static vec<struct btf_fixup> fixups;
>> +
>> +/* For fixups where the underlying type is not used in the end, a BTF_KIND_FWD
>> +   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.
>> +   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
>> +   or union member.  SEEN_PTR is true if CHECK_PTR is true AND one of the
>> +   predecessors was a pointer type.  These two flags are used to avoid chasing
>> +   pointers to struct/union only used from pointer members.  For such types, we
>> +   will emit a forward instead of the full type information, unless
>> +   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)
>> +{
>> +  if (dtd == NULL)
>> +    return NULL;
>> +
>> +  uint32_t ctf_kind = CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info);
>> +  uint32_t kind = get_btf_kind (ctf_kind);
>> +
>> +  /* Check whether the type has already been added.  */
>> +  if (btf_used_types->contains (dtd))
>> +    {
>> +      /* It's possible the type was already added as a fixup, but that we now
>> +        have a concrete use of it.  */
>> +      switch (kind)
>> +       {
>> +       case BTF_KIND_PTR:
>> +       case BTF_KIND_TYPEDEF:
>> +       case BTF_KIND_CONST:
>> +       case BTF_KIND_VOLATILE:
>> +       case BTF_KIND_RESTRICT:
>> +         if (check_ptr)
>> +           /* Type was previously added as a fixup, and that's OK.  */
>> +           return dtd;
>> +         else
>> +           {
>> +             /* The type was previously added as a fixup, but now we have
>> +                a concrete use of it.  Remove the fixup.  */
>> +             for (size_t i = 0; i < fixups.length (); i++)
>> +               if (fixups[i].pointer_dtd == 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);
>> +             return dtd;
>> +           }
>> +       default:
>> +         return dtd;
>> +       }
>> +    }
>> +
>> +  if (ctf_kind == CTF_K_SLICE)
>> +    {
>> +      /* Bitfield.  Add the underlying type to the used set, but leave
>> +        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);
>> +      return dtd;
>> +    }
>> +
>> +  /* Skip redundant definitions of void and types with no BTF encoding.  */
>> +  if ((kind == BTF_KIND_INT && dtd->dtd_data.ctti_size == 0)
>> +      || (kind == BTF_KIND_UNKN))
>> +    return NULL;
>> +
>> +  /* Add the type itself, and assign its id.
>> +     Do this before recursing to handle things like linked list structures.  */
>> +  gcc_assert (ctfc->ctfc_nextid <= BTF_MAX_TYPE);
>> +  dtd->dtd_type = ctfc->ctfc_nextid++;
>> +  btf_used_types->add (dtd);
>> +  ctf_add_string (ctfc, dtd->dtd_name, &(dtd->dtd_data.ctti_name), CTF_STRTAB);
>> +  ctfc->ctfc_num_types++;
>> +  ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (dtd);
>> +
>> +  /* Recursively add types referenced by this type.  */
>> +  switch (kind)
>> +    {
>> +    case BTF_KIND_INT:
>> +    case BTF_KIND_FLOAT:
>> +    case BTF_KIND_FWD:
>> +      /* Leaf kinds which do not refer to any other types.  */
>> +      break;
>> +
>> +    case BTF_KIND_FUNC:
>> +    case BTF_KIND_VAR:
>> +      /* Root kinds; no type we are visiting may refer to these.  */
>> +      gcc_unreachable ();
>> +
>> +    case BTF_KIND_PTR:
>> +    case BTF_KIND_TYPEDEF:
>> +    case BTF_KIND_CONST:
>> +    case BTF_KIND_VOLATILE:
>> +    case BTF_KIND_RESTRICT:
>> +      {
>> +       /* These type kinds refer to exactly one other type.  */
>> +       if (check_ptr && !seen_ptr)
>> +         seen_ptr = (kind == BTF_KIND_PTR);
>> +
>> +       /* Try to avoid chasing pointers to struct/union types if the
>> +          underlying type isn't used.  */
>> +       if (check_ptr && seen_ptr && create_fixups)
>> +         {
>> +           ctf_dtdef_ref ref = dtd->ref_type;
>> +           uint32_t ref_kind = btf_dtd_kind (ref);
>> +
>> +           if ((ref_kind == BTF_KIND_STRUCT || ref_kind == BTF_KIND_UNION)
>> +               && !btf_used_types->contains (ref))
>> +             {
>> +               struct btf_fixup fixup;
>> +               fixup.pointer_dtd = dtd;
>> +               fixup.pointee_dtd = ref;
>> +               fixups.safe_push (fixup);
>> +               break;
>> +             }
>> +         }
>> +
>> +       /* 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);
>> +       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);
>> +       break;
>> +      }
>> +    case BTF_KIND_STRUCT:
>> +    case BTF_KIND_UNION:
>> +    case BTF_KIND_ENUM:
>> +    case BTF_KIND_ENUM64:
>> +      {
>> +       /* Add members.  */
>> +       ctf_dmdef_t *dmd;
>> +       for (dmd = dtd->dtd_u.dtu_members;
>> +            dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
>> +         {
>> +           /* 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);
>> +           ctf_add_string (ctfc, dmd->dmd_name, &(dmd->dmd_name_offset),
>> +                           CTF_STRTAB);
>> +         }
>> +       break;
>> +      }
>> +    case BTF_KIND_FUNC_PROTO:
>> +      {
>> +       /* Add return type.  */
>> +       dtd->ref_type = btf_add_used_type (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);
>> +           /* 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.  */
>> +         }
>> +       break;
>> +      }
>> +    default:
>> +      return NULL;
>> +    }
>> +
>> +  return dtd;
>> +}
>> +
>>  /* 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
>> @@ -972,6 +1181,27 @@ btf_early_finish (void)
>>
>>    btf_early_add_const_void (tu_ctfc);
>>    btf_early_add_func_records (tu_ctfc);
>> +
>> +  /* Note: from here on, destructive changes are made to the TU CTFC to
>> +     translate CTF to BTF.  These fields are reset to count BTF types etc.  */
>> +  tu_ctfc->ctfc_num_types = 0;
>> +  tu_ctfc->ctfc_num_vlen_bytes = 0;
>> +  tu_ctfc->ctfc_vars_list_count = 0;
>> +
>> +  if (flag_prune_btf)
>> +    {
>> +      btf_used_types
>> +       = hash_set<ctf_dtdef_ref>::create_ggc (tu_ctfc->ctfc_types->elements ());
>> +      tu_ctfc->ctfc_nextid = 1;
>> +      fixups.create (1);
>> +
>> +      /* Empty the string table, which was already populated with strings for
>> +        all types translated from DWARF.  We may only need a very small subset
>> +        of these strings; those will be re-added below.  */
>> +      ctfc_delete_strtab (&tu_ctfc->ctfc_strtable);
>> +      init_ctf_strtable (&tu_ctfc->ctfc_strtable);
>> +      tu_ctfc->ctfc_strlen++;
>> +    }
>>  }
>>
>>  /* Push a BTF datasec entry ENTRY into the datasec named SECNAME,
>> @@ -1154,6 +1384,25 @@ btf_late_add_vars (ctf_container_ref ctfc)
>>
>>        /* Add a BTF_KIND_DATASEC entry for the variable.  */
>>        btf_datasec_add_var (ctfc, var, dvd);
>> +
>> +      const char *section = var->get_section ();
>> +      if (section && (strcmp (section, ".maps") == 0) && flag_prune_btf)
>> +       {
>> +         /* The .maps section has special meaning in BTF: it is used for BPF
>> +            map definitions.  These definitions should be structs.  We must
>> +            collect pointee types used in map members as though they are used
>> +            directly, effectively ignoring (from the pruning perspective) that
>> +            they are struct members.  */
>> +         ctf_dtdef_ref dtd = dvd->dvd_type;
>> +         uint32_t kind = btf_dtd_kind (dvd->dvd_type);
>> +         if (kind == BTF_KIND_STRUCT)
>> +           {
>> +             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);
>> +           }
>> +       }
>>      }
>>  }
>>
>> @@ -1252,6 +1501,86 @@ btf_late_assign_datasec_ids (ctf_container_ref ctfc)
>>      }
>>  }
>>
>> +/* Callback used for assembling the only-used-types list.  Note that this is
>> +   the same as btf_type_list_cb above, but the hash_set traverse requires a
>> +   different function signature.  */
>> +
>> +static bool
>> +btf_used_type_list_cb (const ctf_dtdef_ref& dtd, ctf_container_ref ctfc)
>> +{
>> +  ctfc->ctfc_types_list[dtd->dtd_type] = dtd;
>> +  return true;
>> +}
>> +
>> +/* Collect the set of types reachable from global variables and functions.
>> +   This is the minimal set of types, used when generating pruned BTF.  */
>> +
>> +static void
>> +btf_late_collect_pruned_types (ctf_container_ref ctfc)
>> +{
>> +  vec_alloc (forwards, 1);
>> +
>> +  /* Add types used from functions.  */
>> +  ctf_dtdef_ref dtd;
>> +  size_t i;
>> +  FOR_EACH_VEC_ELT (*funcs, i, dtd)
>> +    {
>> +      btf_add_used_type (ctfc, dtd->ref_type, false, false, true);
>> +      ctf_add_string (ctfc, dtd->dtd_name, &(dtd->dtd_data.ctti_name),
>> +                     CTF_STRTAB);
>> +    }
>> +
>> +  /* Add types used from global variables.  */
>> +  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);
>> +      ctf_add_string (ctfc, dvd->dvd_name, &(dvd->dvd_name_offset), CTF_STRTAB);
>> +    }
>> +
>> +  /* 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.  */
>> +  for (i = 0; i < fixups.length (); i++)
>> +    {
>> +      struct btf_fixup *fx = &fixups[i];
>> +      if (!btf_used_types->contains (fx->pointee_dtd))
>> +       {
>> +         /* The underlying type is not used.  Create a forward.  */
>> +         ctf_dtdef_ref fwd = ggc_cleared_alloc<ctf_dtdef_t> ();
>> +         ctf_id_t id = ctfc->ctfc_nextid++;
>> +         gcc_assert (id <= BTF_MAX_TYPE);
>> +
>> +         bool union_p = (btf_dtd_kind (fx->pointee_dtd) == BTF_KIND_UNION);
>> +
>> +         fwd->dtd_name = fx->pointee_dtd->dtd_name;
>> +         fwd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_FORWARD, union_p, 0);
>> +         fwd->dtd_type = id;
>> +         ctfc->ctfc_num_types++;
>> +         ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (fwd);
>> +         ctf_add_string (ctfc, fwd->dtd_name, &(fwd->dtd_data.ctti_name),
>> +                         CTF_STRTAB);
>> +
>> +         /* Update the pointer to point to the forward.  */
>> +         fx->pointer_dtd->ref_type = fwd;
>> +         vec_safe_push (forwards, fwd);
>> +       }
>> +    }
>> +
>> +  /* Construct the resulting pruned type list.  */
>> +  ctfc->ctfc_types_list
>> +    = ggc_vec_alloc<ctf_dtdef_ref> (btf_used_types->elements () + 1
>> +                                   + vec_safe_length (forwards));
>> +
>> +  btf_used_types->traverse<ctf_container_ref, btf_used_type_list_cb> (ctfc);
>> +
>> +  /* Insert the newly created forwards into the regular types list too.  */
>> +  FOR_EACH_VEC_ELT (*forwards, i, dtd)
>> +    ctfc->ctfc_types_list[dtd->dtd_type] = dtd;
>> +
>> +  max_translated_id = btf_used_types->elements () + vec_safe_length (forwards);
>> +}
>> +
>>  /* Late entry point for BTF generation, called from dwarf2out_finish ().
>>     Complete and emit BTF information.  */
>>
>> @@ -1263,13 +1592,22 @@ btf_finish (void)
>>
>>    datasecs.create (0);
>>
>> -  tu_ctfc->ctfc_num_types = 0;
>> -  tu_ctfc->ctfc_num_vlen_bytes = 0;
>> -  tu_ctfc->ctfc_vars_list_count = 0;
>> -
>>    btf_late_add_vars (tu_ctfc);
>> -  btf_late_collect_translated_types (tu_ctfc);
>> +  if (flag_prune_btf)
>> +    {
>> +      /* Collect pruned set of BTF types and prepare for emission.
>> +        This includes only types directly used in file-scope variables and
>> +        function return/argument types.  */
>> +      btf_late_collect_pruned_types (tu_ctfc);
>> +    }
>> +  else
>> +    {
>> +      /* Collect all BTF types and prepare for emission.
>> +        This includes all types translated from DWARF.  */
>> +      btf_late_collect_translated_types (tu_ctfc);
>> +    }
>>    btf_late_add_func_datasec_entries (tu_ctfc);
>> +
>>    btf_late_assign_var_ids (tu_ctfc);
>>    btf_late_assign_func_ids (tu_ctfc);
>>    btf_late_assign_datasec_ids (tu_ctfc);
>> @@ -1302,6 +1640,15 @@ btf_finalize (void)
>>    func_map->empty ();
>>    func_map = NULL;
>>
>> +  if (flag_prune_btf)
>> +    {
>> +      btf_used_types->empty ();
>> +      btf_used_types = NULL;
>> +
>> +      fixups.release ();
>> +      forwards = NULL;
>> +    }
>> +
>>    ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
>>    ctfc_delete_container (tu_ctfc);
>>    tu_ctfc = NULL;
>> diff --git a/gcc/common.opt b/gcc/common.opt
>> index 2c078fdd1f86..3d4a55d02c26 100644
>> --- a/gcc/common.opt
>> +++ b/gcc/common.opt
>> @@ -2588,6 +2588,10 @@ fpatchable-function-entry=
>>  Common Var(flag_patchable_function_entry) Joined Optimization
>>  Insert NOP instructions at each function entry.
>>
>> +fprune-btf
>> +Common Var(flag_prune_btf) Init(0)
>> +Generate minimal BTF debug info.
>> +
>>  frandom-seed
>>  Common Var(common_deferred_options) Defer
>>
>> diff --git a/gcc/ctfc.cc b/gcc/ctfc.cc
>> index 678ca2a072cf..27fc3a6d5851 100644
>> --- a/gcc/ctfc.cc
>> +++ b/gcc/ctfc.cc
>> @@ -913,7 +913,7 @@ ctfc_get_dvd_srcloc (ctf_dvdef_ref dvd, ctf_srcloc_ref loc)
>>  /* Initialize the CTF string table.
>>     The first entry in the CTF string table (empty string) is added.  */
>>
>> -static void
>> +void
>>  init_ctf_strtable (ctf_strtable_t * strtab)
>>  {
>>    strtab->ctstab_head = NULL;
>> diff --git a/gcc/ctfc.h b/gcc/ctfc.h
>> index d0b724817a7f..29267dc036d1 100644
>> --- a/gcc/ctfc.h
>> +++ b/gcc/ctfc.h
>> @@ -369,6 +369,9 @@ extern unsigned int ctfc_get_num_ctf_vars (ctf_container_ref);
>>
>>  extern ctf_strtable_t * ctfc_get_strtab (ctf_container_ref, int);
>>
>> +extern void init_ctf_strtable (ctf_strtable_t *);
>> +extern void ctfc_delete_strtab (ctf_strtable_t *);
>> +
>>  /* Get the length of the specified string table in the CTF container.  */
>>
>>  extern size_t ctfc_get_strtab_len (ctf_container_ref, int);
>> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
>> index 517a782987d9..6dac00d14381 100644
>> --- a/gcc/doc/invoke.texi
>> +++ b/gcc/doc/invoke.texi
>> @@ -534,6 +534,7 @@ Objective-C and Objective-C++ Dialects}.
>>  -gvms -gz@r{[}=@var{type}@r{]}
>>  -gsplit-dwarf  -gdescribe-dies  -gno-describe-dies
>>  -fdebug-prefix-map=@var{old}=@var{new}  -fdebug-types-section
>> +-fprune-btf -fno-prune-btf
>>  -fno-eliminate-unused-debug-types
>>  -femit-struct-debug-baseonly  -femit-struct-debug-reduced
>>  -femit-struct-debug-detailed@r{[}=@var{spec-list}@r{]}
>> @@ -12292,6 +12293,25 @@ compressed debug sections, the option is rejected.  Otherwise, if the
>>  assembler does not support them, @option{-gz} is silently ignored when
>>  producing object files.
>>
>> +@opindex fprune-btf
>> +@opindex fno-prune-btf
>> +@item -fprune-btf
>> +@itemx -fno-prune-btf
>> +Prune BTF information before emission.  When pruning, only type
>> +information for types used by global variables and file-scope functions
>> +will be emitted.  If compiling for the BPF target with BPF CO-RE
>> +enabled, type information will also be emitted for types used in BPF
>> +CO-RE relocations.  In addition, struct and union types which are only
>> +referred to via pointers from members of other struct or union types
>> +shall be pruned and replaced with BTF_KIND_FWD, as though those types
>> +were only present in the input as forward declarations.
>> +
>> +This option substantially reduces the size of produced BTF information,
>> +but at significant loss in the amount of detailed type information.
>> +It is primarily useful when compiling for the BPF target, to minimize
>> +the size of the resulting object, and to eliminate BTF information
>> +which is not immediately relevant to the BPF program loading process.
>> +
>>  @opindex femit-struct-debug-baseonly
>>  @item -femit-struct-debug-baseonly
>>  Emit debug information for struct-like types
>> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c
>> new file mode 100644
>> index 000000000000..3c9b59a07ecf
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c
>> @@ -0,0 +1,25 @@
>> +/* Simple test of -fprune-btf option operation.
>> +   Since 'struct foo' is not used, no BTF shall be emitted for it.  */
>> +
>> +/* { dg-do compile } */
>> +/* { dg-options "-gbtf -fprune-btf -dA" } */
>> +
>> +/* No BTF info for 'struct foo' nor types used only by it.  */
>> +/* { dg-final { scan-assembler-not "BTF_KIND_STRUCT 'foo'" } } */
>> +/* { dg-final { scan-assembler-not "BTF_KIND_INT 'char'" } } */
>> +
>> +/* We should get BTF info for 'struct bar' since it is used.  */
>> +/* { dg-final { scan-assembler "BTF_KIND_STRUCT 'bar'"} } */
>> +
>> +struct foo {
>> +  int a;
>> +  char c;
>> +};
>> +
>> +struct bar {
>> +  int x;
>> +  long z[4];
>> +};
>> +
>> +struct bar a_bar;
>> +
>> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c
>> new file mode 100644
>> index 000000000000..20183dffcc7f
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c
>> @@ -0,0 +1,33 @@
>> +/* Test that -fprune-btf does not chase pointer-to-struct members.  */
>> +
>> +/* { dg-do compile } */
>> +/* { dg-options "-gbtf -fprune-btf -dA" } */
>> +
>> +/* Only use of B is via a pointer member of C.
>> +   Full BTF for B is replaced with a forward.  */
>> +/* { dg-final { scan-assembler-not "BTF_KIND_STRUCT 'B'" } } */
>> +/* { dg-final { scan-assembler-times "TYPE \[0-9\]+ BTF_KIND_FWD 'B'" 1 } } */
>> +
>> +/* Detailed info for B is omitted, and A is otherwise unused.  */
>> +/* { dg-final { scan-assembler-not "BTF_KIND_\[A-Z\]+ 'A'" } } */
>> +
>> +/* { dg-final { scan-assembler "BTF_KIND_STRUCT 'C'" } } */
>> +
>> +struct A;
>> +
>> +struct B {
>> +  int x;
>> +  int (*do_A_thing) (int, int);
>> +  struct A *other;
>> +};
>> +
>> +struct C {
>> +  unsigned int x;
>> +  struct B * a;
>> +};
>> +
>> +int
>> +foo (struct C *c)
>> +{
>> +  return c->x;
>> +}
>> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c
>> new file mode 100644
>> index 000000000000..57a079cf0b4d
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c
>> @@ -0,0 +1,35 @@
>> +/* Test that -fprune-btf */
>> +
>> +/* { dg-do compile } */
>> +/* { dg-options "-gbtf -fprune-btf -dA" } */
>> +
>> +/* We expect full BTF information each struct.  */
>> +/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_FWD 'file'" } } */
>> +/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_STRUCT 'A'" } } */
>> +/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_STRUCT 'B'" } } */
>> +/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_STRUCT 'C'" } } */
>> +
>> +struct file;
>> +
>> +struct A {
>> +  void *private;
>> +  long (*read)(struct file *, char *, unsigned long);
>> +  long (*write)(struct file *, const char *, unsigned long);
>> +};
>> +
>> +struct B {
>> +  unsigned int x;
>> +  struct A **as;
>> +};
>> +
>> +struct C {
>> +  struct A *arr_a[4];
>> +  struct A *lone_a;
>> +  unsigned int z;
>> +};
>> +
>> +unsigned int
>> +foo (struct B *b, struct C *c)
>> +{
>> +  return b->x + c->z;
>> +}
>> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c
>> new file mode 100644
>> index 000000000000..bf3a25e984ff
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c
>> @@ -0,0 +1,20 @@
>> +/* Test special meaning of .maps section for BTF when pruning.  For global
>> +   variables of struct type placed in this section, we must treat members as
>> +   though they are used directly, always collecting pointee types.
>> +   Therefore, full type information for struct keep_me should be emitted.  */
>> +
>> +/* { dg-do compile } */
>> +/* { dg-options "-gbtf -fprune-btf -dA" } */
>> +
>> +/* { dg-final { scan-assembler-not "BTF_KIND_FWD 'keep_me'" } } */
>> +/* { dg-final { scan-assembler "BTF_KIND_STRUCT 'keep_me'" } } */
>> +
>> +struct keep_me {
>> +  int a;
>> +  char c;
>> +};
>> +
>> +struct {
>> +  int *key;
>> +  struct keep_me *value;
>> +} my_map __attribute__((section (".maps")));
>> --
>> 2.43.0
>>

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

* Re: [PATCH v3 4/6] btf: add -fprune-btf option
  2024-05-31 15:57     ` David Faust
@ 2024-05-31 16:24       ` Richard Biener
  0 siblings, 0 replies; 16+ messages in thread
From: Richard Biener @ 2024-05-31 16:24 UTC (permalink / raw)
  To: David Faust; +Cc: gcc-patches, indu.bhagat, jose.marchesi, cupertino.miranda



> Am 31.05.2024 um 17:58 schrieb David Faust <david.faust@oracle.com>:
> 
> 
> 
>> On 5/31/24 00:07, Richard Biener wrote:
>>> On Thu, May 30, 2024 at 11:34 PM David Faust <david.faust@oracle.com> wrote:
>>> 
>>> This patch adds a new option, -fprune-btf, to control BTF debug info
>>> generation.
>> 
>> Can you name it -gprune-btf instead?
> 
> Yes, sure.
> 
> I think I followed -feliminate-unused-debug-types,

Unfortunately GCC is full of historical mistakes…

> but I see the large
> majority of options controlling e.g. DWARF use -g instead.
> 
> Happy to change it to -gprune-btf.

Please.

Richard 

>> 
>>> As the name implies, this option enables a kind of "pruning" of the BTF
>>> information before it is emitted.  When enabled, rather than emitting
>>> all type information translated from DWARF, only information for types
>>> directly used in the source program is emitted.
>>> 
>>> The primary purpose of this pruning is to reduce the amount of
>>> unnecessary BTF information emitted, especially for BPF programs.  It is
>>> very common for BPF programs to incldue Linux kernel internal headers in
>>> order to have access to kernel data structures.  However, doing so often
>>> has the side effect of also adding type definitions for a large number
>>> of types which are not actually used by nor relevant to the program.
>>> In these cases, -fprune-btf commonly reduces the size of the resulting
>>> BTF information by 10x or more, as seen on average when compiling Linux
>>> kernel BPF selftests.  This both slims down the size of the resulting
>>> object and reduces the time required by the BPF loader to verify the
>>> program and its BTF information.
>>> 
>>> Note that the pruning implemented in this patch follows the same rules
>>> as the BTF pruning performed unconditionally by LLVM's BPF backend when
>>> generating BTF.  In particular, the main sources of pruning are:
>>> 
>>>  1) Only generate BTF for types used by variables and functions at
>>>     the file scope.  Note that with or without pruning, BTF_KIND_VAR
>>>     entries are only generated for variables present in the final
>>>     object - unused static variables or variables completely optimized
>>>     away must not have VAR entries in BTF.
>>> 
>>>  2) Avoid emitting full BTF for struct and union types which are only
>>>     pointed-to by members of other struct/union types.  In these cases,
>>>     the full BTF_KIND_STRUCT or BTF_KIND_UNION which would normally
>>>     be emitted is replaced with a BTF_KIND_FWD, as though the
>>>     underlying type was a forward-declared struct or union type.
>>> 
>>> gcc/
>>>        * btfout.cc (btf_used_types): New hash set.
>>>        (struct btf_fixup): New.
>>>        (fixups, forwards): New vecs.
>>>        (btf_output): Calculate num_types depending on flag_prune_btf.
>>>        (btf_early_finsih): New initialization for flag_prune_btf.
>>>        (btf_add_used_type): New function.
>>>        (btf_used_type_list_cb): Likewise.
>>>        (btf_late_collect_pruned_types): Likewise.
>>>        (btf_late_add_vars): Handle special case for variables in ".maps"
>>>        section when generating BTF for BPF CO-RE target.
>>>        (btf_late_finish): Use btf_late_collect_pruned_types when
>>>        flag_prune_btf in effect.  Move some initialization to btf_early_finish.
>>>        (btf_finalize): Additional deallocation for flag_prune_btf.
>>>        * common.opt (fprune-btf): New flag.
>>>        * ctfc.cc (init_ctf_strtable): Make non-static.
>>>        * ctfc.h (struct ctf_dtdef): Add visited_children_p boolean flag.
>>>        (init_ctf_strtable, ctfc_delete_strtab): Make extern.
>>>        * doc/invoke.texi (Debugging Options): Document -fprune-btf.
>>> 
>>> gcc/testsuite/
>>>        * gcc.dg/debug/btf/btf-prune-1.c: New test.
>>>        * gcc.dg/debug/btf/btf-prune-2.c: Likewise.
>>>        * gcc.dg/debug/btf/btf-prune-3.c: Likewise.
>>>        * gcc.dg/debug/btf/btf-prune-maps.c: Likewise.
>>> ---
>>> gcc/btfout.cc                                 | 359 +++++++++++++++++-
>>> gcc/common.opt                                |   4 +
>>> gcc/ctfc.cc                                   |   2 +-
>>> gcc/ctfc.h                                    |   3 +
>>> gcc/doc/invoke.texi                           |  20 +
>>> gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c  |  25 ++
>>> gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c  |  33 ++
>>> gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c  |  35 ++
>>> .../gcc.dg/debug/btf/btf-prune-maps.c         |  20 +
>>> 9 files changed, 494 insertions(+), 7 deletions(-)
>>> create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c
>>> create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c
>>> create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c
>>> create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c
>>> 
>>> diff --git a/gcc/btfout.cc b/gcc/btfout.cc
>>> index 32fda14f704b..a7da164f6b31 100644
>>> --- a/gcc/btfout.cc
>>> +++ b/gcc/btfout.cc
>>> @@ -828,7 +828,10 @@ output_btf_types (ctf_container_ref ctfc)
>>> {
>>>   size_t i;
>>>   size_t num_types;
>>> -  num_types = ctfc->ctfc_types->elements ();
>>> +  if (flag_prune_btf)
>>> +    num_types = max_translated_id;
>>> +  else
>>> +    num_types = ctfc->ctfc_types->elements ();
>>> 
>>>   if (num_types)
>>>     {
>>> @@ -957,6 +960,212 @@ btf_early_add_func_records (ctf_container_ref ctfc)
>>>     }
>>> }
>>> 
>>> +/* The set of types used directly in the source program, and any types manually
>>> +   marked as used.  This is the set of types which will be emitted when
>>> +   flag_prune_btf is set.  */
>>> +static GTY (()) hash_set<ctf_dtdef_ref> *btf_used_types;
>>> +
>>> +/* Fixup used to avoid unnecessary pointer chasing for types.  A fixup is
>>> +   created when a structure or union member is a pointer to another struct
>>> +   or union type.  In such cases, avoid emitting full type information for
>>> +   the pointee struct or union type (which may be quite large), unless that
>>> +   type is used directly elsewhere.  */
>>> +struct btf_fixup
>>> +{
>>> +  ctf_dtdef_ref pointer_dtd; /* Type node to which the fixup is applied.  */
>>> +  ctf_dtdef_ref pointee_dtd; /* Original type node referred to by pointer_dtd.
>>> +                               If this concrete type is not otherwise used,
>>> +                               then a forward is created.  */
>>> +};
>>> +
>>> +/* Stores fixups while processing types.  */
>>> +static vec<struct btf_fixup> fixups;
>>> +
>>> +/* For fixups where the underlying type is not used in the end, a BTF_KIND_FWD
>>> +   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.
>>> +   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
>>> +   or union member.  SEEN_PTR is true if CHECK_PTR is true AND one of the
>>> +   predecessors was a pointer type.  These two flags are used to avoid chasing
>>> +   pointers to struct/union only used from pointer members.  For such types, we
>>> +   will emit a forward instead of the full type information, unless
>>> +   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)
>>> +{
>>> +  if (dtd == NULL)
>>> +    return NULL;
>>> +
>>> +  uint32_t ctf_kind = CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info);
>>> +  uint32_t kind = get_btf_kind (ctf_kind);
>>> +
>>> +  /* Check whether the type has already been added.  */
>>> +  if (btf_used_types->contains (dtd))
>>> +    {
>>> +      /* It's possible the type was already added as a fixup, but that we now
>>> +        have a concrete use of it.  */
>>> +      switch (kind)
>>> +       {
>>> +       case BTF_KIND_PTR:
>>> +       case BTF_KIND_TYPEDEF:
>>> +       case BTF_KIND_CONST:
>>> +       case BTF_KIND_VOLATILE:
>>> +       case BTF_KIND_RESTRICT:
>>> +         if (check_ptr)
>>> +           /* Type was previously added as a fixup, and that's OK.  */
>>> +           return dtd;
>>> +         else
>>> +           {
>>> +             /* The type was previously added as a fixup, but now we have
>>> +                a concrete use of it.  Remove the fixup.  */
>>> +             for (size_t i = 0; i < fixups.length (); i++)
>>> +               if (fixups[i].pointer_dtd == 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);
>>> +             return dtd;
>>> +           }
>>> +       default:
>>> +         return dtd;
>>> +       }
>>> +    }
>>> +
>>> +  if (ctf_kind == CTF_K_SLICE)
>>> +    {
>>> +      /* Bitfield.  Add the underlying type to the used set, but leave
>>> +        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);
>>> +      return dtd;
>>> +    }
>>> +
>>> +  /* Skip redundant definitions of void and types with no BTF encoding.  */
>>> +  if ((kind == BTF_KIND_INT && dtd->dtd_data.ctti_size == 0)
>>> +      || (kind == BTF_KIND_UNKN))
>>> +    return NULL;
>>> +
>>> +  /* Add the type itself, and assign its id.
>>> +     Do this before recursing to handle things like linked list structures.  */
>>> +  gcc_assert (ctfc->ctfc_nextid <= BTF_MAX_TYPE);
>>> +  dtd->dtd_type = ctfc->ctfc_nextid++;
>>> +  btf_used_types->add (dtd);
>>> +  ctf_add_string (ctfc, dtd->dtd_name, &(dtd->dtd_data.ctti_name), CTF_STRTAB);
>>> +  ctfc->ctfc_num_types++;
>>> +  ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (dtd);
>>> +
>>> +  /* Recursively add types referenced by this type.  */
>>> +  switch (kind)
>>> +    {
>>> +    case BTF_KIND_INT:
>>> +    case BTF_KIND_FLOAT:
>>> +    case BTF_KIND_FWD:
>>> +      /* Leaf kinds which do not refer to any other types.  */
>>> +      break;
>>> +
>>> +    case BTF_KIND_FUNC:
>>> +    case BTF_KIND_VAR:
>>> +      /* Root kinds; no type we are visiting may refer to these.  */
>>> +      gcc_unreachable ();
>>> +
>>> +    case BTF_KIND_PTR:
>>> +    case BTF_KIND_TYPEDEF:
>>> +    case BTF_KIND_CONST:
>>> +    case BTF_KIND_VOLATILE:
>>> +    case BTF_KIND_RESTRICT:
>>> +      {
>>> +       /* These type kinds refer to exactly one other type.  */
>>> +       if (check_ptr && !seen_ptr)
>>> +         seen_ptr = (kind == BTF_KIND_PTR);
>>> +
>>> +       /* Try to avoid chasing pointers to struct/union types if the
>>> +          underlying type isn't used.  */
>>> +       if (check_ptr && seen_ptr && create_fixups)
>>> +         {
>>> +           ctf_dtdef_ref ref = dtd->ref_type;
>>> +           uint32_t ref_kind = btf_dtd_kind (ref);
>>> +
>>> +           if ((ref_kind == BTF_KIND_STRUCT || ref_kind == BTF_KIND_UNION)
>>> +               && !btf_used_types->contains (ref))
>>> +             {
>>> +               struct btf_fixup fixup;
>>> +               fixup.pointer_dtd = dtd;
>>> +               fixup.pointee_dtd = ref;
>>> +               fixups.safe_push (fixup);
>>> +               break;
>>> +             }
>>> +         }
>>> +
>>> +       /* 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);
>>> +       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);
>>> +       break;
>>> +      }
>>> +    case BTF_KIND_STRUCT:
>>> +    case BTF_KIND_UNION:
>>> +    case BTF_KIND_ENUM:
>>> +    case BTF_KIND_ENUM64:
>>> +      {
>>> +       /* Add members.  */
>>> +       ctf_dmdef_t *dmd;
>>> +       for (dmd = dtd->dtd_u.dtu_members;
>>> +            dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
>>> +         {
>>> +           /* 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);
>>> +           ctf_add_string (ctfc, dmd->dmd_name, &(dmd->dmd_name_offset),
>>> +                           CTF_STRTAB);
>>> +         }
>>> +       break;
>>> +      }
>>> +    case BTF_KIND_FUNC_PROTO:
>>> +      {
>>> +       /* Add return type.  */
>>> +       dtd->ref_type = btf_add_used_type (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);
>>> +           /* 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.  */
>>> +         }
>>> +       break;
>>> +      }
>>> +    default:
>>> +      return NULL;
>>> +    }
>>> +
>>> +  return dtd;
>>> +}
>>> +
>>> /* 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
>>> @@ -972,6 +1181,27 @@ btf_early_finish (void)
>>> 
>>>   btf_early_add_const_void (tu_ctfc);
>>>   btf_early_add_func_records (tu_ctfc);
>>> +
>>> +  /* Note: from here on, destructive changes are made to the TU CTFC to
>>> +     translate CTF to BTF.  These fields are reset to count BTF types etc.  */
>>> +  tu_ctfc->ctfc_num_types = 0;
>>> +  tu_ctfc->ctfc_num_vlen_bytes = 0;
>>> +  tu_ctfc->ctfc_vars_list_count = 0;
>>> +
>>> +  if (flag_prune_btf)
>>> +    {
>>> +      btf_used_types
>>> +       = hash_set<ctf_dtdef_ref>::create_ggc (tu_ctfc->ctfc_types->elements ());
>>> +      tu_ctfc->ctfc_nextid = 1;
>>> +      fixups.create (1);
>>> +
>>> +      /* Empty the string table, which was already populated with strings for
>>> +        all types translated from DWARF.  We may only need a very small subset
>>> +        of these strings; those will be re-added below.  */
>>> +      ctfc_delete_strtab (&tu_ctfc->ctfc_strtable);
>>> +      init_ctf_strtable (&tu_ctfc->ctfc_strtable);
>>> +      tu_ctfc->ctfc_strlen++;
>>> +    }
>>> }
>>> 
>>> /* Push a BTF datasec entry ENTRY into the datasec named SECNAME,
>>> @@ -1154,6 +1384,25 @@ btf_late_add_vars (ctf_container_ref ctfc)
>>> 
>>>       /* Add a BTF_KIND_DATASEC entry for the variable.  */
>>>       btf_datasec_add_var (ctfc, var, dvd);
>>> +
>>> +      const char *section = var->get_section ();
>>> +      if (section && (strcmp (section, ".maps") == 0) && flag_prune_btf)
>>> +       {
>>> +         /* The .maps section has special meaning in BTF: it is used for BPF
>>> +            map definitions.  These definitions should be structs.  We must
>>> +            collect pointee types used in map members as though they are used
>>> +            directly, effectively ignoring (from the pruning perspective) that
>>> +            they are struct members.  */
>>> +         ctf_dtdef_ref dtd = dvd->dvd_type;
>>> +         uint32_t kind = btf_dtd_kind (dvd->dvd_type);
>>> +         if (kind == BTF_KIND_STRUCT)
>>> +           {
>>> +             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);
>>> +           }
>>> +       }
>>>     }
>>> }
>>> 
>>> @@ -1252,6 +1501,86 @@ btf_late_assign_datasec_ids (ctf_container_ref ctfc)
>>>     }
>>> }
>>> 
>>> +/* Callback used for assembling the only-used-types list.  Note that this is
>>> +   the same as btf_type_list_cb above, but the hash_set traverse requires a
>>> +   different function signature.  */
>>> +
>>> +static bool
>>> +btf_used_type_list_cb (const ctf_dtdef_ref& dtd, ctf_container_ref ctfc)
>>> +{
>>> +  ctfc->ctfc_types_list[dtd->dtd_type] = dtd;
>>> +  return true;
>>> +}
>>> +
>>> +/* Collect the set of types reachable from global variables and functions.
>>> +   This is the minimal set of types, used when generating pruned BTF.  */
>>> +
>>> +static void
>>> +btf_late_collect_pruned_types (ctf_container_ref ctfc)
>>> +{
>>> +  vec_alloc (forwards, 1);
>>> +
>>> +  /* Add types used from functions.  */
>>> +  ctf_dtdef_ref dtd;
>>> +  size_t i;
>>> +  FOR_EACH_VEC_ELT (*funcs, i, dtd)
>>> +    {
>>> +      btf_add_used_type (ctfc, dtd->ref_type, false, false, true);
>>> +      ctf_add_string (ctfc, dtd->dtd_name, &(dtd->dtd_data.ctti_name),
>>> +                     CTF_STRTAB);
>>> +    }
>>> +
>>> +  /* Add types used from global variables.  */
>>> +  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);
>>> +      ctf_add_string (ctfc, dvd->dvd_name, &(dvd->dvd_name_offset), CTF_STRTAB);
>>> +    }
>>> +
>>> +  /* 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.  */
>>> +  for (i = 0; i < fixups.length (); i++)
>>> +    {
>>> +      struct btf_fixup *fx = &fixups[i];
>>> +      if (!btf_used_types->contains (fx->pointee_dtd))
>>> +       {
>>> +         /* The underlying type is not used.  Create a forward.  */
>>> +         ctf_dtdef_ref fwd = ggc_cleared_alloc<ctf_dtdef_t> ();
>>> +         ctf_id_t id = ctfc->ctfc_nextid++;
>>> +         gcc_assert (id <= BTF_MAX_TYPE);
>>> +
>>> +         bool union_p = (btf_dtd_kind (fx->pointee_dtd) == BTF_KIND_UNION);
>>> +
>>> +         fwd->dtd_name = fx->pointee_dtd->dtd_name;
>>> +         fwd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_FORWARD, union_p, 0);
>>> +         fwd->dtd_type = id;
>>> +         ctfc->ctfc_num_types++;
>>> +         ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (fwd);
>>> +         ctf_add_string (ctfc, fwd->dtd_name, &(fwd->dtd_data.ctti_name),
>>> +                         CTF_STRTAB);
>>> +
>>> +         /* Update the pointer to point to the forward.  */
>>> +         fx->pointer_dtd->ref_type = fwd;
>>> +         vec_safe_push (forwards, fwd);
>>> +       }
>>> +    }
>>> +
>>> +  /* Construct the resulting pruned type list.  */
>>> +  ctfc->ctfc_types_list
>>> +    = ggc_vec_alloc<ctf_dtdef_ref> (btf_used_types->elements () + 1
>>> +                                   + vec_safe_length (forwards));
>>> +
>>> +  btf_used_types->traverse<ctf_container_ref, btf_used_type_list_cb> (ctfc);
>>> +
>>> +  /* Insert the newly created forwards into the regular types list too.  */
>>> +  FOR_EACH_VEC_ELT (*forwards, i, dtd)
>>> +    ctfc->ctfc_types_list[dtd->dtd_type] = dtd;
>>> +
>>> +  max_translated_id = btf_used_types->elements () + vec_safe_length (forwards);
>>> +}
>>> +
>>> /* Late entry point for BTF generation, called from dwarf2out_finish ().
>>>    Complete and emit BTF information.  */
>>> 
>>> @@ -1263,13 +1592,22 @@ btf_finish (void)
>>> 
>>>   datasecs.create (0);
>>> 
>>> -  tu_ctfc->ctfc_num_types = 0;
>>> -  tu_ctfc->ctfc_num_vlen_bytes = 0;
>>> -  tu_ctfc->ctfc_vars_list_count = 0;
>>> -
>>>   btf_late_add_vars (tu_ctfc);
>>> -  btf_late_collect_translated_types (tu_ctfc);
>>> +  if (flag_prune_btf)
>>> +    {
>>> +      /* Collect pruned set of BTF types and prepare for emission.
>>> +        This includes only types directly used in file-scope variables and
>>> +        function return/argument types.  */
>>> +      btf_late_collect_pruned_types (tu_ctfc);
>>> +    }
>>> +  else
>>> +    {
>>> +      /* Collect all BTF types and prepare for emission.
>>> +        This includes all types translated from DWARF.  */
>>> +      btf_late_collect_translated_types (tu_ctfc);
>>> +    }
>>>   btf_late_add_func_datasec_entries (tu_ctfc);
>>> +
>>>   btf_late_assign_var_ids (tu_ctfc);
>>>   btf_late_assign_func_ids (tu_ctfc);
>>>   btf_late_assign_datasec_ids (tu_ctfc);
>>> @@ -1302,6 +1640,15 @@ btf_finalize (void)
>>>   func_map->empty ();
>>>   func_map = NULL;
>>> 
>>> +  if (flag_prune_btf)
>>> +    {
>>> +      btf_used_types->empty ();
>>> +      btf_used_types = NULL;
>>> +
>>> +      fixups.release ();
>>> +      forwards = NULL;
>>> +    }
>>> +
>>>   ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
>>>   ctfc_delete_container (tu_ctfc);
>>>   tu_ctfc = NULL;
>>> diff --git a/gcc/common.opt b/gcc/common.opt
>>> index 2c078fdd1f86..3d4a55d02c26 100644
>>> --- a/gcc/common.opt
>>> +++ b/gcc/common.opt
>>> @@ -2588,6 +2588,10 @@ fpatchable-function-entry=
>>> Common Var(flag_patchable_function_entry) Joined Optimization
>>> Insert NOP instructions at each function entry.
>>> 
>>> +fprune-btf
>>> +Common Var(flag_prune_btf) Init(0)
>>> +Generate minimal BTF debug info.
>>> +
>>> frandom-seed
>>> Common Var(common_deferred_options) Defer
>>> 
>>> diff --git a/gcc/ctfc.cc b/gcc/ctfc.cc
>>> index 678ca2a072cf..27fc3a6d5851 100644
>>> --- a/gcc/ctfc.cc
>>> +++ b/gcc/ctfc.cc
>>> @@ -913,7 +913,7 @@ ctfc_get_dvd_srcloc (ctf_dvdef_ref dvd, ctf_srcloc_ref loc)
>>> /* Initialize the CTF string table.
>>>    The first entry in the CTF string table (empty string) is added.  */
>>> 
>>> -static void
>>> +void
>>> init_ctf_strtable (ctf_strtable_t * strtab)
>>> {
>>>   strtab->ctstab_head = NULL;
>>> diff --git a/gcc/ctfc.h b/gcc/ctfc.h
>>> index d0b724817a7f..29267dc036d1 100644
>>> --- a/gcc/ctfc.h
>>> +++ b/gcc/ctfc.h
>>> @@ -369,6 +369,9 @@ extern unsigned int ctfc_get_num_ctf_vars (ctf_container_ref);
>>> 
>>> extern ctf_strtable_t * ctfc_get_strtab (ctf_container_ref, int);
>>> 
>>> +extern void init_ctf_strtable (ctf_strtable_t *);
>>> +extern void ctfc_delete_strtab (ctf_strtable_t *);
>>> +
>>> /* Get the length of the specified string table in the CTF container.  */
>>> 
>>> extern size_t ctfc_get_strtab_len (ctf_container_ref, int);
>>> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
>>> index 517a782987d9..6dac00d14381 100644
>>> --- a/gcc/doc/invoke.texi
>>> +++ b/gcc/doc/invoke.texi
>>> @@ -534,6 +534,7 @@ Objective-C and Objective-C++ Dialects}.
>>> -gvms -gz@r{[}=@var{type}@r{]}
>>> -gsplit-dwarf  -gdescribe-dies  -gno-describe-dies
>>> -fdebug-prefix-map=@var{old}=@var{new}  -fdebug-types-section
>>> +-fprune-btf -fno-prune-btf
>>> -fno-eliminate-unused-debug-types
>>> -femit-struct-debug-baseonly  -femit-struct-debug-reduced
>>> -femit-struct-debug-detailed@r{[}=@var{spec-list}@r{]}
>>> @@ -12292,6 +12293,25 @@ compressed debug sections, the option is rejected.  Otherwise, if the
>>> assembler does not support them, @option{-gz} is silently ignored when
>>> producing object files.
>>> 
>>> +@opindex fprune-btf
>>> +@opindex fno-prune-btf
>>> +@item -fprune-btf
>>> +@itemx -fno-prune-btf
>>> +Prune BTF information before emission.  When pruning, only type
>>> +information for types used by global variables and file-scope functions
>>> +will be emitted.  If compiling for the BPF target with BPF CO-RE
>>> +enabled, type information will also be emitted for types used in BPF
>>> +CO-RE relocations.  In addition, struct and union types which are only
>>> +referred to via pointers from members of other struct or union types
>>> +shall be pruned and replaced with BTF_KIND_FWD, as though those types
>>> +were only present in the input as forward declarations.
>>> +
>>> +This option substantially reduces the size of produced BTF information,
>>> +but at significant loss in the amount of detailed type information.
>>> +It is primarily useful when compiling for the BPF target, to minimize
>>> +the size of the resulting object, and to eliminate BTF information
>>> +which is not immediately relevant to the BPF program loading process.
>>> +
>>> @opindex femit-struct-debug-baseonly
>>> @item -femit-struct-debug-baseonly
>>> Emit debug information for struct-like types
>>> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c
>>> new file mode 100644
>>> index 000000000000..3c9b59a07ecf
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c
>>> @@ -0,0 +1,25 @@
>>> +/* Simple test of -fprune-btf option operation.
>>> +   Since 'struct foo' is not used, no BTF shall be emitted for it.  */
>>> +
>>> +/* { dg-do compile } */
>>> +/* { dg-options "-gbtf -fprune-btf -dA" } */
>>> +
>>> +/* No BTF info for 'struct foo' nor types used only by it.  */
>>> +/* { dg-final { scan-assembler-not "BTF_KIND_STRUCT 'foo'" } } */
>>> +/* { dg-final { scan-assembler-not "BTF_KIND_INT 'char'" } } */
>>> +
>>> +/* We should get BTF info for 'struct bar' since it is used.  */
>>> +/* { dg-final { scan-assembler "BTF_KIND_STRUCT 'bar'"} } */
>>> +
>>> +struct foo {
>>> +  int a;
>>> +  char c;
>>> +};
>>> +
>>> +struct bar {
>>> +  int x;
>>> +  long z[4];
>>> +};
>>> +
>>> +struct bar a_bar;
>>> +
>>> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c
>>> new file mode 100644
>>> index 000000000000..20183dffcc7f
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c
>>> @@ -0,0 +1,33 @@
>>> +/* Test that -fprune-btf does not chase pointer-to-struct members.  */
>>> +
>>> +/* { dg-do compile } */
>>> +/* { dg-options "-gbtf -fprune-btf -dA" } */
>>> +
>>> +/* Only use of B is via a pointer member of C.
>>> +   Full BTF for B is replaced with a forward.  */
>>> +/* { dg-final { scan-assembler-not "BTF_KIND_STRUCT 'B'" } } */
>>> +/* { dg-final { scan-assembler-times "TYPE \[0-9\]+ BTF_KIND_FWD 'B'" 1 } } */
>>> +
>>> +/* Detailed info for B is omitted, and A is otherwise unused.  */
>>> +/* { dg-final { scan-assembler-not "BTF_KIND_\[A-Z\]+ 'A'" } } */
>>> +
>>> +/* { dg-final { scan-assembler "BTF_KIND_STRUCT 'C'" } } */
>>> +
>>> +struct A;
>>> +
>>> +struct B {
>>> +  int x;
>>> +  int (*do_A_thing) (int, int);
>>> +  struct A *other;
>>> +};
>>> +
>>> +struct C {
>>> +  unsigned int x;
>>> +  struct B * a;
>>> +};
>>> +
>>> +int
>>> +foo (struct C *c)
>>> +{
>>> +  return c->x;
>>> +}
>>> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c
>>> new file mode 100644
>>> index 000000000000..57a079cf0b4d
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c
>>> @@ -0,0 +1,35 @@
>>> +/* Test that -fprune-btf */
>>> +
>>> +/* { dg-do compile } */
>>> +/* { dg-options "-gbtf -fprune-btf -dA" } */
>>> +
>>> +/* We expect full BTF information each struct.  */
>>> +/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_FWD 'file'" } } */
>>> +/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_STRUCT 'A'" } } */
>>> +/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_STRUCT 'B'" } } */
>>> +/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_STRUCT 'C'" } } */
>>> +
>>> +struct file;
>>> +
>>> +struct A {
>>> +  void *private;
>>> +  long (*read)(struct file *, char *, unsigned long);
>>> +  long (*write)(struct file *, const char *, unsigned long);
>>> +};
>>> +
>>> +struct B {
>>> +  unsigned int x;
>>> +  struct A **as;
>>> +};
>>> +
>>> +struct C {
>>> +  struct A *arr_a[4];
>>> +  struct A *lone_a;
>>> +  unsigned int z;
>>> +};
>>> +
>>> +unsigned int
>>> +foo (struct B *b, struct C *c)
>>> +{
>>> +  return b->x + c->z;
>>> +}
>>> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c
>>> new file mode 100644
>>> index 000000000000..bf3a25e984ff
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c
>>> @@ -0,0 +1,20 @@
>>> +/* Test special meaning of .maps section for BTF when pruning.  For global
>>> +   variables of struct type placed in this section, we must treat members as
>>> +   though they are used directly, always collecting pointee types.
>>> +   Therefore, full type information for struct keep_me should be emitted.  */
>>> +
>>> +/* { dg-do compile } */
>>> +/* { dg-options "-gbtf -fprune-btf -dA" } */
>>> +
>>> +/* { dg-final { scan-assembler-not "BTF_KIND_FWD 'keep_me'" } } */
>>> +/* { dg-final { scan-assembler "BTF_KIND_STRUCT 'keep_me'" } } */
>>> +
>>> +struct keep_me {
>>> +  int a;
>>> +  char c;
>>> +};
>>> +
>>> +struct {
>>> +  int *key;
>>> +  struct keep_me *value;
>>> +} my_map __attribute__((section (".maps")));
>>> --
>>> 2.43.0
>>> 

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

* Re: [PATCH v3 1/6] ctf, btf: restructure CTF/BTF emission
  2024-05-30 21:32 ` [PATCH v3 1/6] ctf, btf: restructure CTF/BTF emission David Faust
@ 2024-06-05  7:27   ` Indu Bhagat
  0 siblings, 0 replies; 16+ messages in thread
From: Indu Bhagat @ 2024-06-05  7:27 UTC (permalink / raw)
  To: David Faust, gcc-patches; +Cc: jose.marchesi, cupertino.miranda

On 5/30/24 14:32, David Faust wrote:
> This commit makes some structural changes to the CTF/BTF debug info
> emission.  In particular:
> 
>   a) CTF is new always fully generated and emitted before any
>      BTF-related procedures are run.  This means that BTF-related
>      functions can change, even irreversibly, the shared in-memory
>      representation used by the two formats without issue.
> 
>   b) BTF generation has fewer entry points, and is cleanly divided
>      into early_finish and finish.
> 
>   c) BTF is now always emitted at finish (called from dwarf2out_finish),
>      for all targets in non-LTO builds, rather than being emitted at
>      early_finish for targets other than BPF CO-RE.  In LTO builds,
>      BTF is emitted at early_finish as before.
> 
>      Note that this change alone does not alter the contents of BTF at
>      all, regardless of whether it would have previously been emitted at
>      early_finish or finish, because the calculation of the BTF to be
>      emitted is not moved by this patch, only the write-out.
> 
> The changes are transparent to both CTF and BTF emission.
> 

OK.

This will work to keep supporting -flto with BTF (for non-BPF targets) 
and of course -flto with CTF.

One question/nit below.

> gcc/
> 	* btfout.cc (btf_init_postprocess): Rename to...
> 	(btf_early_finish): ...this.
> 	(btf_output): Rename to...
> 	(btf_finish): ...this.
> 	* ctfc.h: Analogous changes.
> 	* dwarf2ctf.cc (ctf_debug_early_finish): Conditionally call
> 	btf_early_finish, or ctf_finalize as appropriate.  Emit BTF
> 	here for LTO builds.
> 	(ctf_debug_finish): Always call btf_finish here if generating
> 	BTF info in non-LTO builds.
> 	(ctf_debug_finalize, ctf_debug_init_postprocess): Delete.
> 	* dwarf2out.cc (dwarf2out_early_finish): Remove call to
> 	ctf_debug_init_postprocess.
> ---
>   gcc/btfout.cc    | 28 +++++++++++++++++++++
>   gcc/ctfc.h       |  4 +--
>   gcc/dwarf2ctf.cc | 65 +++++++++++++++---------------------------------
>   gcc/dwarf2out.cc |  2 --
>   4 files changed, 50 insertions(+), 49 deletions(-)
> 
> diff --git a/gcc/btfout.cc b/gcc/btfout.cc
> index 07f066a47068..1b6a9ed811f0 100644
> --- a/gcc/btfout.cc
> +++ b/gcc/btfout.cc
> @@ -1491,6 +1491,34 @@ btf_finalize (void)
>     tu_ctfc = NULL;
>   }
>   
> +/* 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
> +   BTF_KIND_FUNC records.  */
> +
> +void
> +btf_early_finish (void)
> +{
> +  btf_init_postprocess ();
> +}
> +
> +/* Late entry point for BTF generation, called from dwarf2out_finish ().
> +   Complete and emit BTF information.  */
> +
> +void
> +btf_finish (const char * filename)
> +{
> +  btf_output (filename);
> +
> +  /* If compiling for BPF with CO-RE info, we cannot deallocate until after
> +     CO-RE information is created, which happens very late in BPF backend.

I am wondering if it is more precise to say that "until after contents 
for the .BTF.ext section are finalized" ? We have already called the 
btf_output () above, which means _some_ CO-RE information is already 
created (like the accessor strings for CO-RE relocs that go in the .BTF 
section).

> +     Therefore, the deallocation (i.e. btf_finalize ()) is delayed until
> +     TARGET_ASM_FILE_END for BPF CO-RE.  */
> +  if (!btf_with_core_debuginfo_p ())
> +    btf_finalize ();
> +}
> +
> +
>   /* Traversal function for all BTF_KIND_FUNC type records.  */
>   
>   bool
> diff --git a/gcc/ctfc.h b/gcc/ctfc.h
> index fa188bf2f5a4..e7bd93901cfa 100644
> --- a/gcc/ctfc.h
> +++ b/gcc/ctfc.h
> @@ -384,8 +384,8 @@ extern void ctf_init (void);
>   extern void ctf_output (const char * filename);
>   extern void ctf_finalize (void);
>   
> -extern void btf_output (const char * filename);
> -extern void btf_init_postprocess (void);
> +extern void btf_early_finish (void);
> +extern void btf_finish (const char * filename);
>   extern void btf_finalize (void);
>   
>   extern ctf_container_ref ctf_get_tu_ctfc (void);
> diff --git a/gcc/dwarf2ctf.cc b/gcc/dwarf2ctf.cc
> index dc59569fe560..8f9e2fada9e3 100644
> --- a/gcc/dwarf2ctf.cc
> +++ b/gcc/dwarf2ctf.cc
> @@ -933,30 +933,6 @@ gen_ctf_type (ctf_container_ref ctfc, dw_die_ref die)
>     return type_id;
>   }
>   
> -/* Prepare for output and write out the CTF debug information.  */
> -
> -static void
> -ctf_debug_finalize (const char *filename, bool btf)
> -{
> -  if (btf)
> -    {
> -      btf_output (filename);
> -      /* btf_finalize when compiling BPF applciations gets deallocated by the
> -	 BPF target in bpf_file_end.  */
> -      if (btf_debuginfo_p () && !btf_with_core_debuginfo_p ())
> -	btf_finalize ();
> -    }
> -
> -  else
> -    {
> -      /* Emit the collected CTF information.  */
> -      ctf_output (filename);
> -
> -      /* Reset the CTF state.  */
> -      ctf_finalize ();
> -    }
> -}
> -
>   bool
>   ctf_do_die (dw_die_ref die)
>   {
> @@ -996,27 +972,27 @@ ctf_debug_init (void)
>     add_name_attribute (ctf_unknown_die, "unknown");
>   }
>   
> -/* Preprocess the CTF debug information after initialization.  */
> -
> -void
> -ctf_debug_init_postprocess (bool btf)
> -{
> -  /* Only BTF requires postprocessing right after init.  */
> -  if (btf)
> -    btf_init_postprocess ();
> -}
> -
>   /* Early finish CTF/BTF debug info.  */
>   
>   void
>   ctf_debug_early_finish (const char * filename)
>   {
> -  /* Emit CTF debug info early always.  */
> -  if (ctf_debug_info_level > CTFINFO_LEVEL_NONE
> -      /* Emit BTF debug info early if CO-RE relocations are not
> -	 required.  */
> -      || (btf_debuginfo_p () && !btf_with_core_debuginfo_p ()))
> -    ctf_debug_finalize (filename, btf_debuginfo_p ());
> +  /* Emit the collected CTF information.  */
> +  if (ctf_debug_info_level > CTFINFO_LEVEL_NONE)
> +    ctf_output (filename);
> +
> +  /* If emitting BTF, start translation to BTF.  */
> +  if (btf_debuginfo_p ())
> +    {
> +      btf_early_finish ();
> +
> +      /* For LTO builds, also emit BTF now.  */
> +      if (flag_lto && !in_lto_p)
> +	btf_finish (filename);
> +    }
> +  else
> +    /* Otherwise, done with the CTF container.  */
> +    ctf_finalize ();
>   }
>   
>   /* Finish CTF/BTF debug info emission.  */
> @@ -1024,11 +1000,10 @@ ctf_debug_early_finish (const char * filename)
>   void
>   ctf_debug_finish (const char * filename)
>   {
> -  /* Emit BTF debug info here when CO-RE relocations need to be generated.
> -     BTF with CO-RE relocations needs to be generated when CO-RE is in effect
> -     for the BPF target.  */
> -  if (btf_debuginfo_p () && btf_with_core_debuginfo_p ())
> -    ctf_debug_finalize (filename, btf_debuginfo_p ());
> +  /* Emit BTF late, unless this is an LTO build in which case it was
> +     already done early.  */
> +  if (btf_debuginfo_p () && !flag_lto)
> +    btf_finish (filename);
>   }
>   
>   #include "gt-dwarf2ctf.h"
> diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
> index 5b064ffd78ad..e406df8673ce 100644
> --- a/gcc/dwarf2out.cc
> +++ b/gcc/dwarf2out.cc
> @@ -33202,8 +33202,6 @@ dwarf2out_early_finish (const char *filename)
>         ctf_debug_do_cu (comp_unit_die ());
>         for (limbo_die_node *node = limbo_die_list; node; node = node->next)
>   	ctf_debug_do_cu (node->die);
> -      /* Post process the debug data in the CTF container if necessary.  */
> -      ctf_debug_init_postprocess (btf_debuginfo_p ());
>   
>         ctf_debug_early_finish (filename);
>       }


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

* Re: [PATCH v3 2/6] ctf: use pointers instead of IDs internally
  2024-05-30 21:32 ` [PATCH v3 2/6] ctf: use pointers instead of IDs internally David Faust
@ 2024-06-05  7:31   ` Indu Bhagat
  0 siblings, 0 replies; 16+ messages in thread
From: Indu Bhagat @ 2024-06-05  7:31 UTC (permalink / raw)
  To: David Faust, gcc-patches; +Cc: jose.marchesi, cupertino.miranda

On 5/30/24 14:32, David Faust wrote:
> This patch replaces all inter-type references in the ctfc internal data
> structures with pointers, rather than the references-by-ID which were
> used previously.
> 
> A couple of small updates in the BPF backend are included to make it
> compatible with the change.
> 
> This change is only to the in-memory representation of various CTF
> structures to make them easier to work with in various cases.  It is
> outwardly transparent; there is no change in emitted CTF.
> 
> gcc/
> 	* btfout.cc (BTF_VOID_TYPEID, BTF_INIT_TYPEID): Move defines to
> 	include/btf.h.
> 	(btf_dvd_emit_preprocess_cb, btf_emit_preprocess)
> 	(btf_dmd_representable_bitfield_p, btf_asm_array, btf_asm_varent)
> 	(btf_asm_sou_member, btf_asm_func_arg, btf_init_postprocess):
> 	Adapt to structural changes in ctf_* structs.
> 	* ctfc.h (struct ctf_dtdef): Add forward declaration.
> 	(ctf_dtdef_t, ctf_dtdef_ref): Move typedefs earlier.
> 	(struct ctf_arinfo, struct ctf_funcinfo, struct ctf_sliceinfo)
> 	(struct ctf_itype, struct ctf_dmdef, struct ctf_func_arg)
> 	(struct ctf_dvdef): Use pointers instead of type IDs for
> 	references to other types and use typedefs where appropriate.
> 	(struct ctf_dtdef): Add ref_type member.
> 	(ctf_type_exists): Use pointer instead of type ID.
> 	(ctf_add_reftype, ctf_add_enum, ctf_add_slice, ctf_add_float)
> 	(ctf_add_integer, ctf_add_unknown, ctf_add_pointer)
> 	(ctf_add_array, ctf_add_forward, ctf_add_typedef)
> 	(ctf_add_function, ctf_add_sou, ctf_add_enumerator)
> 	(ctf_add_variable): Likewise. Return pointer instead of ID.
> 	(ctf_lookup_tree_type): Return pointer to type instead of ID.
> 	* ctfc.cc: Analogous changes.
> 	* ctfout.cc (ctf_asm_type, ctf_asm_slice, ctf_asm_varent)
> 	(ctf_asm_sou_lmember, ctf_asm_sou_member, ctf_asm_func_arg)
> 	(output_ctf_objt_info): Adapt to changes.
> 	* dwarf2ctf.cc (gen_ctf_type, gen_ctf_void_type)
> 	(gen_ctf_unknown_type, gen_ctf_base_type, gen_ctf_pointer_type)
> 	(gen_ctf_subrange_type, gen_ctf_array_type, gen_ctf_typedef)
> 	(gen_ctf_modifier_type, gen_ctf_sou_type, gen_ctf_function_type)
> 	(gen_ctf_enumeration_type, gen_ctf_variable, gen_ctf_function)
> 	(gen_ctf_type, ctf_do_die): Likewise.
> 	* config/bpf/btfext-out.cc (struct btf_ext_core_reloc): Use
> 	pointer instead of type ID.
> 	(bpf_core_reloc_add, bpf_core_get_sou_member_index)
> 	(output_btfext_core_sections): Adapt to above changes.
> 	* config/bpf/core-builtins.cc (process_type): Likewise.
> 
> include/
> 	* btf.h (BTF_VOID_TYPEID, BTF_INIT_TYPEID): Move defines here,
> 	from gcc/btfout.cc.

The patch looks OK overall. Few updates to code comments for function 
description or in the body of the function were missed.  I have pointed 
them out below.

OK with those addressed.

Thanks

> ---
>   gcc/btfout.cc                   |  40 +++---
>   gcc/config/bpf/btfext-out.cc    |  14 +-
>   gcc/config/bpf/core-builtins.cc |   3 +-
>   gcc/ctfc.cc                     | 139 +++++++++---------
>   gcc/ctfc.h                      |  90 ++++++------
>   gcc/ctfout.cc                   |  22 +--
>   gcc/dwarf2ctf.cc                | 244 +++++++++++++++-----------------
>   include/btf.h                   |   5 +
>   8 files changed, 278 insertions(+), 279 deletions(-)
> 
> diff --git a/gcc/btfout.cc b/gcc/btfout.cc
> index 1b6a9ed811f0..40e8d8c5c01b 100644
> --- a/gcc/btfout.cc
> +++ b/gcc/btfout.cc
> @@ -61,11 +61,6 @@ static char btf_info_section_label[MAX_BTF_LABEL_BYTES];
>   #define BTF_INFO_SECTION_LABEL  "Lbtf"
>   #endif
>   
> -/* BTF encodes void as type id 0.  */
> -
> -#define BTF_VOID_TYPEID 0
> -#define BTF_INIT_TYPEID 1
> -
>   #define BTF_INVALID_TYPEID 0xFFFFFFFF
>   
>   /* Mapping of CTF variables to the IDs they will be assigned when they are
> @@ -626,7 +621,8 @@ btf_dvd_emit_preprocess_cb (ctf_dvdef_ref *slot, ctf_container_ref arg_ctfc)
>       return 1;
>   
>     /* Do not add variables which refer to unsupported types.  */
> -  if (!voids.contains (var->dvd_type) && btf_removed_type_p (var->dvd_type))
> +  if (!voids.contains (var->dvd_type->dtd_type)
> +      && btf_removed_type_p (var->dvd_type->dtd_type))
>       return 1;
>   
>     arg_ctfc->ctfc_vars_list[num_vars_added] = var;
> @@ -716,7 +712,7 @@ btf_emit_preprocess (ctf_container_ref ctfc)
>   static bool
>   btf_dmd_representable_bitfield_p (ctf_container_ref ctfc, ctf_dmdef_t *dmd)
>   {
> -  ctf_dtdef_ref ref_type = ctfc->ctfc_types_list[dmd->dmd_type];
> +  ctf_dtdef_ref ref_type = ctfc->ctfc_types_list[dmd->dmd_type->dtd_type];
>   
>     if (CTF_V2_INFO_KIND (ref_type->dtd_data.ctti_info) == CTF_K_SLICE)
>       {
> @@ -913,8 +909,8 @@ btf_asm_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
>   static void
>   btf_asm_array (ctf_container_ref ctfc, ctf_arinfo_t arr)
>   {
> -  btf_asm_type_ref ("bta_elem_type", ctfc, arr.ctr_contents);
> -  btf_asm_type_ref ("bta_index_type", ctfc, arr.ctr_index);
> +  btf_asm_type_ref ("bta_elem_type", ctfc, arr.ctr_contents->dtd_type);
> +  btf_asm_type_ref ("bta_index_type", ctfc, arr.ctr_index->dtd_type);
>     dw2_asm_output_data (4, arr.ctr_nelems, "bta_nelems");
>   }
>   
> @@ -927,7 +923,7 @@ btf_asm_varent (ctf_container_ref ctfc, ctf_dvdef_ref var)
>   		       (*(btf_var_ids->get (var)) + num_types_added + 1),
>   		       var->dvd_name);
>     dw2_asm_output_data (4, BTF_TYPE_INFO (BTF_KIND_VAR, 0, 0), "btv_info");
> -  btf_asm_type_ref ("btv_type", ctfc, var->dvd_type);
> +  btf_asm_type_ref ("btv_type", ctfc, var->dvd_type->dtd_type);
>     dw2_asm_output_data (4, var->dvd_visibility, "btv_linkage");
>   }
>   
> @@ -937,8 +933,8 @@ btf_asm_varent (ctf_container_ref ctfc, ctf_dvdef_ref var)
>   static void
>   btf_asm_sou_member (ctf_container_ref ctfc, ctf_dmdef_t * dmd, unsigned int idx)
>   {
> -  ctf_dtdef_ref ref_type = ctfc->ctfc_types_list[dmd->dmd_type];
> -  ctf_id_t base_type = dmd->dmd_type;
> +  ctf_dtdef_ref ref_type = ctfc->ctfc_types_list[dmd->dmd_type->dtd_type];
> +  ctf_id_t base_type = dmd->dmd_type->dtd_type;
>     uint64_t sou_offset = dmd->dmd_offset;
>   
>     dw2_asm_output_data (4, dmd->dmd_name_offset,
> @@ -959,7 +955,7 @@ btf_asm_sou_member (ctf_container_ref ctfc, ctf_dmdef_t * dmd, unsigned int idx)
>   	  sou_offset |= ((bits & 0xff) << 24);
>   
>   	  /* Refer to the base type of the slice.  */
> -	  base_type = ref_type->dtd_u.dtu_slice.cts_type;
> +	  base_type = ref_type->dtd_u.dtu_slice.cts_type->dtd_type;
>   	}
>         else
>   	{
> @@ -1003,9 +999,11 @@ btf_asm_func_arg (ctf_container_ref ctfc, ctf_func_arg_t * farg,
>     else
>       dw2_asm_output_data (4, 0, "farg_name");
>   
> -  btf_asm_type_ref ("farg_type", ctfc, (btf_removed_type_p (farg->farg_type)
> -					? BTF_VOID_TYPEID
> -					: farg->farg_type));
> +  ctf_id_t ref_id = BTF_VOID_TYPEID;
> +  if (farg->farg_type && !btf_removed_type_p (farg->farg_type->dtd_type))
> +    ref_id = farg->farg_type->dtd_type;
> +
> +  btf_asm_type_ref ("farg_type", ctfc, ref_id);
>   }
>   
>   /* Asm'out a BTF_KIND_FUNC type.  */
> @@ -1381,7 +1379,7 @@ btf_init_postprocess (void)
>        to create the const modifier type (if needed) now, before making the types
>        list.  So we can't avoid iterating with FOR_EACH_VARIABLE here, and then
>        again when creating the DATASEC entries.  */
> -  ctf_id_t constvoid_id = CTF_NULL_TYPEID;
> +  ctf_dtdef_ref constvoid_dtd = NULL;
>     varpool_node *var;
>     FOR_EACH_VARIABLE (var)
>       {
> @@ -1400,10 +1398,10 @@ btf_init_postprocess (void)
>   	    continue;
>   
>   	  /* Create the 'const' modifier type for void.  */
> -	  if (constvoid_id == CTF_NULL_TYPEID)
> -	    constvoid_id = ctf_add_reftype (tu_ctfc, CTF_ADD_ROOT,
> -					    dvd->dvd_type, CTF_K_CONST, NULL);
> -	  dvd->dvd_type = constvoid_id;
> +	  if (constvoid_dtd == NULL)
> +	    constvoid_dtd = ctf_add_reftype (tu_ctfc, CTF_ADD_ROOT,
> +					     dvd->dvd_type, CTF_K_CONST, NULL);
> +	  dvd->dvd_type = constvoid_dtd;
>   	}
>       }
>   
> diff --git a/gcc/config/bpf/btfext-out.cc b/gcc/config/bpf/btfext-out.cc
> index 7ec438fd1d10..b3df7b555d5f 100644
> --- a/gcc/config/bpf/btfext-out.cc
> +++ b/gcc/config/bpf/btfext-out.cc
> @@ -134,7 +134,7 @@ struct GTY ((chain_next ("%h.next"))) btf_ext_lineinfo
>   
>   /* Internal representation of a BPF CO-RE relocation record.  */
>   struct GTY ((chain_next ("%h.next"))) btf_ext_core_reloc {
> -  unsigned int bpfcr_type;		/* BTF type ID of container.  */
> +  ctf_dtdef_ref bpfcr_type;		/* BTF type involved in relocation.  */
>     unsigned int  bpfcr_astr_off;		/* Offset of access string in .BTF
>   					   string table.  */
>     rtx_code_label * bpfcr_insn_label;	/* RTX label attached to instruction
> @@ -296,13 +296,14 @@ bpf_core_reloc_add (const tree type, const char * section_name,
>     struct btf_ext_core_reloc *bpfcr = bpf_create_core_reloc (section_name, &sec);
>   
>     ctf_container_ref ctfc = ctf_get_tu_ctfc ();
> +  ctf_dtdef_ref dtd = ctf_lookup_tree_type (ctfc, type);
>   
>     /* Buffer the access string in the auxiliary strtab.  */
>     bpfcr->bpfcr_astr_off = 0;
>     gcc_assert (accessor != NULL);
>     bpfcr->bpfcr_astr_off = btf_ext_add_string (accessor);
>   
> -  bpfcr->bpfcr_type = get_btf_id (ctf_lookup_tree_type (ctfc, type));
> +  bpfcr->bpfcr_type = dtd;
>     bpfcr->bpfcr_insn_label = label;
>     bpfcr->bpfcr_kind = kind;
>   
> @@ -341,7 +342,8 @@ bpf_core_get_sou_member_index (ctf_container_ref ctfc, const tree node)
>         for (dmd = dtd->dtd_u.dtu_members;
>              dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
>           {
> -	  bool field_has_btf = get_btf_id (dmd->dmd_type) <= BTF_MAX_TYPE;
> +	  bool field_has_btf = (dmd->dmd_type
> +				&& dmd->dmd_type->dtd_type <= BTF_MAX_TYPE);
>   
>   	  if (field == node)
>   	    return field_has_btf ? i : -1;
> @@ -574,8 +576,10 @@ output_btfext_core_sections (void)
>   				 false);
>   	      char *str = xstrdup (pp_formatted_text (&pp));
>   
> -	      dw2_asm_output_data (4, bpfcr->bpfcr_type, "bpfcr_type (%s)",
> -				   str);
> +	      uint32_t type_id = bpfcr->bpfcr_type
> +		? bpfcr->bpfcr_type->dtd_type
> +		: BTF_VOID_TYPEID;
> +	      dw2_asm_output_data (4, type_id, "bpfcr_type (%s)", str);
>   	      dw2_asm_output_data (4, bpfcr->bpfcr_astr_off + str_aux_off,
>   				   "bpfcr_astr_off (\"%s\")",
>   				   bpfcr->info.accessor_str);
> diff --git a/gcc/config/bpf/core-builtins.cc b/gcc/config/bpf/core-builtins.cc
> index 829acea98f79..232bebcadbd5 100644
> --- a/gcc/config/bpf/core-builtins.cc
> +++ b/gcc/config/bpf/core-builtins.cc
> @@ -1021,7 +1021,8 @@ process_type (struct cr_builtins *data)
>         && data->default_value != NULL)
>     {
>       ctf_container_ref ctfc = ctf_get_tu_ctfc ();
> -    unsigned int btf_id = get_btf_id (ctf_lookup_tree_type (ctfc, ret.type));
> +    ctf_dtdef_ref dtd = ctf_lookup_tree_type (ctfc, ret.type);
> +    unsigned int btf_id = dtd ? dtd->dtd_type : BTF_VOID_TYPEID;
>       data->rtx_default_value = expand_normal (build_int_cst (integer_type_node,
>   							    btf_id));
>     }
> diff --git a/gcc/ctfc.cc b/gcc/ctfc.cc
> index 67711606ab85..678ca2a072cf 100644
> --- a/gcc/ctfc.cc
> +++ b/gcc/ctfc.cc
> @@ -373,9 +373,9 @@ ctf_add_cuname (ctf_container_ref ctfc, const char * filename)
>      ctf_dvd_lookup, as applicable, to ascertain that the CTF type or the CTF
>      variable respectively does not already exist, and then add it.  */
>   
> -static ctf_id_t
> +static ctf_dtdef_ref
>   ctf_add_generic (ctf_container_ref ctfc, uint32_t flag, const char * name,
> -		 ctf_dtdef_ref * rp, dw_die_ref die)
> +		 dw_die_ref die)
>   {
>     ctf_dtdef_ref dtd;
>     ctf_id_t type;
> @@ -397,18 +397,16 @@ ctf_add_generic (ctf_container_ref ctfc, uint32_t flag, const char * name,
>   
>     ctf_dtd_insert (ctfc, dtd);
>   
> -  *rp = dtd;
> -  return type;
> +  return dtd;
>   }
>   
> -static ctf_id_t
> +static ctf_dtdef_ref
>   ctf_add_encoded (ctf_container_ref ctfc, uint32_t flag, const char * name,
>   		 const ctf_encoding_t * ep, uint32_t kind, dw_die_ref die)
>   {
>     ctf_dtdef_ref dtd;
> -  ctf_id_t type;
>   
> -  type = ctf_add_generic (ctfc, flag, name, &dtd, die);
> +  dtd = ctf_add_generic (ctfc, flag, name, die);
>   
>     dtd->dtd_data.ctti_info = CTF_TYPE_INFO (kind, flag, 0);
>   
> @@ -424,83 +422,81 @@ ctf_add_encoded (ctf_container_ref ctfc, uint32_t flag, const char * name,
>   
>     ctfc->ctfc_num_stypes++;
>   
> -  return type;
> +  return dtd;
>   }
>   
> -ctf_id_t
> -ctf_add_reftype (ctf_container_ref ctfc, uint32_t flag, ctf_id_t ref,
> +ctf_dtdef_ref
> +ctf_add_reftype (ctf_container_ref ctfc, uint32_t flag, ctf_dtdef_ref ref,
>   		 uint32_t kind, dw_die_ref die)
>   {
>     ctf_dtdef_ref dtd;
> -  ctf_id_t type;
>   
> -  gcc_assert (ref <= CTF_MAX_TYPE);
> +  gcc_assert (ref != NULL);
>   
> -  type = ctf_add_generic (ctfc, flag, NULL, &dtd, die);
> +  dtd = ctf_add_generic (ctfc, flag, NULL, die);
>     dtd->dtd_data.ctti_info = CTF_TYPE_INFO (kind, flag, 0);
>     /* Caller of this API must guarantee that a CTF type with id = ref already
>        exists.  This will also be validated for us at link-time.  */
> -  dtd->dtd_data.ctti_type = (uint32_t) ref;
> +  dtd->dtd_data.ctti_type = (uint32_t) ref->dtd_type;
> +  dtd->ref_type = ref;
>   
>     ctfc->ctfc_num_stypes++;
>   
> -  return type;
> +  return dtd;
>   }
>   
> -ctf_id_t
> +ctf_dtdef_ref
>   ctf_add_forward (ctf_container_ref ctfc, uint32_t flag, const char * name,
>   		 uint32_t kind, dw_die_ref die)
>   {
>     ctf_dtdef_ref dtd;
> -  ctf_id_t type = 0;
>   
> -  type = ctf_add_generic (ctfc, flag, name, &dtd, die);
> +  dtd = ctf_add_generic (ctfc, flag, name, die);
>   
>     dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_FORWARD, flag, 0);
>     dtd->dtd_data.ctti_type = kind;
>   
>     ctfc->ctfc_num_stypes++;
>   
> -  return type;
> +  return dtd;
>   }
>   
> -ctf_id_t
> +ctf_dtdef_ref
>   ctf_add_typedef (ctf_container_ref ctfc, uint32_t flag, const char * name,
> -		 ctf_id_t ref, dw_die_ref die)
> +		 ctf_dtdef_ref ref, dw_die_ref die)
>   {
>     ctf_dtdef_ref dtd;
> -  ctf_id_t type;
>   
> -  gcc_assert (ref <= CTF_MAX_TYPE);
> +  gcc_assert (ref != NULL);
>     /* Nameless Typedefs are not expected.  */
>     gcc_assert ((name != NULL) && strcmp (name, ""));
>   
> -  type = ctf_add_generic (ctfc, flag, name, &dtd, die);
> +  dtd = ctf_add_generic (ctfc, flag, name, die);
>     dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_TYPEDEF, flag, 0);
>     /* Caller of this API must guarantee that a CTF type with id = ref already
>        exists.  This will also be validated for us at link-time.  */

This comment may cause confusion now. Can you please reword or may be 
remove it altogether ?

Other places where a similar function needs update: ctf_add_slice too. 
Please do the same action on that comment too.

> -  dtd->dtd_data.ctti_type = (uint32_t) ref;
> +  dtd->dtd_data.ctti_type = (uint32_t) ref->dtd_type;
> +  dtd->ref_type = ref;
>   
>     gcc_assert (dtd->dtd_type != dtd->dtd_data.ctti_type);
>   
>     ctfc->ctfc_num_stypes++;
>   
> -  return type;
> +  return dtd;
>   }
>   
> -ctf_id_t
> -ctf_add_slice (ctf_container_ref ctfc, uint32_t flag, ctf_id_t ref,
> +ctf_dtdef_ref
> +ctf_add_slice (ctf_container_ref ctfc, uint32_t flag, ctf_dtdef_ref ref,
>   	       uint32_t bit_offset, uint32_t bit_size, dw_die_ref die)
>   {
>     ctf_dtdef_ref dtd;
> -  ctf_id_t type;
>     uint32_t roundup_nbytes;
>   
>     gcc_assert ((bit_size <= 255) && (bit_offset <= 255));
>   
> -  gcc_assert (ref <= CTF_MAX_TYPE);
> +  gcc_assert (ref != NULL);
>   
> -  type = ctf_add_generic (ctfc, flag, NULL, &dtd, die);
> +  dtd = ctf_add_generic (ctfc, flag, NULL, die);
>   
>     dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_SLICE, flag, 0);
>   
> @@ -514,49 +510,48 @@ ctf_add_slice (ctf_container_ref ctfc, uint32_t flag, ctf_id_t ref,
>   
>     /* Caller of this API must guarantee that a CTF type with id = ref already
>        exists.  This will also be validated for us at link-time.  */
> -  dtd->dtd_u.dtu_slice.cts_type = (uint32_t) ref;
> +  dtd->dtd_u.dtu_slice.cts_type = ref;
>     dtd->dtd_u.dtu_slice.cts_bits = bit_size;
>     dtd->dtd_u.dtu_slice.cts_offset = bit_offset;
>   
>     ctfc->ctfc_num_stypes++;
>   
> -  return type;
> +  return dtd;
>   }
>   
> -ctf_id_t
> +ctf_dtdef_ref
>   ctf_add_float (ctf_container_ref ctfc, uint32_t flag,
>   	       const char * name, const ctf_encoding_t * ep, dw_die_ref die)
>   {
>     return (ctf_add_encoded (ctfc, flag, name, ep, CTF_K_FLOAT, die));
>   }
>   
> -ctf_id_t
> +ctf_dtdef_ref
>   ctf_add_integer (ctf_container_ref ctfc, uint32_t flag,
>   		 const char * name, const ctf_encoding_t * ep, dw_die_ref die)
>   {
>     return (ctf_add_encoded (ctfc, flag, name, ep, CTF_K_INTEGER, die));
>   }
>   
> -ctf_id_t
> +ctf_dtdef_ref
>   ctf_add_unknown (ctf_container_ref ctfc, uint32_t flag,
>   		 const char * name, const ctf_encoding_t * ep, dw_die_ref die)
>   {
>     return (ctf_add_encoded (ctfc, flag, name, ep, CTF_K_UNKNOWN, die));
>   }
>   
> -ctf_id_t
> -ctf_add_pointer (ctf_container_ref ctfc, uint32_t flag, ctf_id_t ref,
> +ctf_dtdef_ref
> +ctf_add_pointer (ctf_container_ref ctfc, uint32_t flag, ctf_dtdef_ref ref,
>   		 dw_die_ref die)
>   {
>     return (ctf_add_reftype (ctfc, flag, ref, CTF_K_POINTER, die));
>   }
>   
> -ctf_id_t
> +ctf_dtdef_ref
>   ctf_add_array (ctf_container_ref ctfc, uint32_t flag, const ctf_arinfo_t * arp,
>   	       dw_die_ref die)
>   {
>     ctf_dtdef_ref dtd;
> -  ctf_id_t type;
>   
>     gcc_assert (arp);
>   
> @@ -564,7 +559,7 @@ ctf_add_array (ctf_container_ref ctfc, uint32_t flag, const ctf_arinfo_t * arp,
>        arp->ctr_index are already added.  This will also be validated for us at
>        link-time.  */
>   
> -  type = ctf_add_generic (ctfc, flag, NULL, &dtd, die);
> +  dtd = ctf_add_generic (ctfc, flag, NULL, die);
>   
>     dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_ARRAY, flag, 0);
>     dtd->dtd_data.ctti_size = 0;
> @@ -572,15 +567,14 @@ ctf_add_array (ctf_container_ref ctfc, uint32_t flag, const ctf_arinfo_t * arp,
>   
>     ctfc->ctfc_num_stypes++;
>   
> -  return type;
> +  return dtd;
>   }
>   
> -ctf_id_t
> +ctf_dtdef_ref
>   ctf_add_enum (ctf_container_ref ctfc, uint32_t flag, const char * name,
>   	      HOST_WIDE_INT size, bool eunsigned, dw_die_ref die)
>   {
>     ctf_dtdef_ref dtd;
> -  ctf_id_t type;
>   
>     /* In the compiler, no need to handle the case of promoting forwards to
>        enums.  This comment is simply to note a divergence from libctf.  */
> @@ -595,7 +589,7 @@ ctf_add_enum (ctf_container_ref ctfc, uint32_t flag, const char * name,
>   	= CTF_TYPE_INFO (CTF_K_FORWARD, CTF_ADD_NONROOT, 0);
>       }
>   
> -  type = ctf_add_generic (ctfc, flag, name, &dtd, die);
> +  dtd = ctf_add_generic (ctfc, flag, name, die);
>   
>     dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_ENUM, flag, 0);
>   
> @@ -608,12 +602,12 @@ ctf_add_enum (ctf_container_ref ctfc, uint32_t flag, const char * name,
>   
>     ctfc->ctfc_num_stypes++;
>   
> -  return type;
> +  return dtd;
>   }
>   
>   int
> -ctf_add_enumerator (ctf_container_ref ctfc, ctf_id_t enid, const char * name,
> -		    HOST_WIDE_INT value, dw_die_ref die)
> +ctf_add_enumerator (ctf_container_ref ctfc, ctf_dtdef_ref enum_dtd,
> +		    const char * name, HOST_WIDE_INT value, dw_die_ref die)

This function body has a comment which mentions "enid" argument.  It 
needs to be updated too. How about:

"Callers of this API must make sure that CTF_K_ENUM with enid has been ..."
   -->
"The associated CTF type of kind CTF_K_ENUM must already exist."

>   {
>     ctf_dmdef_t * dmd;
>     uint32_t kind, vlen, root;
> @@ -622,7 +616,7 @@ ctf_add_enumerator (ctf_container_ref ctfc, ctf_id_t enid, const char * name,
>        addded.  This will also be validated for us at link-time.  */
>     ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, die);
>     gcc_assert (dtd);
> -  gcc_assert (dtd->dtd_type == enid);
> +  gcc_assert (dtd == enum_dtd);
>     gcc_assert (name);
>   
>     kind = CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info);
> @@ -646,7 +640,7 @@ ctf_add_enumerator (ctf_container_ref ctfc, ctf_id_t enid, const char * name,
>   
>     /* Buffer the strings in the CTF string table.  */
>     dmd->dmd_name = ctf_add_string (ctfc, name, &(dmd->dmd_name_offset));
> -  dmd->dmd_type = CTF_NULL_TYPEID;
> +  dmd->dmd_type = NULL;
>     dmd->dmd_offset = 0;
>   
>     dmd->dmd_value = value;
> @@ -662,7 +656,7 @@ ctf_add_enumerator (ctf_container_ref ctfc, ctf_id_t enid, const char * name,
>   
>   int
>   ctf_add_member_offset (ctf_container_ref ctfc, dw_die_ref sou,
> -		       const char * name, ctf_id_t type,
> +		       const char * name, ctf_dtdef_ref type,
>   		       uint64_t bit_offset)
>   {
>     ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, sou);
> @@ -702,7 +696,7 @@ ctf_add_member_offset (ctf_container_ref ctfc, dw_die_ref sou,
>   }
>   
>   int
> -ctf_add_variable (ctf_container_ref ctfc, const char * name, ctf_id_t 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)
>   {
> @@ -747,16 +741,16 @@ ctf_add_variable (ctf_container_ref ctfc, const char * name, ctf_id_t ref,
>   
>   int
>   ctf_add_function_arg (ctf_container_ref ctfc, dw_die_ref func,
> -		      const char * name, ctf_id_t type)
> +		      const char * name, ctf_dtdef_ref arg_dtd)
>   {
> -  ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, func);
> +  ctf_dtdef_ref func_dtd = ctf_dtd_lookup (ctfc, func);
>     ctf_func_arg_t * farg;
>     uint32_t vlen;
>   
>     /* The function to which argument is being added must already exist.  */
> -  gcc_assert (dtd);
> +  gcc_assert (func_dtd);
>     /* The number of args must have been non-zero.  */
> -  vlen = CTF_V2_INFO_VLEN (dtd->dtd_data.ctti_info);
> +  vlen = CTF_V2_INFO_VLEN (func_dtd->dtd_data.ctti_info);
>     gcc_assert (vlen);
>   
>     farg = ggc_cleared_alloc<ctf_func_arg_t> ();
> @@ -766,9 +760,9 @@ ctf_add_function_arg (ctf_container_ref ctfc, dw_die_ref func,
>        these strings to avoid unnecessary bloat in CTF section in CTF V3.  */
>     farg->farg_name = ctf_add_string (ctfc, name, &(farg->farg_name_offset),
>   				    CTF_AUX_STRTAB);
> -  farg->farg_type = type;
> +  farg->farg_type = arg_dtd;
>   
> -  ctf_farg_list_append (&dtd->dtd_u.dtu_argv, farg);
> +  ctf_farg_list_append (&func_dtd->dtd_u.dtu_argv, farg);
>   
>     /* For aux_str, keep ctfc_aux_strlen updated for debugging.  */
>     if ((name != NULL) && strcmp (name, ""))
> @@ -777,13 +771,12 @@ ctf_add_function_arg (ctf_container_ref ctfc, dw_die_ref func,
>     return 0;
>   }
>   
> -ctf_id_t
> +ctf_dtdef_ref
>   ctf_add_function (ctf_container_ref ctfc, uint32_t flag, const char * name,
>   		  const ctf_funcinfo_t * ctc, dw_die_ref die,
>   		  bool from_global_func, int linkage)
>   {
>     ctf_dtdef_ref dtd;
> -  ctf_id_t type;
>     uint32_t vlen;
>   
>     gcc_assert (ctc);
> @@ -791,27 +784,27 @@ ctf_add_function (ctf_container_ref ctfc, uint32_t flag, const char * name,
>     vlen = ctc->ctc_argc;
>     gcc_assert (vlen <= CTF_MAX_VLEN);
>   
> -  type = ctf_add_generic (ctfc, flag, name, &dtd, die);
> +  dtd = ctf_add_generic (ctfc, flag, name, die);
>   
>     dtd->from_global_func = from_global_func;
>     dtd->linkage = linkage;
>     dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_FUNCTION, flag, vlen);
> +  dtd->ref_type = ctc->ctc_return;
>     /* Caller must make sure CTF types for ctc->ctc_return are already added.  */
> -  dtd->dtd_data.ctti_type = (uint32_t) ctc->ctc_return;
> +  dtd->dtd_data.ctti_type = (uint32_t) ctc->ctc_return->dtd_type;
>     /* Caller must make sure CTF types for function arguments are already added
>        via ctf_add_function_arg () API.  */
>   
>     ctfc->ctfc_num_stypes++;
>   
> -  return type;
> +  return dtd;
>   }
>   
> -ctf_id_t
> +ctf_dtdef_ref
>   ctf_add_sou (ctf_container_ref ctfc, uint32_t flag, const char * name,
>   	     uint32_t kind, size_t size, dw_die_ref die)
>   {
>     ctf_dtdef_ref dtd;
> -  ctf_id_t type = 0;
>   
>     gcc_assert ((kind == CTF_K_STRUCT) || (kind == CTF_K_UNION));
>   
> @@ -828,7 +821,7 @@ ctf_add_sou (ctf_container_ref ctfc, uint32_t flag, const char * name,
>   	= CTF_TYPE_INFO (CTF_K_FORWARD, CTF_ADD_NONROOT, 0);
>       }
>   
> -  type = ctf_add_generic (ctfc, flag, name, &dtd, die);
> +  dtd = ctf_add_generic (ctfc, flag, name, die);
>   
>     dtd->dtd_data.ctti_info = CTF_TYPE_INFO (kind, flag, 0);
>   
> @@ -845,23 +838,23 @@ ctf_add_sou (ctf_container_ref ctfc, uint32_t flag, const char * name,
>         ctfc->ctfc_num_stypes++;
>       }
>   
> -  return type;
> +  return dtd;
>   }
>   
>   /* Given a TREE_TYPE node, return the CTF type ID for that type.  */
>   

This comment needs an update: "return the CTF type object" ?

> -ctf_id_t
> +ctf_dtdef_ref
>   ctf_lookup_tree_type (ctf_container_ref ctfc, const tree type)
>   {
>     dw_die_ref die = lookup_type_die (type);
>     if (die == NULL)
> -    return CTF_NULL_TYPEID;
> +    return NULL;
>   
>     ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, die);
>     if (dtd == NULL)
> -    return CTF_NULL_TYPEID;
> +    return NULL;
>   
> -  return dtd->dtd_type;
> +  return dtd;
>   }
>   
>   /* Check if CTF for TYPE has already been generated.  Mainstay for
> @@ -870,7 +863,7 @@ ctf_lookup_tree_type (ctf_container_ref ctfc, const tree type)
>   
>   bool
>   ctf_type_exists (ctf_container_ref ctfc, dw_die_ref type,
> -		 ctf_id_t * type_id)
> +		 ctf_dtdef_ref * dtd)

The function-level comment for this one also needs an update.
"updates the TYPE_ID for the caller."
-->
"updates the CTF type object DTD for the caller" ?

>   {
>     bool exists = false;
>     ctf_dtdef_ref ctf_type_seen = ctf_dtd_lookup (ctfc, type);
> @@ -879,7 +872,7 @@ ctf_type_exists (ctf_container_ref ctfc, dw_die_ref type,
>       {
>         exists = true;
>         /* CTF type for this type exists.  */
> -      *type_id = ctf_type_seen->dtd_type;
> +      *dtd = ctf_type_seen;
>       }
>   
>     return exists;
> diff --git a/gcc/ctfc.h b/gcc/ctfc.h
> index e7bd93901cfa..b2060eaf7e9b 100644
> --- a/gcc/ctfc.h
> +++ b/gcc/ctfc.h
> @@ -48,6 +48,10 @@ along with GCC; see the file COPYING3.  If not see
>   
>   typedef uint64_t ctf_id_t;
>   
> +struct ctf_dtdef;
> +typedef struct ctf_dtdef ctf_dtdef_t;
> +typedef ctf_dtdef_t * ctf_dtdef_ref;
> +
>   /* CTF string table element (list node).  */
>   
>   typedef struct GTY ((chain_next ("%h.cts_next"))) ctf_string
> @@ -81,8 +85,8 @@ typedef struct GTY (()) ctf_encoding
>   
>   typedef struct GTY (()) ctf_arinfo
>   {
> -  ctf_id_t ctr_contents;	/* Type of array contents.  */
> -  ctf_id_t ctr_index;		/* Type of array index.  */
> +  ctf_dtdef_ref ctr_contents;	/* Type of array contents.  */
> +  ctf_dtdef_ref ctr_index;	/* Type of array index.  */
>     unsigned int ctr_nelems;	/* Number of elements.  */
>   } ctf_arinfo_t;
>   
> @@ -90,14 +94,14 @@ typedef struct GTY (()) ctf_arinfo
>   
>   typedef struct GTY (()) ctf_funcinfo
>   {
> -  ctf_id_t ctc_return;		/* Function return type.  */
> -  unsigned int ctc_argc;	/* Number of typed arguments to function.  */
> -  unsigned int ctc_flags;	/* Function attributes (see below).  */
> +  ctf_dtdef_ref ctc_return;	 /* Function return type.  */
> +  unsigned int ctc_argc;	 /* Number of typed arguments to function.  */
> +  unsigned int ctc_flags;	 /* Function attributes (see below).  */
>   } ctf_funcinfo_t;
>   
>   typedef struct GTY (()) ctf_sliceinfo
>   {
> -  unsigned int cts_type;	/* Reference CTF type.  */
> +  ctf_dtdef_ref cts_type;	/* Reference CTF type.  */
>     unsigned short cts_offset;	/* Offset in bits of the first bit.  */
>     unsigned short cts_bits;	/* Size in bits.  */
>   } ctf_sliceinfo_t;
> @@ -130,7 +134,7 @@ typedef struct GTY (()) ctf_itype
>   typedef struct GTY ((chain_next ("%h.dmd_next"))) ctf_dmdef
>   {
>     const char * dmd_name;	/* Name of this member.  */
> -  ctf_id_t dmd_type;		/* Type of this member (for sou).  */
> +  ctf_dtdef_ref dmd_type;	/* Type of this member (for sou).  */
>     uint32_t dmd_name_offset;	/* Offset of the name in str table.  */
>     uint64_t dmd_offset;		/* Offset of this member in bits (for sou).  */
>     HOST_WIDE_INT dmd_value;	/* Value of this member (for enum).  */
> @@ -143,7 +147,7 @@ typedef struct GTY ((chain_next ("%h.dmd_next"))) ctf_dmdef
>   
>   typedef struct GTY (()) ctf_func_arg
>   {
> -  ctf_id_t farg_type;		  /* Type identifier of the argument.  */
> +  ctf_dtdef_ref farg_type;	  /* Type of the argument.  */
>     const char * farg_name;	  /* Name of the argument.  */
>     uint32_t farg_name_offset;	  /* Offset of the name in str table.  */
>     struct ctf_func_arg * farg_next;/* A list node.  */
> @@ -158,6 +162,7 @@ struct GTY ((for_user)) ctf_dtdef
>     dw_die_ref dtd_key;	      /* Type key for hashing.  */
>     const char * dtd_name;      /* Name associated with definition (if any).  */
>     ctf_id_t dtd_type;	      /* Type identifier for this definition.  */
> +  ctf_dtdef_ref ref_type;     /* Type referred to by this type (if any).  */
>     ctf_itype_t dtd_data;	      /* Type node.  */
>     bool from_global_func; /* Whether this type was added from a global
>   			    function.  */
> @@ -178,7 +183,7 @@ struct GTY ((for_user)) ctf_dtdef
>     } dtd_u;
>   };
>   
> -typedef struct ctf_dtdef ctf_dtdef_t;
> +#define ctf_type_id(dtd) ((uint32_t) dtd->dtd_type)
>   
>   /* Variable definition for CTF generation.  */
>   
> @@ -188,13 +193,11 @@ struct GTY ((for_user)) ctf_dvdef
>     const char * dvd_name;	/* Name associated with variable.  */
>     uint32_t dvd_name_offset;	/* Offset of the name in str table.  */
>     unsigned int dvd_visibility;	/* External visibility.  0=static,1=global.  */
> -  ctf_id_t dvd_type;		/* Type of variable.  */
> +  ctf_dtdef_ref dvd_type;	/* Type of variable.  */
>   };
>   
>   typedef struct ctf_dvdef ctf_dvdef_t;
> -
>   typedef ctf_dvdef_t * ctf_dvdef_ref;
> -typedef ctf_dtdef_t * ctf_dtdef_ref;
>   
>   /* Location information for CTF Types and CTF Variables.  */
>   
> @@ -390,7 +393,7 @@ extern void btf_finalize (void);
>   
>   extern ctf_container_ref ctf_get_tu_ctfc (void);
>   
> -extern bool ctf_type_exists (ctf_container_ref, dw_die_ref, ctf_id_t *);
> +extern bool ctf_type_exists (ctf_container_ref, dw_die_ref, ctf_dtdef_ref *);
>   
>   extern void ctf_add_cuname (ctf_container_ref, const char *);
>   
> @@ -404,41 +407,42 @@ extern bool ctf_dvd_ignore_lookup (const ctf_container_ref ctfc,
>   extern const char * ctf_add_string (ctf_container_ref, const char *,
>   				    uint32_t *, int);
>   
> -extern ctf_id_t ctf_add_reftype (ctf_container_ref, uint32_t, ctf_id_t,
> -				 uint32_t, dw_die_ref);
> -extern ctf_id_t ctf_add_enum (ctf_container_ref, uint32_t, const char *,
> -			      HOST_WIDE_INT, bool, dw_die_ref);
> -extern ctf_id_t ctf_add_slice (ctf_container_ref, uint32_t, ctf_id_t,
> -			       uint32_t, uint32_t, dw_die_ref);
> -extern ctf_id_t ctf_add_float (ctf_container_ref, uint32_t, const char *,
> -			       const ctf_encoding_t *, dw_die_ref);
> -extern ctf_id_t ctf_add_integer (ctf_container_ref, uint32_t, const char *,
> -				 const ctf_encoding_t *, dw_die_ref);
> -extern ctf_id_t ctf_add_unknown (ctf_container_ref, uint32_t, const char *,
> -				 const ctf_encoding_t *, dw_die_ref);
> -extern ctf_id_t ctf_add_pointer (ctf_container_ref, uint32_t, ctf_id_t,
> -				 dw_die_ref);
> -extern ctf_id_t ctf_add_array (ctf_container_ref, uint32_t,
> -			       const ctf_arinfo_t *, dw_die_ref);
> -extern ctf_id_t ctf_add_forward (ctf_container_ref, uint32_t, const char *,
> -				 uint32_t, dw_die_ref);
> -extern ctf_id_t ctf_add_typedef (ctf_container_ref, uint32_t, const char *,
> -				 ctf_id_t, dw_die_ref);
> -extern ctf_id_t ctf_add_function (ctf_container_ref, uint32_t, const char *,
> -				  const ctf_funcinfo_t *, dw_die_ref, bool, int);
> -extern ctf_id_t ctf_add_sou (ctf_container_ref, uint32_t, const char *,
> -			     uint32_t, size_t, dw_die_ref);
> -
> -extern int ctf_add_enumerator (ctf_container_ref, ctf_id_t, const char *,
> +extern ctf_dtdef_ref ctf_add_reftype (ctf_container_ref, uint32_t,
> +				      ctf_dtdef_ref, uint32_t, dw_die_ref);
> +extern ctf_dtdef_ref ctf_add_enum (ctf_container_ref, uint32_t, const char *,
> +				   HOST_WIDE_INT, bool, dw_die_ref);
> +extern ctf_dtdef_ref ctf_add_slice (ctf_container_ref, uint32_t, ctf_dtdef_ref,
> +				    uint32_t, uint32_t, dw_die_ref);
> +extern ctf_dtdef_ref ctf_add_float (ctf_container_ref, uint32_t, const char *,
> +				    const ctf_encoding_t *, dw_die_ref);
> +extern ctf_dtdef_ref ctf_add_integer (ctf_container_ref, uint32_t, const char *,
> +				      const ctf_encoding_t *, dw_die_ref);
> +extern ctf_dtdef_ref ctf_add_unknown (ctf_container_ref, uint32_t, const char *,
> +				      const ctf_encoding_t *, dw_die_ref);
> +extern ctf_dtdef_ref ctf_add_pointer (ctf_container_ref, uint32_t,
> +				      ctf_dtdef_ref, dw_die_ref);
> +extern ctf_dtdef_ref ctf_add_array (ctf_container_ref, uint32_t,
> +				    const ctf_arinfo_t *, dw_die_ref);
> +extern ctf_dtdef_ref ctf_add_forward (ctf_container_ref, uint32_t, const char *,
> +				      uint32_t, dw_die_ref);
> +extern ctf_dtdef_ref ctf_add_typedef (ctf_container_ref, uint32_t, const char *,
> +				      ctf_dtdef_ref, dw_die_ref);
> +extern ctf_dtdef_ref ctf_add_function (ctf_container_ref, uint32_t,
> +				       const char *, const ctf_funcinfo_t *,
> +				       dw_die_ref, bool, int);
> +extern ctf_dtdef_ref ctf_add_sou (ctf_container_ref, uint32_t, const char *,
> +				  uint32_t, size_t, dw_die_ref);
> +
> +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_id_t, uint64_t);
> +				  ctf_dtdef_ref, uint64_t);
>   extern int ctf_add_function_arg (ctf_container_ref, dw_die_ref,
> -				 const char *, ctf_id_t);
> -extern int ctf_add_variable (ctf_container_ref, const char *, ctf_id_t,
> +				 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_id_t ctf_lookup_tree_type (ctf_container_ref, const tree);
> +extern ctf_dtdef_ref ctf_lookup_tree_type (ctf_container_ref, const tree);
>   extern ctf_id_t get_btf_id (ctf_id_t);
>   
>   typedef bool (*funcs_traverse_callback) (ctf_dtdef_ref, void *);
> diff --git a/gcc/ctfout.cc b/gcc/ctfout.cc
> index ee082b5fd016..157afbac4f48 100644
> --- a/gcc/ctfout.cc
> +++ b/gcc/ctfout.cc
> @@ -380,7 +380,8 @@ ctf_asm_type (ctf_dtdef_ref type)
>   static void
>   ctf_asm_slice (ctf_dtdef_ref type)
>   {
> -  dw2_asm_output_data (4, type->dtd_u.dtu_slice.cts_type, "cts_type");
> +  dw2_asm_output_data (4, ctf_type_id (type->dtd_u.dtu_slice.cts_type),
> +		       "cts_type");
>     dw2_asm_output_data (2, type->dtd_u.dtu_slice.cts_offset, "cts_offset");
>     dw2_asm_output_data (2, type->dtd_u.dtu_slice.cts_bits, "cts_bits");
>   }
> @@ -390,8 +391,10 @@ ctf_asm_slice (ctf_dtdef_ref type)
>   static void
>   ctf_asm_array (ctf_dtdef_ref dtd)
>   {
> -  dw2_asm_output_data (4, dtd->dtd_u.dtu_arr.ctr_contents, "cta_contents");
> -  dw2_asm_output_data (4, dtd->dtd_u.dtu_arr.ctr_index, "cta_index");
> +  dw2_asm_output_data (4, ctf_type_id (dtd->dtd_u.dtu_arr.ctr_contents),
> +		       "cta_contents");
> +  dw2_asm_output_data (4, ctf_type_id (dtd->dtd_u.dtu_arr.ctr_index),
> +		       "cta_index");
>     dw2_asm_output_data (4, dtd->dtd_u.dtu_arr.ctr_nelems, "cta_nelems");
>   }
>   
> @@ -403,7 +406,7 @@ ctf_asm_varent (ctf_dvdef_ref var)
>     /* Output the reference to the name in the string table.  */
>     dw2_asm_output_data (4, var->dvd_name_offset, "ctv_name");
>     /* Output the type index.  */
> -  dw2_asm_output_data (4, var->dvd_type, "ctv_typeidx");
> +  dw2_asm_output_data (4, ctf_type_id (var->dvd_type), "ctv_typeidx");
>   }
>   
>   /* Asm'out a member of CTF struct or union, represented by ctf_lmember_t.  */
> @@ -414,7 +417,7 @@ ctf_asm_sou_lmember (ctf_dmdef_t * dmd)
>     dw2_asm_output_data (4, dmd->dmd_name_offset, "ctlm_name");
>     dw2_asm_output_data (4, CTF_OFFSET_TO_LMEMHI (dmd->dmd_offset),
>   		       "ctlm_offsethi");
> -  dw2_asm_output_data (4, dmd->dmd_type, "ctlm_type");
> +  dw2_asm_output_data (4, ctf_type_id (dmd->dmd_type), "ctlm_type");
>     dw2_asm_output_data (4, CTF_OFFSET_TO_LMEMLO (dmd->dmd_offset),
>   		       "ctlm_offsetlo");
>   }
> @@ -426,7 +429,7 @@ ctf_asm_sou_member (ctf_dmdef_t * dmd)
>   {
>     dw2_asm_output_data (4, dmd->dmd_name_offset, "ctm_name");
>     dw2_asm_output_data (4, dmd->dmd_offset, "ctm_offset");
> -  dw2_asm_output_data (4, dmd->dmd_type, "ctm_type");
> +  dw2_asm_output_data (4, ctf_type_id (dmd->dmd_type), "ctm_type");
>   }
>   
>   /* Asm'out an enumerator constant.  */
> @@ -443,7 +446,10 @@ ctf_asm_enum_const (ctf_dmdef_t * dmd)
>   static void
>   ctf_asm_func_arg (ctf_func_arg_t * farg)
>   {
> -  dw2_asm_output_data (4, farg->farg_type, "dtu_argv");
> +  /* farg_type may be NULL, indicating varargs.  */
> +  dw2_asm_output_data (4, farg->farg_type
> +		       ? ctf_type_id (farg->farg_type)
> +		       : 0, "dtu_argv");
>   }
>   
>   /* CTF writeout to asm file.  */
> @@ -537,7 +543,7 @@ output_ctf_obj_info (ctf_container_ref ctfc)
>         var = ctfc->ctfc_gobjts_list[i];
>   
>         /* CTF type ID corresponding to the type of the variable.  */
> -      dw2_asm_output_data (4, var->dvd_type, "objtinfo_var_type");
> +      dw2_asm_output_data (4, ctf_type_id (var->dvd_type), "objtinfo_var_type");
>       }
>   
>   }
> diff --git a/gcc/dwarf2ctf.cc b/gcc/dwarf2ctf.cc
> index 8f9e2fada9e3..a6c8541ef2b4 100644
> --- a/gcc/dwarf2ctf.cc
> +++ b/gcc/dwarf2ctf.cc
> @@ -29,7 +29,7 @@ along with GCC; see the file COPYING3.  If not see
>   
>   /* Forward declarations for some routines defined in this file.  */
>   
> -static ctf_id_t
> +static ctf_dtdef_ref
>   gen_ctf_type (ctf_container_ref, dw_die_ref);
>   
>   /* All the DIE structures we handle come from the DWARF information
> @@ -156,7 +156,7 @@ ctf_get_die_loc_col (dw_die_ref die)
>   
>   /* Generate CTF for the void type.  */
>   
> -static ctf_id_t
> +static ctf_dtdef_ref
>   gen_ctf_void_type (ctf_container_ref ctfc)
>   {
>     ctf_encoding_t ctf_encoding = {0, 0, 0};
> @@ -174,10 +174,10 @@ gen_ctf_void_type (ctf_container_ref ctfc)
>   
>   /* Generate CTF type of unknown kind.  */
>   
> -static ctf_id_t
> +static ctf_dtdef_ref
>   gen_ctf_unknown_type (ctf_container_ref ctfc)
>   {
> -  ctf_id_t unknown_type_id;
> +  ctf_dtdef_ref dtd;
>   
>     /* In CTF, the unknown type is encoded as a 0 byte sized type with kind
>        CTF_K_UNKNOWN.  Create an encoding object merely to reuse the underlying
> @@ -187,11 +187,11 @@ gen_ctf_unknown_type (ctf_container_ref ctfc)
>   
>     gcc_assert (ctf_unknown_die != NULL);
>     /* Type de-duplication.  */
> -  if (!ctf_type_exists (ctfc, ctf_unknown_die, &unknown_type_id))
> -    unknown_type_id = ctf_add_unknown (ctfc, CTF_ADD_ROOT, "unknown",
> -				       &ctf_encoding, ctf_unknown_die);
> +  if (!ctf_type_exists (ctfc, ctf_unknown_die, &dtd))
> +    dtd = ctf_add_unknown (ctfc, CTF_ADD_ROOT, "unknown",
> +			   &ctf_encoding, ctf_unknown_die);
>   
> -  return unknown_type_id;
> +  return dtd;
>   }
>   
>   /* Sizes of entities can be given in bytes or bits.  This function
> @@ -217,10 +217,10 @@ ctf_die_bitsize (dw_die_ref die)
>      Important: the caller of this API must make sure that duplicate types are
>      not added.  */
>   
> -static ctf_id_t
> +static ctf_dtdef_ref
>   gen_ctf_base_type (ctf_container_ref ctfc, dw_die_ref type)
>   {
> -  ctf_id_t type_id = CTF_NULL_TYPEID;
> +  ctf_dtdef_ref dtd = NULL;
>   
>     ctf_encoding_t ctf_encoding = {0, 0, 0};
>   
> @@ -236,8 +236,8 @@ gen_ctf_base_type (ctf_container_ref ctfc, dw_die_ref type)
>         ctf_encoding.cte_bits = 0;
>   
>         gcc_assert (name_string);
> -      type_id = ctf_add_integer (ctfc, CTF_ADD_ROOT, name_string,
> -				 &ctf_encoding, type);
> +      dtd = ctf_add_integer (ctfc, CTF_ADD_ROOT, name_string,
> +			     &ctf_encoding, type);
>   
>         break;
>       case DW_ATE_boolean:
> @@ -246,8 +246,8 @@ gen_ctf_base_type (ctf_container_ref ctfc, dw_die_ref type)
>         ctf_encoding.cte_bits = bit_size;
>   
>         gcc_assert (name_string);
> -      type_id = ctf_add_integer (ctfc, CTF_ADD_ROOT, name_string,
> -				 &ctf_encoding, type);
> +      dtd = ctf_add_integer (ctfc, CTF_ADD_ROOT, name_string,
> +			     &ctf_encoding, type);
>         break;
>       case DW_ATE_float:
>         {
> @@ -269,7 +269,7 @@ gen_ctf_base_type (ctf_container_ref ctfc, dw_die_ref type)
>   	  break;
>   
>   	ctf_encoding.cte_bits = bit_size;
> -	type_id = ctf_add_float (ctfc, CTF_ADD_ROOT, name_string,
> +	dtd = ctf_add_float (ctfc, CTF_ADD_ROOT, name_string,
>   				 &ctf_encoding, type);
>   
>   	break;
> @@ -291,7 +291,7 @@ gen_ctf_base_type (ctf_container_ref ctfc, dw_die_ref type)
>   	ctf_encoding.cte_format |= CTF_INT_SIGNED;
>   
>         ctf_encoding.cte_bits = bit_size;
> -      type_id = ctf_add_integer (ctfc, CTF_ADD_ROOT, name_string,
> +      dtd = ctf_add_integer (ctfc, CTF_ADD_ROOT, name_string,
>   				 &ctf_encoding, type);
>         break;
>   
> @@ -315,7 +315,7 @@ gen_ctf_base_type (ctf_container_ref ctfc, dw_die_ref type)
>   	  break;
>   
>   	ctf_encoding.cte_bits = bit_size;
> -	type_id = ctf_add_float (ctfc, CTF_ADD_ROOT, name_string,
> +	dtd = ctf_add_float (ctfc, CTF_ADD_ROOT, name_string,
>   				 &ctf_encoding, type);
>   	break;
>         }
> @@ -324,41 +324,39 @@ gen_ctf_base_type (ctf_container_ref ctfc, dw_die_ref type)
>         break;
>       }
>   
> -  return type_id;
> +  return dtd;
>   }
>   
>   /* Generate CTF for a pointer type.  */
>   
> -static ctf_id_t
> +static ctf_dtdef_ref
>   gen_ctf_pointer_type (ctf_container_ref ctfc, dw_die_ref ptr_type)
>   {
> -  ctf_id_t type_id = CTF_NULL_TYPEID;
> -  ctf_id_t ptr_type_id = CTF_NULL_TYPEID;
> +  ctf_dtdef_ref pointed_dtd, pointer_dtd;
>     dw_die_ref pointed_type_die = ctf_get_AT_type (ptr_type);
>   
> -  type_id = gen_ctf_type (ctfc, pointed_type_die);
> +  pointed_dtd = gen_ctf_type (ctfc, pointed_type_die);
>   
>     /* 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
>        the gen_ctf_type call above.  */
> -  if (ctf_type_exists (ctfc, ptr_type, &ptr_type_id))
> -    return ptr_type_id;
> +  if (!ctf_type_exists (ctfc, ptr_type, &pointer_dtd))
> +    pointer_dtd = ctf_add_pointer (ctfc, CTF_ADD_ROOT, pointed_dtd, ptr_type);
>   
> -  ptr_type_id = ctf_add_pointer (ctfc, CTF_ADD_ROOT, type_id, ptr_type);
> -  return ptr_type_id;
> +  return pointer_dtd;
>   }
>   
>   /* Recursively generate CTF for array dimensions starting at DIE C (of type
>      DW_TAG_subrange_type) until DIE LAST (of type DW_TAG_subrange_type) is
>      reached.  ARRAY_ELEMS_TYPE_ID is base type for the array.  */
>   

This function-level comment needs updating. "ARRAY_ELEMS_TYPE_ID is ..." 
--> say, "ARRAY_ELEMS_TYPE is the CTF type for the type of the array 
elements.".

> -static ctf_id_t
> -gen_ctf_subrange_type (ctf_container_ref ctfc, ctf_id_t array_elems_type_id,
> +static ctf_dtdef_ref
> +gen_ctf_subrange_type (ctf_container_ref ctfc, ctf_dtdef_ref array_elems_type,
>   		       dw_die_ref c, dw_die_ref last)
>   {
>     ctf_arinfo_t arinfo;
> -  ctf_id_t array_node_type_id = CTF_NULL_TYPEID;
> +  ctf_dtdef_ref array_dtd;
>   
>     dw_attr_node *upper_bound_at;
>     dw_die_ref array_index_type;
> @@ -398,30 +396,29 @@ gen_ctf_subrange_type (ctf_container_ref ctfc, ctf_id_t array_elems_type_id,
>     arinfo.ctr_index = gen_ctf_type (ctfc, array_index_type);
>   
>     if (c == last)
> -    arinfo.ctr_contents = array_elems_type_id;
> +    arinfo.ctr_contents = array_elems_type;
>     else
> -    arinfo.ctr_contents = gen_ctf_subrange_type (ctfc, array_elems_type_id,
> +    arinfo.ctr_contents = gen_ctf_subrange_type (ctfc, array_elems_type,
>   						 dw_get_die_sib (c), last);
>   
> -  if (!ctf_type_exists (ctfc, c, &array_node_type_id))
> -    array_node_type_id = ctf_add_array (ctfc, CTF_ADD_ROOT, &arinfo, c);
> +  if (!ctf_type_exists (ctfc, c, &array_dtd))
> +    array_dtd = ctf_add_array (ctfc, CTF_ADD_ROOT, &arinfo, c);
>   
> -  return array_node_type_id;
> +  return array_dtd;
>   }
>   
>   /* Generate CTF for an ARRAY_TYPE.  */
>   
> -static ctf_id_t
> +static ctf_dtdef_ref
>   gen_ctf_array_type (ctf_container_ref ctfc,
>   		    dw_die_ref array_type)
>   {
>     dw_die_ref first, last, array_elems_type;
> -  ctf_id_t array_elems_type_id = CTF_NULL_TYPEID;
> -  ctf_id_t array_type_id = CTF_NULL_TYPEID;
> +  ctf_dtdef_ref array_dtd, elem_dtd;
>   
>     int vector_type_p = get_AT_flag (array_type, DW_AT_GNU_vector);
>     if (vector_type_p)
> -    return array_elems_type_id;
> +    return NULL;
>   
>     /* Find the first and last array dimension DIEs.  */
>     last = dw_get_die_child (array_type);
> @@ -429,41 +426,36 @@ gen_ctf_array_type (ctf_container_ref ctfc,
>   
>     /* Type de-duplication.
>        Consult the ctfc_types before adding CTF type for the first dimension.  */
> -  if (!ctf_type_exists (ctfc, first, &array_type_id))
> +  if (!ctf_type_exists (ctfc, first, &array_dtd))
>       {
>         array_elems_type = ctf_get_AT_type (array_type);
>         /* First, register the type of the array elements if needed.  */
> -      array_elems_type_id = gen_ctf_type (ctfc, array_elems_type);
> +      elem_dtd = gen_ctf_type (ctfc, array_elems_type);
>   
> -      array_type_id = gen_ctf_subrange_type (ctfc, array_elems_type_id, first,
> -					     last);
> +      array_dtd = gen_ctf_subrange_type (ctfc, elem_dtd, first, last);
>       }
>   
> -  return array_type_id;
> +  return array_dtd;
>   }
>   
>   /* Generate CTF for a typedef.  */
>   
> -static ctf_id_t
> +static ctf_dtdef_ref
>   gen_ctf_typedef (ctf_container_ref ctfc, dw_die_ref tdef)
>   {
> -  ctf_id_t tdef_type_id, tid;
> +  ctf_dtdef_ref tdef_dtd, dtd;
>     const char *tdef_name = get_AT_string (tdef, DW_AT_name);
>     dw_die_ref tdef_type = ctf_get_AT_type (tdef);
>   
> -  tid = gen_ctf_type (ctfc, tdef_type);
> +  dtd = gen_ctf_type (ctfc, tdef_type);
>   
>     /* Type de-duplication.
>        This is necessary because the ctf for the typedef may have been already
>        added due to the gen_ctf_type call above.  */
> -  if (!ctf_type_exists (ctfc, tdef, &tdef_type_id))
> -  {
> -    tdef_type_id = ctf_add_typedef (ctfc, CTF_ADD_ROOT,
> -				    tdef_name,
> -				    tid,
> -				    tdef);
> -  }
> -  return tdef_type_id;
> +  if (!ctf_type_exists (ctfc, tdef, &tdef_dtd))
> +    tdef_dtd = ctf_add_typedef (ctfc, CTF_ADD_ROOT, tdef_name, dtd, tdef);
> +
> +  return tdef_dtd;
>   }
>   
>   /* Generate CTF for a type modifier.
> @@ -475,11 +467,11 @@ gen_ctf_typedef (ctf_container_ref ctfc, dw_die_ref tdef)
>      For all other cases, this function returns a CTF_NULL_TYPEID;
>   */
>   
> -static ctf_id_t
> +static ctf_dtdef_ref
>   gen_ctf_modifier_type (ctf_container_ref ctfc, dw_die_ref modifier)

The function-level comment of this one needs updating.

Also a stub in this function (not present in the diff below) has a 
"return CTF_NULL_TYPEID;".  Changing it to return NULL; is better.

>   {
>     uint32_t kind = CTF_K_MAX;
> -  ctf_id_t modifier_type_id, qual_type_id;
> +  ctf_dtdef_ref dtd, modifier_dtd;
>     dw_die_ref qual_type = ctf_get_AT_type (modifier);
>   
>     switch (dw_get_die_tag (modifier))
> @@ -493,33 +485,31 @@ gen_ctf_modifier_type (ctf_container_ref ctfc, dw_die_ref modifier)
>       }
>   
>     /* Register the type for which this modifier applies.  */
> -  qual_type_id = gen_ctf_type (ctfc, qual_type);
> +  dtd = gen_ctf_type (ctfc, qual_type);
>   
>     /* Skip generating a CTF modifier record for _Atomic as there is no
>        representation for it.  */
>     if (dw_get_die_tag (modifier) == DW_TAG_atomic_type)
> -    return qual_type_id;
> +    return dtd;
>   
>     gcc_assert (kind != CTF_K_MAX);
>     /* Now register the modifier itself.  */
> -  if (!ctf_type_exists (ctfc, modifier, &modifier_type_id))
> -    modifier_type_id = ctf_add_reftype (ctfc, CTF_ADD_ROOT,
> -					qual_type_id, kind,
> -					modifier);
> +  if (!ctf_type_exists (ctfc, modifier, &modifier_dtd))
> +    modifier_dtd = ctf_add_reftype (ctfc, CTF_ADD_ROOT, dtd, kind, modifier);
>   
> -  return modifier_type_id;
> +  return modifier_dtd;
>   }
>   
>   /* Generate CTF for a struct type.  */
>   
> -static ctf_id_t
> +static ctf_dtdef_ref
>   gen_ctf_sou_type (ctf_container_ref ctfc, dw_die_ref sou, uint32_t kind)
>   {
>     uint32_t bit_size = ctf_die_bitsize (sou);
>     int declaration_p = get_AT_flag (sou, DW_AT_declaration);
>     const char *sou_name = get_AT_string (sou, DW_AT_name);
>   
> -  ctf_id_t sou_type_id;
> +  ctf_dtdef_ref sou_dtd;
>   
>     /* An incomplete structure or union type is represented in DWARF by
>        a structure or union DIE that does not have a size attribute and
> @@ -531,10 +521,10 @@ gen_ctf_sou_type (ctf_container_ref ctfc, dw_die_ref sou, uint32_t kind)
>   
>     /* This is a complete struct or union type.  Generate a CTF type for
>        it if it doesn't exist already.  */
> -  if (!ctf_type_exists (ctfc, sou, &sou_type_id))
> -    sou_type_id = ctf_add_sou (ctfc, CTF_ADD_ROOT,
> -			       sou_name, kind, bit_size / 8,
> -			       sou);
> +  if (!ctf_type_exists (ctfc, sou, &sou_dtd))
> +    sou_dtd = ctf_add_sou (ctfc, CTF_ADD_ROOT,
> +			   sou_name, kind, bit_size / 8,
> +			   sou);
>   
>     /* Now process the struct members.  */
>     {
> @@ -547,7 +537,7 @@ gen_ctf_sou_type (ctf_container_ref ctfc, dw_die_ref sou, uint32_t kind)
>   	  const char *field_name;
>   	  dw_die_ref field_type;
>   	  HOST_WIDE_INT field_location;
> -	  ctf_id_t field_type_id;
> +	  ctf_dtdef_ref field_dtd;
>   
>   	  c = dw_get_die_sib (c);
>   
> @@ -556,7 +546,7 @@ gen_ctf_sou_type (ctf_container_ref ctfc, dw_die_ref sou, uint32_t kind)
>   	  field_location = ctf_get_AT_data_member_location (c);
>   
>   	  /* Generate the field type.  */
> -	  field_type_id = gen_ctf_type (ctfc, field_type);
> +	  field_dtd = gen_ctf_type (ctfc, field_type);
>   
>   	  /* If this is a bit-field, then wrap the field type
>   	     generated above with a CTF slice.  */
> @@ -610,29 +600,29 @@ gen_ctf_sou_type (ctf_container_ref ctfc, dw_die_ref sou, uint32_t kind)
>   		 surely something to look at for the next format version bump
>   		 for CTF.  */
>   	      if (bitsize <= 255 && (bitpos - field_location) <= 255)
> -		field_type_id = ctf_add_slice (ctfc, CTF_ADD_NONROOT,
> -					       field_type_id,
> +		field_dtd = ctf_add_slice (ctfc, CTF_ADD_NONROOT,
> +					       field_dtd,
>   					       bitpos - field_location,
>   					       bitsize, c);
>   	      else
> -		field_type_id = gen_ctf_unknown_type (ctfc);
> +		field_dtd = gen_ctf_unknown_type (ctfc);
>   	    }
>   
>   	  /* Add the field type to the struct or union type.  */
>   	  ctf_add_member_offset (ctfc, sou,
>   				 field_name,
> -				 field_type_id,
> +				 field_dtd,
>   				 field_location);
>   	}
>         while (c != dw_get_die_child (sou));
>     }
>   
> -  return sou_type_id;
> +  return sou_dtd;
>   }
>   
>   /* Generate CTF for a function type.  */
>   
> -static ctf_id_t
> +static ctf_dtdef_ref
>   gen_ctf_function_type (ctf_container_ref ctfc, dw_die_ref function,
>   		       bool from_global_func)
>   {
> @@ -643,17 +633,16 @@ gen_ctf_function_type (ctf_container_ref ctfc, dw_die_ref function,
>     uint32_t num_args = 0;
>     int linkage = get_AT_flag (function, DW_AT_external);
>   
> -  ctf_id_t return_type_id;
> -  ctf_id_t function_type_id;
> +  ctf_dtdef_ref return_dtd, function_dtd;
>   
>     /* First, add the return type.  */
> -  return_type_id = gen_ctf_type (ctfc, return_type);
> -  func_info.ctc_return = return_type_id;
> +  return_dtd = gen_ctf_type (ctfc, return_type);
> +  func_info.ctc_return = return_dtd;
>   
>     /* Type de-duplication.
>        Consult the ctfc_types hash before adding the CTF function type.  */
> -  if (ctf_type_exists (ctfc, function, &function_type_id))
> -    return function_type_id;
> +  if (ctf_type_exists (ctfc, function, &function_dtd))
> +    return function_dtd;
>   
>     /* Do a first pass on the formals to determine the number of
>        arguments, and whether the function type gets a varargs.  */
> @@ -681,12 +670,12 @@ gen_ctf_function_type (ctf_container_ref ctfc, dw_die_ref function,
>     func_info.ctc_argc = num_args;
>   
>     /* Type de-duplication has already been performed by now.  */
> -  function_type_id = ctf_add_function (ctfc, CTF_ADD_ROOT,
> -				       function_name,
> -				       (const ctf_funcinfo_t *)&func_info,
> -				       function,
> -				       from_global_func,
> -                                       linkage);
> +  function_dtd = ctf_add_function (ctfc, CTF_ADD_ROOT,
> +				   function_name,
> +				   (const ctf_funcinfo_t *)&func_info,
> +				   function,
> +				   from_global_func,
> +				   linkage);
>   
>     /* Second pass on formals: generate the CTF types corresponding to
>        them and add them as CTF function args.  */
> @@ -694,7 +683,7 @@ gen_ctf_function_type (ctf_container_ref ctfc, dw_die_ref function,
>       dw_die_ref c;
>       unsigned int i = 0;
>       const char *arg_name;
> -    ctf_id_t arg_type;
> +    ctf_dtdef_ref arg_type;
>   
>       c = dw_get_die_child (function);
>       if (c)
> @@ -706,7 +695,7 @@ 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, "", 0);
> +	      ctf_add_function_arg (ctfc, function, "", NULL);
>   	    }
>   	  else if (dw_get_die_tag (c) == DW_TAG_formal_parameter)
>   	    {
> @@ -723,12 +712,12 @@ gen_ctf_function_type (ctf_container_ref ctfc, dw_die_ref function,
>         while (c != dw_get_die_child (function));
>     }
>   
> -  return function_type_id;
> +  return function_dtd;
>   }
>   
>   /* Generate CTF for an enumeration type.  */
>   
> -static ctf_id_t
> +static ctf_dtdef_ref
>   gen_ctf_enumeration_type (ctf_container_ref ctfc, dw_die_ref enumeration)
>   {
>     const char *enum_name = get_AT_string (enumeration, DW_AT_name);
> @@ -736,7 +725,7 @@ gen_ctf_enumeration_type (ctf_container_ref ctfc, dw_die_ref enumeration)
>     unsigned int signedness = get_AT_unsigned (enumeration, DW_AT_encoding);
>     int declaration_p = get_AT_flag (enumeration, DW_AT_declaration);
>   
> -  ctf_id_t enumeration_type_id;
> +  ctf_dtdef_ref enum_dtd;
>   
>     /* If this is an incomplete enum, generate a CTF forward for it and
>        be done.  */
> @@ -756,10 +745,10 @@ gen_ctf_enumeration_type (ctf_container_ref ctfc, dw_die_ref enumeration)
>       }
>   
>     /* Generate a CTF type for the enumeration.  */
> -  enumeration_type_id = ctf_add_enum (ctfc, CTF_ADD_ROOT,
> -				      enum_name, bit_size / 8,
> -				      (signedness == DW_ATE_unsigned),
> -				      enumeration);
> +  enum_dtd = ctf_add_enum (ctfc, CTF_ADD_ROOT,
> +			   enum_name, bit_size / 8,
> +			   (signedness == DW_ATE_unsigned),
> +			   enumeration);
>   
>     /* Process the enumerators.  */
>     {
> @@ -787,13 +776,13 @@ gen_ctf_enumeration_type (ctf_container_ref ctfc, dw_die_ref enumeration)
>   	  else
>   	    value_wide_int = AT_int (enumerator_value);
>   
> -	  ctf_add_enumerator (ctfc, enumeration_type_id,
> +	  ctf_add_enumerator (ctfc, enum_dtd,
>   			      enumerator_name, value_wide_int, enumeration);
>   	}
>         while (c != dw_get_die_child (enumeration));
>     }
>   
> -  return enumeration_type_id;
> +  return enum_dtd;
>   }
>   
>   /* Add a CTF variable record for the given input DWARF DIE.  */
> @@ -804,7 +793,7 @@ gen_ctf_variable (ctf_container_ref ctfc, dw_die_ref die)
>     const char *var_name = get_AT_string (die, DW_AT_name);
>     dw_die_ref var_type = ctf_get_AT_type (die);
>     unsigned int external_vis = get_AT_flag (die, DW_AT_external);
> -  ctf_id_t var_type_id;
> +  ctf_dtdef_ref var_dtd;
>   
>     /* Avoid duplicates.  */
>     if (ctf_dvd_lookup (ctfc, die))
> @@ -822,11 +811,10 @@ gen_ctf_variable (ctf_container_ref ctfc, dw_die_ref die)
>     dw_die_ref decl = get_AT_ref (die, DW_AT_specification);
>   
>     /* Add the type of the variable.  */
> -  var_type_id = gen_ctf_type (ctfc, var_type);
> +  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_type_id, die, external_vis,
> -			   decl);
> +  (void) 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].  */
> @@ -837,10 +825,10 @@ gen_ctf_variable (ctf_container_ref ctfc, dw_die_ref die)
>   static void
>   gen_ctf_function (ctf_container_ref ctfc, dw_die_ref die)
>   {
> -  ctf_id_t function_type_id;
> +  ctf_dtdef_ref function_dtd;
>     /* Type de-duplication.
>        Consult the ctfc_types hash before adding the CTF function type.  */
> -  if (ctf_type_exists (ctfc, die, &function_type_id))
> +  if (ctf_type_exists (ctfc, die, &function_dtd))
>       return;
>   
>     /* Add the type of the function and update the global functions
> @@ -859,41 +847,41 @@ gen_ctf_function (ctf_container_ref ctfc, dw_die_ref die)
>      If the given DIE is not recognized as a type, then this function
>      returns CTF_NULL_TYPEID.  */
>   

Update function-level comment: returns NULL.

> -static ctf_id_t
> +static ctf_dtdef_ref
>   gen_ctf_type (ctf_container_ref ctfc, dw_die_ref die)
>   {
> -  ctf_id_t type_id;
> +  ctf_dtdef_ref dtd = NULL;
>     int unrecog_die = false;
>   
> -  if (ctf_type_exists (ctfc, die, &type_id))
> -    return type_id;
> +  if (ctf_type_exists (ctfc, die, &dtd))
> +    return dtd;
>   
>     switch (dw_get_die_tag (die))
>       {
>       case DW_TAG_base_type:
> -      type_id = gen_ctf_base_type (ctfc, die);
> +      dtd = gen_ctf_base_type (ctfc, die);
>         break;
>       case DW_TAG_pointer_type:
> -      type_id = gen_ctf_pointer_type (ctfc, die);
> +      dtd = gen_ctf_pointer_type (ctfc, die);
>         break;
>       case DW_TAG_typedef:
> -      type_id = gen_ctf_typedef (ctfc, die);
> +      dtd = gen_ctf_typedef (ctfc, die);
>         break;
>       case DW_TAG_array_type:
> -      type_id = gen_ctf_array_type (ctfc, die);
> +      dtd = gen_ctf_array_type (ctfc, die);
>         break;
>       case DW_TAG_structure_type:
> -      type_id = gen_ctf_sou_type (ctfc, die, CTF_K_STRUCT);
> +      dtd = gen_ctf_sou_type (ctfc, die, CTF_K_STRUCT);
>         break;
>       case DW_TAG_union_type:
> -      type_id = gen_ctf_sou_type (ctfc, die, CTF_K_UNION);
> +      dtd = gen_ctf_sou_type (ctfc, die, CTF_K_UNION);
>         break;
>       case DW_TAG_subroutine_type:
> -      type_id = gen_ctf_function_type (ctfc, die,
> -				       false /* from_global_func */);
> +      dtd = gen_ctf_function_type (ctfc, die,
> +				   false /* from_global_func */);
>         break;
>       case DW_TAG_enumeration_type:
> -      type_id = gen_ctf_enumeration_type (ctfc, die);
> +      dtd = gen_ctf_enumeration_type (ctfc, die);
>         break;
>       case DW_TAG_atomic_type:
>         /* FALLTHROUGH */
> @@ -902,35 +890,35 @@ gen_ctf_type (ctf_container_ref ctfc, dw_die_ref die)
>       case DW_TAG_restrict_type:
>         /* FALLTHROUGH */
>       case DW_TAG_volatile_type:
> -      type_id = gen_ctf_modifier_type (ctfc, die);
> +      dtd = gen_ctf_modifier_type (ctfc, die);
>         break;
>       case DW_TAG_unspecified_type:
>         {
>   	const char *name = get_AT_string (die, DW_AT_name);
>   
>   	if (name && strcmp (name, "void") == 0)
> -	  type_id = gen_ctf_void_type (ctfc);
> +	  dtd = gen_ctf_void_type (ctfc);
>   	else
> -	  type_id = CTF_NULL_TYPEID;
> +	  dtd = NULL;
>   
>   	break;
>         }
>       case DW_TAG_reference_type:
> -      type_id = CTF_NULL_TYPEID;
> +      dtd = NULL;
>         break;
>       default:
>         /* Unrecognized DIE.  */
>         unrecog_die = true;
> -      type_id = CTF_NULL_TYPEID;
> +      dtd = NULL;
>         break;
>       }
>   
>     /* For all types unrepresented in CTF, use an explicit CTF type of kind
>        CTF_K_UNKNOWN.  */
> -  if ((type_id == CTF_NULL_TYPEID) && (!unrecog_die))
> -    type_id = gen_ctf_unknown_type (ctfc);
> +  if ((dtd == NULL) && (!unrecog_die))
> +    dtd = gen_ctf_unknown_type (ctfc);
>   
> -  return type_id;
> +  return dtd;
>   }
>   
>   bool
> @@ -951,7 +939,7 @@ ctf_do_die (dw_die_ref die)
>         return false;
>       }
>     else
> -    return gen_ctf_type (tu_ctfc, die) == CTF_NULL_TYPEID;
> +    return (gen_ctf_type (tu_ctfc, die) == NULL);
>   }
>   
>   /* Initialize CTF subsystem for CTF debug info generation.  */
> diff --git a/include/btf.h b/include/btf.h
> index 24b38bace178..3f45ffb0b6bb 100644
> --- a/include/btf.h
> +++ b/include/btf.h
> @@ -56,6 +56,11 @@ struct btf_header
>   /* Maximum number of struct, union, enum members or func args.  */
>   #define BTF_MAX_VLEN	0xffff
>   
> +/* Type ID 0 represents the void type.  */
> +#define BTF_VOID_TYPEID 0
> +/* Initial type ID for regular types.  */
> +#define BTF_INIT_TYPEID 1
> +
>   struct btf_type
>   {
>     uint32_t name_off; 	/* Offset in string section of type name.  */


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

* Re: [PATCH v3 3/6] btf: refactor and simplify implementation
  2024-05-30 21:32 ` [PATCH v3 3/6] btf: refactor and simplify implementation David Faust
@ 2024-06-05  7:41   ` Indu Bhagat
  0 siblings, 0 replies; 16+ messages in thread
From: Indu Bhagat @ 2024-06-05  7:41 UTC (permalink / raw)
  To: David Faust, gcc-patches; +Cc: jose.marchesi, cupertino.miranda

On 5/30/24 14:32, David Faust wrote:
> This patch heavily refactors btfout.cc to take advantage of the
> structural changes in the prior commits.
> 
> Now that inter-type references are internally stored as simply pointers,
> all the painful, brittle, confusing infrastructure that was used in the
> process of converting CTF type IDs to BTF type IDs can be thrown out.
> This greatly simplifies the entire process of converting from CTF to
> BTF, making the code cleaner, easier to read, and easier to maintain.
> 
> In addition, we no longer need to worry about destructive changes in
> internal data structures used commonly by CTF and BTF, which allows
> deleting several ancillary data structures previously used in btfout.cc.
> 
> This is nearly transparent, but a few improvements have also been made:
> 
>   1) BTF_KIND_FUNC records are now _always_ constructed at early_finish,
>      allowing us to construct records even for functions which are later
>      inlined by optimizations. DATASEC entries for functions are only
>      constructed at late_finish, to avoid incorrectly generating entries
>      for functions which get inlined.
> 

I find this aspect of the BTF specification "interesting": Neither 
BTF_KIND_VAR record, nor DATASEC entry must be emitted for optimized 
away variables.  But for functions, OTH,  BTF_KIND_FUNC (and 
BTF_KIND_FUNC_PROTO (albeit anon) ?) are expected for inlined functions, 
but no DATASEC.

Anayway, that aside, I have one comment below.  Other than that, this 
looks OK to me.

Thanks

>   2) BTF_KIND_VAR records and DATASEC entries for them are now always
>      constructed at (late) finish, which avoids cases where we could
>      incorrectly create records for variables which were completely
>      optimized away.  This fixes PR debug/113566 for non-LTO builds.
>      In LTO builds, BTF must be emitted at early_finish, so some VAR
>      records may be emitted for variables which are later optimized away.
> 
>   3) Some additional assembler comments have been added with more
>      information for debugging.
> 
> gcc/
> 	* btfout.cc (struct btf_datasec_entry): New.
> 	(struct btf_datasec): Add `id' member.  Change `entries' to use
> 	new struct btf_datasec_entry.
> 	(func_map): New hash_map.
> 	(max_translated_id): New.
> 	(btf_var_ids, btf_id_map, holes, voids, num_vars_added)
> 	(num_types_added, num_types_created): Delete.
> 	(btf_absolute_var_id, btf_relative_var_id, btf_absolute_func_id)
> 	(btf_relative_func_id, btf_absolute_datasec_id, init_btf_id_map)
> 	(get_btf_id, set_btf_id, btf_emit_id_p): Delete.
> 	(btf_removed_type_p): Delete.
> 	(btf_dtd_kind, btf_emit_type_p): New helpers.
> 	(btf_fwd_to_enum_p, btf_calc_num_vbytes): Use them.
> 	(btf_collect_datasec): Delete.
> 	(btf_dtd_postprocess_cb, btf_dvd_emit_preprocess_cb)
> 	(btf_dtd_emit_preprocess_cb, btf_emit_preprocess): Delete.
> 	(btf_dmd_representable_bitfield_p): Adapt to type reference changes
> 	and delete now-unused ctfc argument.
> 	(btf_asm_datasec_type_ref): Delete.
> 	(btf_asm_type_ref): Adapt to type reference changes, simplify.
> 	(btf_asm_type): Likewise. Mark struct/union types with bitfield
> 	members.
> 	(btf_asm_array): Adapt to data structure changes.
> 	(btf_asm_varent): Likewise.
> 	(btf_asm_sou_member): Likewise. Ensure non-bitfield members are
> 	correctly re-encoded if struct or union contains any bitfield.
> 	(btf_asm_func_arg, btf_asm_func_type, btf_asm_datasec_entry)
> 	(btf_asm_datasec_type): Adapt to data structure changes.
> 	(output_btf_header): Adapt to other changes, simplify type
> 	length calculation, add info to assembler comments.
> 	(output_btf_vars): Adapt to other changes.
> 	(output_btf_strs): Fix overlong lines.
> 	(output_asm_btf_sou_fields, output_asm_btf_enum_list)
> 	(output_asm_btf_func_args_list, output_asm_btf_vlen_bytes)
> 	(output_asm_btf_type, output_btf_types, output_btf_func_types)
> 	(output_btf_datasec_types): Adapt to other changes.
> 	(btf_init_postprocess): Delete.
> 	(btf_output): Change to only perform output.
> 	(btf_early_add_const_void, btf_early_add_func_records): New.
> 	(btf_early_finish): Use them here. New.
> 	(btf_datasec_push_entry): Adapt to data structure changes.
> 	(btf_datasec_add_func, btf_datasec_add_var): New.
> 	(btf_late_add_func_datasec_entries): New.
> 	(btf_emit_variable_p): New helper.
> 	(btf_late_add_vars): Use it here. New.
> 	(btf_type_list_cb, btf_late_collect_translated_types): New.
> 	(btf_late_assign_func_ids, btf_late_assign_var_ids)
> 	(btf_late_assign_datasec_ids): New.
> 	(btf_finish): Remove unused argument. Call new btf_late*
> 	functions and btf_output.
> 	(btf_finalize): Adapt to data structure changes.
> 	* ctfc.h (struct ctf_dtdef): Convert existing boolean flags to
> 	BOOL_BITFIELD and reorder.
> 	(struct ctf_dvdef): Add dvd_id member.
> 	(btf_finish): Remove argument from prototype.
> 	(get_btf_id): Delete prototype.
> 	(funcs_traverse_callback, traverse_btf_func_types): Add an
> 	explanatory comment.
> 	* dwarf2ctf.cc (ctf_debug_finish): Remove unused argument.
> 	* dwarf2ctf.h: Analogous change.
> 	* dwarf2out.cc: Likewise.
> ---
>   gcc/btfout.cc    | 1254 +++++++++++++++++++---------------------------
>   gcc/ctfc.h       |   17 +-
>   gcc/dwarf2ctf.cc |    6 +-
>   gcc/dwarf2ctf.h  |    2 +-
>   gcc/dwarf2out.cc |    2 +-
>   5 files changed, 539 insertions(+), 742 deletions(-)
> 
> diff --git a/gcc/btfout.cc b/gcc/btfout.cc
> index 40e8d8c5c01b..32fda14f704b 100644
> --- a/gcc/btfout.cc
> +++ b/gcc/btfout.cc
> @@ -63,53 +63,44 @@ static char btf_info_section_label[MAX_BTF_LABEL_BYTES];
>   
>   #define BTF_INVALID_TYPEID 0xFFFFFFFF
>   
> -/* Mapping of CTF variables to the IDs they will be assigned when they are
> -   converted to BTF_KIND_VAR type records. Strictly accounts for the index
> -   from the start of the variable type entries, does not include the number
> -   of types emitted prior to the variable records.  */
> -static GTY (()) hash_map <ctf_dvdef_ref, unsigned> *btf_var_ids;
> -
> -/* Mapping of type IDs from original CTF ID to BTF ID. Types do not map
> -   1-to-1 from CTF to BTF. To avoid polluting the CTF container when updating
> -   type references-by-ID, we use this map instead.  */
> -static ctf_id_t * btf_id_map = NULL;
> -
> -/* Information for creating the BTF_KIND_DATASEC records.  */
> +/* Internal representation of an entry in a BTF_KIND_DATASEC record.  */
> +struct btf_datasec_entry
> +{
> +  union {
> +    ctf_dvdef_ref dvd; /* Reference to the underlying variable represented.  */
> +    ctf_dtdef_ref dtd; /* Reference to the underlying type represented.  */
> +  };
> +  bool is_var;	       /* True iff this entry represents a variable.  */
> +  uint32_t size;       /* Size of variable or function, in bytes.
> +			  For functions, always zero at compile time.  */
> +};
> +
> +/* Internal representation of a BTF_KIND_DATASEC record.  */
>   typedef struct btf_datasec
>   {
> -  const char *name;                    /* Section name, e.g. ".bss".  */
> -  uint32_t name_offset;                /* Offset to name in string table.  */
> -  vec<struct btf_var_secinfo> entries; /* Variable entries in this section.  */
> +  ctf_id_t id;				 /* BTF type ID of this record.  */
> +  const char *name;			 /* Section name, e.g. ".bss".  */
> +  uint32_t name_offset;			 /* Offset to name in string table.  */
> +  vec<struct btf_datasec_entry> entries; /* Entries in this section.  */
>   } btf_datasec_t;
>   
>   /* One BTF_KIND_DATASEC record is created for each output data section which
>      will hold at least one variable.  */
>   static vec<btf_datasec_t> datasecs;
>   
> -/* Holes occur for types which are present in the CTF container, but are either
> -   non-representable or redundant in BTF.  */
> -static vec<ctf_id_t> holes;
> -
> -/* CTF definition(s) of void. Only one definition of void should be generated.
> -   We should not encounter more than one definition of void, but use a vector
> -   to be safe.  */
> -static vec<ctf_id_t> voids;
> -
>   /* Functions in BTF have two separate type records - one for the prototype
>      (BTF_KIND_FUNC_PROTO), as well as a BTF_KIND_FUNC. CTF_K_FUNCTION types
>      map closely to BTF_KIND_FUNC_PROTO, but the BTF_KIND_FUNC records must be
>      created. This vector holds them.  */
>   static GTY (()) vec<ctf_dtdef_ref, va_gc> *funcs;
>   
> -/* The number of BTF variables added to the TU CTF container.  */
> -static unsigned int num_vars_added = 0;
> -
> -/* The number of BTF types added to the TU CTF container.  */
> -static unsigned int num_types_added = 0;
> +/* Maps BTF_KIND_FUNC_PROTO to the BTF_KIND_FUNC record for it.  Used when
> +   creating DATASEC entries.  */
> +static GTY (()) hash_map<ctf_dtdef_ref, ctf_dtdef_ref> *func_map;
>   
> -/* The number of types synthesized for BTF that do not correspond to
> -   CTF types.  */
> -static unsigned int num_types_created = 0;
> +/* Highest BTF ID assigned to any regular type translated from CTF.
> +   Does not include BTF_KIND_{VAR,FUNC,DATASEC} types.  */
> +static ctf_id_t max_translated_id = 0;
>   
>   /* Name strings for BTF kinds.
>      Note: the indices here must match the type defines in btf.h.  */
> @@ -155,6 +146,16 @@ get_btf_kind (uint32_t ctf_kind)
>     return BTF_KIND_UNKN;
>   }
>   
> +/* Convenience wrapper around get_btf_kind for the common case.  */
> +
> +static uint32_t
> +btf_dtd_kind (ctf_dtdef_ref dtd)
> +{
> +  if (!dtd)
> +    return BTF_KIND_UNKN;
> +  return get_btf_kind (CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info));
> +}
> +
>   /* Some BTF types, like BTF_KIND_FUNC_PROTO, are anonymous.  The machinery
>      in btfout to emit BTF, may reset dtd_data->ctti_name, but does not update
>      the name in the ctf_dtdef_ref type object (deliberate choice).  This
> @@ -168,101 +169,20 @@ get_btf_type_name (ctf_dtdef_ref dtd)
>     return (dtd->dtd_data.ctti_name) ? dtd->dtd_name : anon;
>   }
>   
> -/* Helper routines to map between 'relative' and 'absolute' IDs.
> -
> -   In BTF all records (including variables) are output in one long list, and all
> -   inter-type references are via index into that list.  But internally since we
> -   a) translate from CTF, which separates variable records from regular types
> -   and b) create some additional types after the fact, things like VAR and FUNC
> -   records are stored in separate vectors with their own indices.  These
> -   functions map between the 'relative' IDs (i.e.  indices in their respective
> -   containers) and 'absolute' IDs (i.e.  indices in the final contiguous
> -   output list), which goes in order:
> -     all normal type records translated from CTF
> -     all BTF_KIND_VAR records
> -     all BTF_KIND_FUNC records (synthesized split function records)
> -     all BTF_KIND_DATASEC records (synthesized)
> -
> -   The extra '+ 1's below are to account for the implicit "void" record, which
> -   has index 0 but isn't actually contained in the type list.  */
> -
> -/* Return the final BTF ID of the variable at relative index REL.  */
> -
> -static ctf_id_t
> -btf_absolute_var_id (ctf_id_t rel)
> -{
> -  return rel + (num_types_added + 1);
> -}
> -
> -/* Return the relative index of the variable with final BTF ID ABS.  */
> -
> -static ctf_id_t
> -btf_relative_var_id (ctf_id_t abs)
> -{
> -  return abs - (num_types_added + 1);
> -}
> -
> -/* Return the final BTF ID of the func record at relative index REL.  */
> -
> -static ctf_id_t
> -btf_absolute_func_id (ctf_id_t rel)
> -{
> -  return rel + (num_types_added + 1) + num_vars_added;
> -}
> -
> -/* Return the relative index of the func record with final BTF ID ABS.  */
> -
> -static ctf_id_t
> -btf_relative_func_id (ctf_id_t abs)
> -{
> -  return abs - ((num_types_added + 1) + num_vars_added);
> -}
> -
> -/* Return the final BTF ID of the datasec record at relative index REL.  */
> -
> -static ctf_id_t
> -btf_absolute_datasec_id (ctf_id_t rel)
> -{
> -  return rel + (num_types_added + 1) + num_vars_added + funcs->length ();
> -}
> -
> -
> -/* Allocate the btf_id_map, and initialize elements to BTF_INVALID_TYPEID.  */
> -
> -static void
> -init_btf_id_map (size_t len)
> +static bool
> +btf_emit_type_p (ctf_dtdef_ref dtd)
>   {
> -  btf_id_map = XNEWVEC (ctf_id_t, len);
> +  uint32_t kind = btf_dtd_kind (dtd);
>   
> -  btf_id_map[0] = BTF_VOID_TYPEID;
> -  for (size_t i = 1; i < len; i++)
> -    btf_id_map[i] = BTF_INVALID_TYPEID;
> -}
> +  if (kind == BTF_KIND_UNKN)
> +    /* This type is not representable in BTF.  */
> +    return false;
>   
> -/* Return the BTF type ID of CTF type ID KEY, or BTF_INVALID_TYPEID if the CTF
> -   type with ID KEY does not map to a BTF type.  */
> +  if (kind == BTF_KIND_INT && dtd->dtd_data.ctti_size == 0)
> +    /* This is a (redundant) definition of void.  */
> +    return false;
>   
> -ctf_id_t
> -get_btf_id (ctf_id_t key)
> -{
> -  return btf_id_map[key];
> -}
> -
> -/* Set the CTF type ID KEY to map to BTF type ID VAL.  */
> -
> -static inline void
> -set_btf_id (ctf_id_t key, ctf_id_t val)
> -{
> -  btf_id_map[key] = val;
> -}
> -
> -/* Return TRUE iff the given CTF type ID maps to a BTF type which will
> -   be emitted.  */
> -static inline bool
> -btf_emit_id_p (ctf_id_t id)
> -{
> -  return ((btf_id_map[id] != BTF_VOID_TYPEID)
> -	  && (btf_id_map[id] <= BTF_MAX_TYPE));
> +  return true;
>   }
>   
>   /* Return true if DTD is a forward-declared enum.  The BTF representation
> @@ -271,9 +191,8 @@ btf_emit_id_p (ctf_id_t id)
>   static bool
>   btf_fwd_to_enum_p (ctf_dtdef_ref dtd)
>   {
> -  uint32_t btf_kind = get_btf_kind (CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info));
> -
> -  return (btf_kind == BTF_KIND_FWD && dtd->dtd_data.ctti_type == CTF_K_ENUM);
> +  uint32_t kind = btf_dtd_kind (dtd);
> +  return (kind == BTF_KIND_FWD && dtd->dtd_data.ctti_type == CTF_K_ENUM);
>   }
>   
>   /* Each BTF type can be followed additional, variable-length information
> @@ -285,7 +204,7 @@ btf_calc_num_vbytes (ctf_dtdef_ref dtd)
>   {
>     uint64_t vlen_bytes = 0;
>   
> -  uint32_t kind = get_btf_kind (CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info));
> +  uint32_t kind = btf_dtd_kind (dtd);
>     uint32_t vlen = CTF_V2_INFO_VLEN (dtd->dtd_data.ctti_info);
>   
>     switch (kind)
> @@ -355,41 +274,6 @@ init_btf_sections (void)
>   			       BTF_INFO_SECTION_LABEL, btf_label_num++);
>   }
>   
> -/* Push a BTF datasec variable entry INFO into the datasec named SECNAME,
> -   creating the datasec if it does not already exist.  */
> -
> -static void
> -btf_datasec_push_entry (ctf_container_ref ctfc, const char *secname,
> -			struct btf_var_secinfo info)
> -{
> -  if (secname == NULL)
> -    return;
> -
> -  for (size_t i = 0; i < datasecs.length (); i++)
> -    if (strcmp (datasecs[i].name, secname) == 0)
> -      {
> -	datasecs[i].entries.safe_push (info);
> -	return;
> -      }
> -
> -  /* If we don't already have a datasec record for secname, make one.  */
> -
> -  uint32_t str_off;
> -  ctf_add_string (ctfc, secname, &str_off, CTF_AUX_STRTAB);
> -  if (strcmp (secname, ""))
> -    ctfc->ctfc_aux_strlen += strlen (secname) + 1;
> -
> -  btf_datasec_t ds;
> -  ds.name = secname;
> -  ds.name_offset = str_off;
> -
> -  ds.entries.create (0);
> -  ds.entries.safe_push (info);
> -
> -  datasecs.safe_push (ds);
> -}
> -
> -
>   /* Return the section name, as of interest to btf_collect_datasec, for the
>      given symtab node.  Note that this deliberately returns NULL for objects
>      which do not go in a section btf_collect_datasec cares about.  */
> @@ -418,301 +302,15 @@ get_section_name (symtab_node *node)
>     return section_name;
>   }
>   
> -/* Construct all BTF_KIND_DATASEC records for CTFC. One such record is created
> -   for each non-empty data-containing section in the output. Each record is
> -   followed by a variable number of entries describing the variables stored
> -   in that section.  */
> -
> -static void
> -btf_collect_datasec (ctf_container_ref ctfc)
> -{
> -  cgraph_node *func;
> -  FOR_EACH_FUNCTION (func)
> -    {
> -      dw_die_ref die = lookup_decl_die (func->decl);
> -      if (die == NULL)
> -	continue;
> -
> -      ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, die);
> -      if (dtd == NULL)
> -	continue;
> -
> -      if (DECL_EXTERNAL (func->decl)
> -	  && (lookup_attribute ("kernel_helper",
> -				DECL_ATTRIBUTES (func->decl))) != NULL_TREE)
> -	continue;
> -
> -      /* Functions actually get two types: a BTF_KIND_FUNC_PROTO, and
> -	 also a BTF_KIND_FUNC.  But the CTF container only allocates one
> -	 type per function, which matches closely with BTF_KIND_FUNC_PROTO.
> -	 For each such function, also allocate a BTF_KIND_FUNC entry.
> -	 These will be output later.  */
> -      ctf_dtdef_ref func_dtd = ggc_cleared_alloc<ctf_dtdef_t> ();
> -      func_dtd->dtd_data = dtd->dtd_data;
> -      func_dtd->dtd_data.ctti_type = dtd->dtd_type;
> -      func_dtd->linkage = dtd->linkage;
> -      func_dtd->dtd_name = dtd->dtd_name;
> -      /* +1 for the sentinel type not in the types map.  */
> -      func_dtd->dtd_type = num_types_added + num_types_created + 1;
> -
> -      /* Only the BTF_KIND_FUNC type actually references the name. The
> -	 BTF_KIND_FUNC_PROTO is always anonymous.  */
> -      dtd->dtd_data.ctti_name = 0;
> -
> -      vec_safe_push (funcs, func_dtd);
> -      num_types_created++;
> -
> -      /* Mark any 'extern' funcs and add DATASEC entries for them.  */
> -      if (DECL_EXTERNAL (func->decl))
> -	{
> -	  func_dtd->linkage = BTF_FUNC_EXTERN;
> -
> -	  const char *section_name = get_section_name (func);
> -	  /* Note: get_section_name () returns NULL for functions in text
> -	     section.  This is intentional, since we do not want to generate
> -	     DATASEC entries for them.  */
> -	  if (section_name == NULL)
> -	    continue;
> -
> -	  struct btf_var_secinfo info;
> -
> -	  info.type = func_dtd->dtd_type;
> -
> -	  /* Both zero at compile time.  */
> -	  info.size = 0;
> -	  info.offset = 0;
> -
> -	  btf_datasec_push_entry (ctfc, section_name, info);
> -	}
> -    }
> -
> -  varpool_node *node;
> -  FOR_EACH_VARIABLE (node)
> -    {
> -      dw_die_ref die = lookup_decl_die (node->decl);
> -      if (die == NULL)
> -	continue;
> -
> -      ctf_dvdef_ref dvd = ctf_dvd_lookup (ctfc, die);
> -      if (dvd == NULL)
> -	continue;
> -
> -      /* Mark extern variables.  */
> -      if (DECL_EXTERNAL (node->decl))
> -	{
> -	  dvd->dvd_visibility = BTF_VAR_GLOBAL_EXTERN;
> -
> -	  /* PR112849: avoid assuming a section for extern decls without
> -	     an explicit section, which would result in incorrectly
> -	     emitting a BTF_KIND_DATASEC entry for them.  */
> -	  if (node->get_section () == NULL)
> -	    continue;
> -	}
> -
> -      const char *section_name = get_section_name (node);
> -      if (section_name == NULL)
> -	continue;
> -
> -      struct btf_var_secinfo info;
> -
> -      info.type = 0;
> -      unsigned int *var_id = btf_var_ids->get (dvd);
> -      if (var_id)
> -	info.type = btf_absolute_var_id (*var_id);
> -      else
> -	continue;
> -
> -      info.size = 0;
> -      tree size = DECL_SIZE_UNIT (node->decl);
> -      if (tree_fits_uhwi_p (size))
> -	info.size = tree_to_uhwi (size);
> -      else if (VOID_TYPE_P (TREE_TYPE (node->decl)))
> -	info.size = 1;
> -
> -      /* Offset is left as 0 at compile time, to be filled in by loaders such
> -	 as libbpf.  */
> -      info.offset = 0;
> -
> -      btf_datasec_push_entry (ctfc, section_name, info);
> -    }
> -
> -  num_types_created += datasecs.length ();
> -}
> -
> -/* Return true if the type ID is that of a type which will not be emitted (for
> -   example, if it is not representable in BTF).  */
> -
> -static bool
> -btf_removed_type_p (ctf_id_t id)
> -{
> -  return holes.contains (id);
> -}
> -
> -/* Adjust the given type ID to account for holes and duplicate definitions of
> -   void.  */
> -
> -static ctf_id_t
> -btf_adjust_type_id (ctf_id_t id)
> -{
> -  size_t n;
> -  ctf_id_t i = 0;
> -
> -  /* Do not adjust invalid type markers.  */
> -  if (id == BTF_INVALID_TYPEID)
> -    return id;
> -
> -  for (n = 0; n < voids.length (); n++)
> -    if (id == voids[n])
> -      return BTF_VOID_TYPEID;
> -
> -  for (n = 0; n < holes.length (); n++)
> -    {
> -      if (holes[n] < id)
> -	i++;
> -      else if (holes[n] == id)
> -	return BTF_VOID_TYPEID;
> -    }
> -
> -  return id - i;
> -}
> -
> -/* Postprocessing callback routine for types.  */
> -
> -int
> -btf_dtd_postprocess_cb (ctf_dtdef_ref *slot, ctf_container_ref arg_ctfc)
> -{
> -  ctf_dtdef_ref ctftype = (ctf_dtdef_ref) * slot;
> -
> -  size_t index = ctftype->dtd_type;
> -  gcc_assert (index <= arg_ctfc->ctfc_types->elements ());
> -
> -  uint32_t ctf_kind, btf_kind;
> -
> -  ctf_kind = CTF_V2_INFO_KIND (ctftype->dtd_data.ctti_info);
> -  btf_kind = get_btf_kind (ctf_kind);
> -
> -  if (btf_kind == BTF_KIND_UNKN)
> -    /* This type is not representable in BTF. Create a hole.  */
> -    holes.safe_push (ctftype->dtd_type);
> -
> -  else if (btf_kind == BTF_KIND_INT && ctftype->dtd_data.ctti_size == 0)
> -    {
> -      /* This is a (redundant) definition of void.  */
> -      voids.safe_push (ctftype->dtd_type);
> -      holes.safe_push (ctftype->dtd_type);
> -    }
> -
> -  arg_ctfc->ctfc_types_list[index] = ctftype;
> -
> -  return 1;
> -}
> -
> -/* Preprocessing callback routine for variables.  */
> -
> -int
> -btf_dvd_emit_preprocess_cb (ctf_dvdef_ref *slot, ctf_container_ref arg_ctfc)
> -{
> -  ctf_dvdef_ref var = (ctf_dvdef_ref) * slot;
> -
> -  /* If this is an extern variable declaration with a defining declaration
> -     later, skip it so that only the defining declaration is emitted.
> -     This is the same case, fix and reasoning as in CTF; see PR105089.  */
> -  if (ctf_dvd_ignore_lookup (arg_ctfc, var->dvd_key))
> -    return 1;
> -
> -  /* Do not add variables which refer to unsupported types.  */
> -  if (!voids.contains (var->dvd_type->dtd_type)
> -      && btf_removed_type_p (var->dvd_type->dtd_type))
> -    return 1;
> -
> -  arg_ctfc->ctfc_vars_list[num_vars_added] = var;
> -  btf_var_ids->put (var, num_vars_added);
> -
> -  num_vars_added++;
> -  num_types_created++;
> -
> -  return 1;
> -}
> -
> -/* Preprocessing callback routine for types.  */
> -
> -static void
> -btf_dtd_emit_preprocess_cb (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
> -{
> -  if (!btf_emit_id_p (dtd->dtd_type))
> -    return;
> -
> -  ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (dtd);
> -}
> -
> -/* Preprocess the CTF information to prepare for BTF output.  BTF is almost a
> -   subset of CTF, with many small differences in encoding, and lacking support
> -   for some types (notably floating point formats).
> -
> -   During the preprocessing pass:
> -   - Ascertain that the sorted list of types has been prepared.  For the BTF
> -     generation process, this is taken care of by the btf_init_postprocess ().
> -
> -   - BTF_KIND_FUNC and BTF_KIND_DATASEC records are constructed. These types do
> -     not have analogues in CTF (the analogous type to CTF_K_FUNCTION is
> -     BTF_KIND_FUNC_PROTO), but can be relatively easily deduced from CTF
> -     information.
> -
> -   - Construct BTF_KIND_VAR records, representing variables.
> -
> -   - Calculate the total size in bytes of variable-length information following
> -     BTF type records. This is used for outputting the BTF header.
> -
> -   After preprocessing, all BTF information is ready to be output:
> -   - ctfc->ctfc_types_list holdstypes converted from CTF types. This does not
> -     include KIND_VAR, KIND_FUNC, nor KIND_DATASEC types. These types have been
> -     re-encoded to the appropriate representation in BTF.
> -   - ctfc->ctfc_vars_list holds all variables which should be output.
> -     Variables of unsupported types are not present in this list.
> -   - Vector 'funcs' holds all BTF_KIND_FUNC types, one to match each
> -     BTF_KIND_FUNC_PROTO.
> -   - Vector 'datasecs' holds all BTF_KIND_DATASEC types.  */
> -
> -static void
> -btf_emit_preprocess (ctf_container_ref ctfc)
> -{
> -  size_t num_ctf_types = ctfc->ctfc_types->elements ();
> -  size_t num_ctf_vars = ctfc->ctfc_vars->elements ();
> -  size_t i;
> -
> -  if (num_ctf_types)
> -    {
> -      gcc_assert (ctfc->ctfc_types_list);
> -      /* Preprocess the types.  */
> -      for (i = 1; i <= num_ctf_types; i++)
> -	btf_dtd_emit_preprocess_cb (ctfc, ctfc->ctfc_types_list[i]);
> -    }
> -
> -  btf_var_ids = hash_map<ctf_dvdef_ref, unsigned int>::create_ggc (100);
> -
> -  if (num_ctf_vars)
> -    {
> -      /* Allocate and construct the list of variables. While BTF variables are
> -	 not distinct from types (in that variables are simply types with
> -	 BTF_KIND_VAR), it is simpler to maintain a separate list of variables
> -	 and append them to the types list during output.  */
> -      ctfc->ctfc_vars_list = ggc_vec_alloc<ctf_dvdef_ref>(num_ctf_vars);
> -      ctfc->ctfc_vars->traverse<ctf_container_ref, btf_dvd_emit_preprocess_cb>
> -	(ctfc);
> -
> -      ctfc->ctfc_num_vlen_bytes += (num_vars_added * sizeof (struct btf_var));
> -    }
> -
> -  btf_collect_datasec (ctfc);
> -}
> -
>   /* Return true iff DMD is a member description of a bit-field which can be
>      validly represented in BTF.  */
>   
>   static bool
> -btf_dmd_representable_bitfield_p (ctf_container_ref ctfc, ctf_dmdef_t *dmd)
> +btf_dmd_representable_bitfield_p (ctf_dmdef_t *dmd)
>   {
> -  ctf_dtdef_ref ref_type = ctfc->ctfc_types_list[dmd->dmd_type->dtd_type];
> +  ctf_dtdef_ref ref_type = dmd->dmd_type;
> +  if (!ref_type)
> +    return false;
>   
>     if (CTF_V2_INFO_KIND (ref_type->dtd_data.ctti_info) == CTF_K_SLICE)
>       {
> @@ -734,76 +332,34 @@ btf_dmd_representable_bitfield_p (ctf_container_ref ctfc, ctf_dmdef_t *dmd)
>   /* Asm'out a reference to another BTF type.  */
>   
>   static void
> -btf_asm_type_ref (const char *prefix, ctf_container_ref ctfc, ctf_id_t ctf_id)
> +btf_asm_type_ref (const char *prefix, ctf_dtdef_ref dtd)
>   {
> -  ctf_id_t btf_id = get_btf_id (ctf_id);
> -  if (btf_id == BTF_VOID_TYPEID || btf_id == BTF_INVALID_TYPEID)
> -    {
> -      /* There is no explicit void type.
> -	 Also handle any invalid refs that made it this far, just in case.  */
> -      dw2_asm_output_data (4, btf_id, "%s: void", prefix);
> -    }
> +  if (!dtd || !btf_emit_type_p (dtd))
> +    dw2_asm_output_data (4, BTF_VOID_TYPEID, "%s: void", prefix);
>     else
>       {
> -      gcc_assert (btf_id <= num_types_added);
> -
> -      /* Ref to a standard type in the types list.  Note: take care that we
> -	 must index the type list by the original CTF id, not the BTF id.  */
> -      ctf_dtdef_ref ref_type = ctfc->ctfc_types_list[ctf_id];
> -      uint32_t ref_kind
> -	= get_btf_kind (CTF_V2_INFO_KIND (ref_type->dtd_data.ctti_info));
> -
> -      const char *kind_name = btf_fwd_to_enum_p (ref_type)
> -	? btf_kind_name (BTF_KIND_ENUM)
> -	: btf_kind_name (ref_kind);
> -
> -      dw2_asm_output_data (4, btf_id, "%s: (BTF_KIND_%s '%s')",
> -			   prefix, kind_name,
> -			   get_btf_type_name (ref_type));
> -    }
> -}
> -
> -/* Asm'out a reference to a BTF_KIND_VAR or BTF_KIND_FUNC type.  These type
> -   kinds are BTF-specific, and should only be referred to by entries in
> -   BTF_KIND_DATASEC records.  */
> -
> -static void
> -btf_asm_datasec_type_ref (const char *prefix, ctf_container_ref ctfc,
> -			  ctf_id_t btf_id)
> -{
> -  if (btf_id >= num_types_added + 1
> -      && btf_id < num_types_added + num_vars_added + 1)
> -    {
> -      /* Ref to a variable.  Should only appear in DATASEC entries.  */
> -      ctf_id_t var_id = btf_relative_var_id (btf_id);
> -      ctf_dvdef_ref dvd = ctfc->ctfc_vars_list[var_id];
> -      dw2_asm_output_data (4, btf_id, "%s: (BTF_KIND_VAR '%s')",
> -			   prefix, dvd->dvd_name);
> -
> -    }
> -  else if (btf_id >= num_types_added + num_vars_added + 1)
> -    {
> -      /* Ref to a FUNC record.  */
> -      size_t func_id = btf_relative_func_id (btf_id);
> -      ctf_dtdef_ref ref_type = (*funcs)[func_id];
> -      dw2_asm_output_data (4, btf_id, "%s: (BTF_KIND_FUNC '%s')",
> -			   prefix, get_btf_type_name (ref_type));
> +      uint32_t kind = btf_dtd_kind (dtd);
> +      if (btf_fwd_to_enum_p (dtd))
> +	kind = BTF_KIND_ENUM;
> +      else if (kind == BTF_KIND_FUNC_PROTO && dtd->dtd_type > max_translated_id)
> +	kind = BTF_KIND_FUNC;
> +
> +      dw2_asm_output_data (4, dtd->dtd_type, "%s: (BTF_KIND_%s '%s')",
> +			   prefix, btf_kind_name (kind),
> +			   get_btf_type_name (dtd));
>       }
> -  else
> -    /* The caller should not be calling this.  */
> -    gcc_unreachable ();
>   }
>   
>   /* Asm'out a BTF type. This routine is responsible for the bulk of the task
>      of converting CTF types to their BTF representation.  */
>   
>   static void
> -btf_asm_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
> +btf_asm_type (ctf_dtdef_ref dtd)
>   {
>     uint32_t btf_kind, btf_kflag, btf_vlen, btf_size;
>     uint32_t ctf_info = dtd->dtd_data.ctti_info;
>   
> -  btf_kind = get_btf_kind (CTF_V2_INFO_KIND (ctf_info));
> +  btf_kind = btf_dtd_kind (dtd);
>     btf_size = dtd->dtd_data.ctti_size;
>     btf_vlen = CTF_V2_INFO_VLEN (ctf_info);
>   
> @@ -822,17 +378,17 @@ btf_asm_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
>   
>     if (btf_kind == BTF_KIND_STRUCT || btf_kind == BTF_KIND_UNION)
>       {
> -      /* If a struct/union has ANY bitfield members, set kflag=1.
> -	 Note that we must also change the encoding of every member to encode
> -	 both member bitfield size (stealing most-significant 8 bits) and bit
> -	 offset (LS 24 bits). This is done during preprocessing.  */
> +      /* If a struct/union has ANY bitfield members, set kflag=1.  */
>         ctf_dmdef_t *dmd;
>         for (dmd = dtd->dtd_u.dtu_members;
>   	   dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
>   	{
>   	  /* Set kflag if this member is a representable bitfield.  */
> -	  if (btf_dmd_representable_bitfield_p (ctfc, dmd))
> -	    btf_kflag = 1;
> +	  if (btf_dmd_representable_bitfield_p (dmd))
> +	    {
> +	      btf_kflag = 1;
> +	      break;
> +	    }
>   	}
>       }
>   
> @@ -866,7 +422,7 @@ btf_asm_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
>   		    : BTF_KF_ENUM_SIGNED;
>         if (dtd->dtd_data.ctti_size == 0x8)
>   	btf_kind = BTF_KIND_ENUM64;
> -   }
> +    }
>   
>     /* PR debug/112656.  BTF_KIND_FUNC_PROTO is always anonymous.  */
>     else if (btf_kind == BTF_KIND_FUNC_PROTO)
> @@ -874,7 +430,7 @@ btf_asm_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
>   
>     dw2_asm_output_data (4, dtd->dtd_data.ctti_name,
>   		       "TYPE %" PRIu64 " BTF_KIND_%s '%s'",
> -		       get_btf_id (dtd->dtd_type), btf_kind_name (btf_kind),
> +		       dtd->dtd_type, btf_kind_name (btf_kind),
>   		       get_btf_type_name (dtd));
>     dw2_asm_output_data (4, BTF_TYPE_INFO (btf_kind, btf_kflag, btf_vlen),
>   		       "btt_info: kind=%u, kflag=%u, vlen=%u",
> @@ -900,30 +456,29 @@ btf_asm_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
>         break;
>       }
>   
> -  ctf_id_t ref_id = dtd->dtd_data.ctti_type;
> -  btf_asm_type_ref ("btt_type", ctfc, ref_id);
> +  btf_asm_type_ref ("btt_type", dtd->ref_type);
>   }
>   
>   /* Asm'out the variable information following a BTF_KIND_ARRAY.  */
>   
>   static void
> -btf_asm_array (ctf_container_ref ctfc, ctf_arinfo_t arr)
> +btf_asm_array (ctf_arinfo_t arr)
>   {
> -  btf_asm_type_ref ("bta_elem_type", ctfc, arr.ctr_contents->dtd_type);
> -  btf_asm_type_ref ("bta_index_type", ctfc, arr.ctr_index->dtd_type);
> +  btf_asm_type_ref ("bta_elem_type", arr.ctr_contents);
> +  btf_asm_type_ref ("bta_index_type", arr.ctr_index);
>     dw2_asm_output_data (4, arr.ctr_nelems, "bta_nelems");
>   }
>   
>   /* Asm'out a BTF_KIND_VAR.  */
>   
>   static void
> -btf_asm_varent (ctf_container_ref ctfc, ctf_dvdef_ref var)
> +btf_asm_varent (ctf_dvdef_ref var)
>   {
> -  dw2_asm_output_data (4, var->dvd_name_offset, "TYPE %u BTF_KIND_VAR '%s'",
> -		       (*(btf_var_ids->get (var)) + num_types_added + 1),
> -		       var->dvd_name);
> +  dw2_asm_output_data (4, var->dvd_name_offset,
> +		       "TYPE %" PRIu64 " BTF_KIND_VAR '%s'",
> +		       var->dvd_id, var->dvd_name);
>     dw2_asm_output_data (4, BTF_TYPE_INFO (BTF_KIND_VAR, 0, 0), "btv_info");
> -  btf_asm_type_ref ("btv_type", ctfc, var->dvd_type->dtd_type);
> +  btf_asm_type_ref ("btv_type", var->dvd_type);
>     dw2_asm_output_data (4, var->dvd_visibility, "btv_linkage");
>   }
>   
> @@ -931,23 +486,22 @@ btf_asm_varent (ctf_container_ref ctfc, ctf_dvdef_ref var)
>      BTF_KIND_UNION.  */
>   
>   static void
> -btf_asm_sou_member (ctf_container_ref ctfc, ctf_dmdef_t * dmd, unsigned int idx)
> +btf_asm_sou_member (ctf_dmdef_t * dmd, unsigned int idx)
>   {
> -  ctf_dtdef_ref ref_type = ctfc->ctfc_types_list[dmd->dmd_type->dtd_type];
> -  ctf_id_t base_type = dmd->dmd_type->dtd_type;
> +  ctf_dtdef_ref base_type = dmd->dmd_type;
>     uint64_t sou_offset = dmd->dmd_offset;
>   
>     dw2_asm_output_data (4, dmd->dmd_name_offset,
>   		       "MEMBER '%s' idx=%u",
>   		       dmd->dmd_name, idx);
>   
> -  /* Re-encode bitfields to BTF representation.  */
> -  if (CTF_V2_INFO_KIND (ref_type->dtd_data.ctti_info) == CTF_K_SLICE)
> +  if (base_type
> +      && CTF_V2_INFO_KIND (base_type->dtd_data.ctti_info) == CTF_K_SLICE)
>       {
> -      if (btf_dmd_representable_bitfield_p (ctfc, dmd))
> +      if (btf_dmd_representable_bitfield_p (dmd))
>   	{
> -	  unsigned short word_offset = ref_type->dtd_u.dtu_slice.cts_offset;
> -	  unsigned short bits = ref_type->dtd_u.dtu_slice.cts_bits;
> +	  unsigned short word_offset = base_type->dtd_u.dtu_slice.cts_offset;
> +	  unsigned short bits = base_type->dtd_u.dtu_slice.cts_bits;
>   
>   	  /* Pack the bit offset and bitfield size together.  */
>   	  sou_offset += word_offset;
> @@ -955,17 +509,17 @@ btf_asm_sou_member (ctf_container_ref ctfc, ctf_dmdef_t * dmd, unsigned int idx)
>   	  sou_offset |= ((bits & 0xff) << 24);
>   
>   	  /* Refer to the base type of the slice.  */
> -	  base_type = ref_type->dtd_u.dtu_slice.cts_type->dtd_type;
> +	  base_type = base_type->dtd_u.dtu_slice.cts_type;
>   	}
>         else
>   	{
>   	  /* Bitfield cannot be represented in BTF.  Emit the member as having
>   	     'void' type.  */
> -	  base_type = BTF_VOID_TYPEID;
> +	  base_type = NULL;
>   	}
>       }
>   
> -  btf_asm_type_ref ("btm_type", ctfc, base_type);
> +  btf_asm_type_ref ("btm_type", base_type);
>     dw2_asm_output_data (4, sou_offset, "btm_offset");
>   }
>   
> @@ -988,86 +542,68 @@ btf_asm_enum_const (unsigned int size, ctf_dmdef_t * dmd, unsigned int idx)
>   /* Asm'out a function parameter description following a BTF_KIND_FUNC_PROTO.  */
>   
>   static void
> -btf_asm_func_arg (ctf_container_ref ctfc, ctf_func_arg_t * farg,
> -		  size_t stroffset)
> +btf_asm_func_arg (ctf_func_arg_t * farg, size_t stroffset)
>   {
>     /* If the function arg does not have a name, refer to the null string at
>        the start of the string table. This ensures correct encoding for varargs
>        '...' arguments.  */
>     if ((farg->farg_name != NULL) && strcmp (farg->farg_name, ""))
> -    dw2_asm_output_data (4, farg->farg_name_offset + stroffset, "farg_name");
> +    dw2_asm_output_data (4, farg->farg_name_offset + stroffset,
> +			 "farg_name '%s'", farg->farg_name);
>     else
> -    dw2_asm_output_data (4, 0, "farg_name");
> -
> -  ctf_id_t ref_id = BTF_VOID_TYPEID;
> -  if (farg->farg_type && !btf_removed_type_p (farg->farg_type->dtd_type))
> -    ref_id = farg->farg_type->dtd_type;
> +    dw2_asm_output_data (4, 0, "farg_name ''");
>   
> -  btf_asm_type_ref ("farg_type", ctfc, ref_id);
> +  btf_asm_type_ref ("farg_type", farg->farg_type);
>   }
>   
>   /* Asm'out a BTF_KIND_FUNC type.  */
>   
>   static void
> -btf_asm_func_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd, ctf_id_t id)
> +btf_asm_func_type (ctf_dtdef_ref dtd)
>   {
> -  ctf_id_t ref_id = dtd->dtd_data.ctti_type;
>     dw2_asm_output_data (4, dtd->dtd_data.ctti_name,
>   		       "TYPE %" PRIu64 " BTF_KIND_FUNC '%s'",
> -		       btf_absolute_func_id (id), get_btf_type_name (dtd));
> +		       dtd->dtd_type, get_btf_type_name (dtd));
>     dw2_asm_output_data (4, BTF_TYPE_INFO (BTF_KIND_FUNC, 0, dtd->linkage),
>   		       "btt_info: kind=%u, kflag=%u, linkage=%u",
>   		       BTF_KIND_FUNC, 0, dtd->linkage);
> -  btf_asm_type_ref ("btt_type", ctfc, ref_id);
> +  btf_asm_type_ref ("btt_type", dtd->ref_type);
>   }
>   
> -/* Collect the name for the DATASEC reference required to be output as a
> -   symbol. */
> +/* Asm'out a variable entry following a BTF_KIND_DATASEC.  */
>   
> -static const char *
> -get_name_for_datasec_entry (ctf_container_ref ctfc, ctf_id_t ref_id)
> +static void
> +btf_asm_datasec_entry (struct btf_datasec_entry entry)
>   {
> -  if (ref_id >= num_types_added + 1
> -      && ref_id < num_types_added + num_vars_added + 1)
> +  const char *symbol_name = NULL;
> +  if (entry.is_var)
>       {
> -      /* Ref to a variable.  Should only appear in DATASEC entries.  */
> -      ctf_id_t var_id = btf_relative_var_id (ref_id);
> -      ctf_dvdef_ref dvd = ctfc->ctfc_vars_list[var_id];
> -      return dvd->dvd_name;
> +      symbol_name = entry.dvd->dvd_name;
> +      dw2_asm_output_data (4, entry.dvd->dvd_id,
> +			   "bts_type: (BTF_KIND_VAR '%s')", symbol_name);
>       }
> -  else if (ref_id >= num_types_added + num_vars_added + 1)
> +  else
>       {
> -      /* Ref to a FUNC record.  */
> -      size_t func_id = btf_relative_func_id (ref_id);
> -      ctf_dtdef_ref ref_type = (*funcs)[func_id];
> -      return get_btf_type_name (ref_type);
> +      symbol_name = entry.dtd->dtd_name;
> +      btf_asm_type_ref ("bts_type", entry.dtd);
>       }
> -  return NULL;
> -}
> -
> -/* Asm'out a variable entry following a BTF_KIND_DATASEC.  */
>   
> -static void
> -btf_asm_datasec_entry (ctf_container_ref ctfc, struct btf_var_secinfo info)
> -{
> -  const char *symbol_name = get_name_for_datasec_entry (ctfc, info.type);
> -  btf_asm_datasec_type_ref ("bts_type", ctfc, info.type);
>     if (!btf_with_core_debuginfo_p () || symbol_name == NULL)
> -    dw2_asm_output_data (4, info.offset, "bts_offset");
> +    dw2_asm_output_data (4, 0, "bts_offset");
>     else
>       dw2_asm_output_offset (4, symbol_name, NULL, "bts_offset");
> -  dw2_asm_output_data (4, info.size, "bts_size");
> +
> +  dw2_asm_output_data (4, entry.size, "bts_size");
>   }
>   
>   /* Asm'out a whole BTF_KIND_DATASEC, including its variable entries.  */
>   
>   static void
> -btf_asm_datasec_type (ctf_container_ref ctfc, btf_datasec_t ds, ctf_id_t id,
> -		      size_t stroffset)
> +btf_asm_datasec_type (btf_datasec_t ds)
>   {
> -  dw2_asm_output_data (4, ds.name_offset + stroffset,
> +  dw2_asm_output_data (4, ds.name_offset,
>   		       "TYPE %" PRIu64 " BTF_KIND_DATASEC '%s'",
> -		       btf_absolute_datasec_id (id), ds.name);
> +		       ds.id, ds.name);
>     dw2_asm_output_data (4, BTF_TYPE_INFO (BTF_KIND_DATASEC, 0,
>   					 ds.entries.length ()),
>   		       "btt_info: n_entries=%u", ds.entries.length ());
> @@ -1075,7 +611,7 @@ btf_asm_datasec_type (ctf_container_ref ctfc, btf_datasec_t ds, ctf_id_t id,
>        loaders such as libbpf.  */
>     dw2_asm_output_data (4, 0, "btt_size");
>     for (size_t i = 0; i < ds.entries.length (); i++)
> -    btf_asm_datasec_entry (ctfc, ds.entries[i]);
> +    btf_asm_datasec_entry (ds.entries[i]);
>   }
>   
>   /* Compute and output the header information for a .BTF section.  */
> @@ -1094,20 +630,11 @@ output_btf_header (ctf_container_ref ctfc)
>   
>      uint32_t type_off = 0, type_len = 0;
>      uint32_t str_off = 0, str_len = 0;
> -   uint32_t datasec_vlen_bytes = 0;
>   
>      if (!ctfc_is_empty_container (ctfc))
>        {
> -       for (size_t i = 0; i < datasecs.length (); i++)
> -	 {
> -	   datasec_vlen_bytes += ((datasecs[i].entries.length ())
> -				  * sizeof (struct btf_var_secinfo));
> -	 }
> -
>          /* Total length (bytes) of the types section.  */
> -       type_len = (num_types_added * sizeof (struct btf_type))
> -	 + (num_types_created * sizeof (struct btf_type))
> -	 + datasec_vlen_bytes
> +       type_len = ctfc->ctfc_num_types * sizeof (struct btf_type)
>   	 + ctfc->ctfc_num_vlen_bytes;
>   
>          str_off = type_off + type_len;
> @@ -1119,7 +646,9 @@ output_btf_header (ctf_container_ref ctfc)
>      /* Offset of type section.  */
>      dw2_asm_output_data (4, type_off, "type_off");
>      /* Length of type section in bytes.  */
> -   dw2_asm_output_data (4, type_len, "type_len");
> +   dw2_asm_output_data (4, type_len, "type_len: ntypes=%u, vlen=%u",
> +			(uint32_t) ctfc->ctfc_num_types,
> +			(uint32_t) ctfc->ctfc_num_vlen_bytes);
>       /* Offset of string section.  */
>      dw2_asm_output_data (4, str_off, "str_off");
>       /* Length of string section in bytes.  */
> @@ -1132,11 +661,11 @@ static void
>   output_btf_vars (ctf_container_ref ctfc)
>   {
>     size_t i;
> -  size_t num_ctf_vars = num_vars_added;
> +  size_t num_ctf_vars = ctfc->ctfc_vars_list_count;
>     if (num_ctf_vars)
>       {
>         for (i = 0; i < num_ctf_vars; i++)
> -	btf_asm_varent (ctfc, ctfc->ctfc_vars_list[i]);
> +	btf_asm_varent (ctfc->ctfc_vars_list[i]);
>       }
>   }
>   
> @@ -1151,7 +680,8 @@ output_btf_strs (ctf_container_ref ctfc)
>   
>     while (ctf_string)
>       {
> -      dw2_asm_output_nstring (ctf_string->cts_str, -1, "btf_string, str_pos = 0x%x", str_pos);
> +      dw2_asm_output_nstring (ctf_string->cts_str, -1,
> +			      "btf_string, str_pos = 0x%x", str_pos);
>         str_pos += strlen(ctf_string->cts_str) + 1;
>         ctf_string = ctf_string->cts_next;
>       }
> @@ -1159,7 +689,8 @@ output_btf_strs (ctf_container_ref ctfc)
>     ctf_string = ctfc->ctfc_aux_strtable.ctstab_head;
>     while (ctf_string)
>       {
> -      dw2_asm_output_nstring (ctf_string->cts_str, -1, "btf_aux_string, str_pos = 0x%x", str_pos);
> +      dw2_asm_output_nstring (ctf_string->cts_str, -1,
> +			      "btf_aux_string, str_pos = 0x%x", str_pos);
>         str_pos += strlen(ctf_string->cts_str) + 1;
>         ctf_string = ctf_string->cts_next;
>       }
> @@ -1169,7 +700,7 @@ output_btf_strs (ctf_container_ref ctfc)
>      BTF_KIND_UNION type.  */
>   
>   static void
> -output_asm_btf_sou_fields (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
> +output_asm_btf_sou_fields (ctf_dtdef_ref dtd)
>   {
>     ctf_dmdef_t * dmd;
>   
> @@ -1177,7 +708,7 @@ output_asm_btf_sou_fields (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
>     for (dmd = dtd->dtd_u.dtu_members;
>          dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
>       {
> -      btf_asm_sou_member (ctfc, dmd, idx);
> +      btf_asm_sou_member (dmd, idx);
>         idx++;
>       }
>   }
> @@ -1185,8 +716,7 @@ output_asm_btf_sou_fields (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
>   /* Output all enumerator constants following a BTF_KIND_ENUM{,64}.  */
>   
>   static void
> -output_asm_btf_enum_list (ctf_container_ref ARG_UNUSED (ctfc),
> -			  ctf_dtdef_ref dtd)
> +output_asm_btf_enum_list (ctf_dtdef_ref dtd)
>   {
>     ctf_dmdef_t * dmd;
>   
> @@ -1209,7 +739,7 @@ output_asm_btf_func_args_list (ctf_container_ref ctfc,
>     ctf_func_arg_t * farg;
>     for (farg = dtd->dtd_u.dtu_argv;
>          farg != NULL; farg = (ctf_func_arg_t *) ctf_farg_list_next (farg))
> -    btf_asm_func_arg (ctfc, farg, farg_name_offset);
> +    btf_asm_func_arg (farg, farg_name_offset);
>   }
>   
>   /* Output the variable portion of a BTF type record. The information depends
> @@ -1220,7 +750,7 @@ output_asm_btf_vlen_bytes (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
>   {
>     uint32_t btf_kind, encoding;
>   
> -  btf_kind = get_btf_kind (CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info));
> +  btf_kind = btf_dtd_kind (dtd);
>   
>     if (btf_kind == BTF_KIND_UNKN)
>       return;
> @@ -1233,8 +763,7 @@ output_asm_btf_vlen_bytes (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
>         if (dtd->dtd_data.ctti_size < 1)
>   	break;
>   
> -      /* In BTF the CHAR `encoding' seems to not be used, so clear it
> -         here.  */
> +      /* In BTF the CHAR `encoding' seems to not be used, so clear it here.  */
>         dtd->dtd_u.dtu_enc.cte_format &= ~BTF_INT_CHAR;
>   
>         encoding = BTF_INT_DATA (dtd->dtd_u.dtu_enc.cte_format,
> @@ -1245,16 +774,16 @@ output_asm_btf_vlen_bytes (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
>         break;
>   
>       case BTF_KIND_ARRAY:
> -      btf_asm_array (ctfc, dtd->dtd_u.dtu_arr);
> +      btf_asm_array (dtd->dtd_u.dtu_arr);
>         break;
>   
>       case BTF_KIND_STRUCT:
>       case BTF_KIND_UNION:
> -      output_asm_btf_sou_fields (ctfc, dtd);
> +      output_asm_btf_sou_fields (dtd);
>         break;
>   
>       case BTF_KIND_ENUM:
> -      output_asm_btf_enum_list (ctfc, dtd);
> +      output_asm_btf_enum_list (dtd);
>         break;
>   
>       case BTF_KIND_FUNC_PROTO:
> @@ -1284,9 +813,9 @@ output_asm_btf_vlen_bytes (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
>   static void
>   output_asm_btf_type (ctf_container_ref ctfc, ctf_dtdef_ref type)
>   {
> -  if (btf_emit_id_p (type->dtd_type))
> +  if (btf_emit_type_p (type))
>       {
> -      btf_asm_type (ctfc, type);
> +      btf_asm_type (type);
>         output_asm_btf_vlen_bytes (ctfc, type);
>       }
>   }
> @@ -1298,7 +827,9 @@ static void
>   output_btf_types (ctf_container_ref ctfc)
>   {
>     size_t i;
> -  size_t num_types = ctfc->ctfc_types->elements ();
> +  size_t num_types;
> +  num_types = ctfc->ctfc_types->elements ();
> +
>     if (num_types)
>       {
>         for (i = 1; i <= num_types; i++)
> @@ -1309,76 +840,45 @@ output_btf_types (ctf_container_ref ctfc)
>   /* Output all BTF_KIND_FUNC type records.  */
>   
>   static void
> -output_btf_func_types (ctf_container_ref ctfc)
> +output_btf_func_types (void)
>   {
>     ctf_dtdef_ref ref;
>     unsigned i;
>     FOR_EACH_VEC_ELT (*funcs, i, ref)
> -    btf_asm_func_type (ctfc, ref, i);
> +    btf_asm_func_type (ref);
>   }
>   
>   /* Output all BTF_KIND_DATASEC records.  */
>   
>   static void
> -output_btf_datasec_types (ctf_container_ref ctfc)
> +output_btf_datasec_types (void)
>   {
> -  size_t name_offset = ctfc_get_strtab_len (ctfc, CTF_STRTAB);
> -
> -  for (size_t i = 0; i < datasecs.length(); i++)
> -    btf_asm_datasec_type (ctfc, datasecs[i], i, name_offset);
> +  for (size_t i = 0; i < datasecs.length (); i++)
> +    btf_asm_datasec_type (datasecs[i]);
>   }
>   
> -/* Postprocess the CTF debug data post initialization.
> -
> -   During the postprocess pass:
> -
> -   - Prepare the sorted list of BTF types.
> -
> -     The sorted list of BTF types is, firstly, used for lookup (during the BTF
> -     generation process) of CTF/BTF types given a typeID.
> -
> -     Secondly, in the emitted BTF section, BTF Types need to be in the sorted
> -     order of their type IDs.  The BTF types section is viewed as an array,
> -     with type IDs used to index into that array.  It is essential that every
> -     type be placed at the exact index corresponding to its ID, or else
> -     references to that type from other types will no longer be correct.
> -
> -   - References to void types are converted to reference BTF_VOID_TYPEID. In
> -     CTF, a distinct type is used to encode void.
> -
> -   - Bitfield struct/union members are converted to BTF encoding. CTF uses
> -     slices to encode bitfields, but BTF does not have slices and encodes
> -     bitfield information directly in the variable-length btf_member
> -     descriptions following the struct or union type.
> -
> -   - Unrepresentable types are removed. We cannot have any invalid BTF types
> -     appearing in the output so they must be removed, and type ids of other
> -     types and references adjust accordingly. This also involves ensuring that
> -     BTF descriptions of struct members referring to unrepresentable types are
> -     not emitted, as they would be nonsensical.
> -
> -   - Adjust inner- and inter-type references-by-ID to account for removed
> -     types, and construct the types list.  */
> +/* Write out all BTF debug info.  */
>   
>   void
> -btf_init_postprocess (void)
> +btf_output (ctf_container_ref ctfc)
>   {
> -  ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
> -
> -  holes.create (0);
> -  voids.create (0);
> +  output_btf_header (ctfc);
> +  output_btf_types (ctfc);
> +  output_btf_vars (ctfc);
> +  output_btf_func_types ();
> +  output_btf_datasec_types ();
> +  output_btf_strs (ctfc);
> +}
>   
> -  num_types_added = 0;
> -  num_types_created = 0;
> +/* Workaround for 'const void' variables.  These variables are sometimes used
> +   in eBPF programs to address kernel symbols.  DWARF does not generate const
> +   qualifier on void type, so we would incorrectly emit these variables
> +   without the const qualifier.  Find any such variables, and update them to
> +   refer to a new 'const' modifier type for void.  */
>   
> -  /* Workaround for 'const void' variables.  These variables are sometimes used
> -     in eBPF programs to address kernel symbols.  DWARF does not generate const
> -     qualifier on void type, so we would incorrectly emit these variables
> -     without the const qualifier.
> -     Unfortunately we need the TREE node to know it was const, and we need
> -     to create the const modifier type (if needed) now, before making the types
> -     list.  So we can't avoid iterating with FOR_EACH_VARIABLE here, and then
> -     again when creating the DATASEC entries.  */
> +static void
> +btf_early_add_const_void (ctf_container_ref ctfc)
> +{
>     ctf_dtdef_ref constvoid_dtd = NULL;
>     varpool_node *var;
>     FOR_EACH_VARIABLE (var)
> @@ -1393,120 +893,389 @@ btf_init_postprocess (void)
>   	  if (die == NULL)
>   	    continue;
>   
> -	  ctf_dvdef_ref dvd = ctf_dvd_lookup (tu_ctfc, die);
> +	  ctf_dvdef_ref dvd = ctf_dvd_lookup (ctfc, die);
>   	  if (dvd == NULL)
>   	    continue;
>   
>   	  /* Create the 'const' modifier type for void.  */
>   	  if (constvoid_dtd == NULL)
> -	    constvoid_dtd = ctf_add_reftype (tu_ctfc, CTF_ADD_ROOT,
> +	    constvoid_dtd = ctf_add_reftype (ctfc, CTF_ADD_ROOT,
>   					     dvd->dvd_type, CTF_K_CONST, NULL);
>   	  dvd->dvd_type = constvoid_dtd;
>   	}
>       }
> +}
>   
> -  size_t i;
> -  size_t num_ctf_types = tu_ctfc->ctfc_types->elements ();
> +/* Functions actually get two type records: a BTF_KIND_FUNC_PROTO, and also a
> +   BTF_KIND_FUNC.  But the CTF container only allocates one type per function,
> +   which matches closely with BTF_KIND_FUNC_PROTO.  For each such function,
> +   construct a BTF_KIND_FUNC entry.  This is done early, because we want FUNC
> +   records even for functions which are later inlined by optimizations.  */
>   
> -  if (num_ctf_types)
> +static void
> +btf_early_add_func_records (ctf_container_ref ctfc)
> +{
> +  cgraph_node *func;
> +  FOR_EACH_FUNCTION (func)
>       {
> -      init_btf_id_map (num_ctf_types + 1);
> -
> -      /* Allocate the types list and traverse all types, placing each type
> -	 at the index according to its ID.  Add 1 because type ID 0 always
> -	 represents VOID.  */
> -      tu_ctfc->ctfc_types_list
> -	= ggc_vec_alloc<ctf_dtdef_ref>(num_ctf_types + 1);
> -      tu_ctfc->ctfc_types->traverse<ctf_container_ref, btf_dtd_postprocess_cb>
> -	(tu_ctfc);
> -
> -      /* Build mapping of CTF type ID -> BTF type ID, and count total number
> -	 of valid BTF types added.  */
> -      for (i = 1; i <= num_ctf_types; i++)
> -	{
> -	  ctf_dtdef_ref dtd = tu_ctfc->ctfc_types_list[i];
> -	  ctf_id_t btfid = btf_adjust_type_id (dtd->dtd_type);
> -	  set_btf_id (dtd->dtd_type, btfid);
> -	  if (btfid < BTF_MAX_TYPE && (btfid != BTF_VOID_TYPEID))
> -	    num_types_added ++;
> -	}
> +      dw_die_ref die = lookup_decl_die (func->decl);
> +      if (die == NULL)
> +	continue;
> +
> +      ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, die);
> +      if (dtd == NULL)
> +	continue;
> +
> +      /* Do not add FUNC records for kernel helpers.  */
> +      if (DECL_EXTERNAL (func->decl)
> +	  && (lookup_attribute ("kernel_helper",
> +				DECL_ATTRIBUTES (func->decl))) != NULL_TREE)
> +	continue;
> +
> +      ctf_dtdef_ref func_dtd = ggc_cleared_alloc<ctf_dtdef_t> ();
> +      func_dtd->dtd_data = dtd->dtd_data;
> +      func_dtd->dtd_data.ctti_type = dtd->dtd_type;
> +      func_dtd->ref_type = dtd;
> +      func_dtd->linkage = dtd->linkage;
> +      func_dtd->dtd_name = dtd->dtd_name;
> +      /* Type ID will be assigned just before output.  */
> +
> +      /* Only the BTF_KIND_FUNC type actually references the name.
> +	 The BTF_KIND_FUNC_PROTO is always anonymous.  */
> +      dtd->dtd_data.ctti_name = 0;
> +
> +      /* Mark 'extern' funcs.  */
> +      if (DECL_EXTERNAL (func->decl))
> +	func_dtd->linkage = BTF_FUNC_EXTERN;
> +
> +      /* Buffer newly created FUNC records.  We cannot simply insert them
> +	 into the types map, because types are keyed by their DWARF DIE,
> +	 and we have no unique DIE to use as a key since the FUNC_PROTOs
> +	 are already present in the map.  */
> +      vec_safe_push (funcs, func_dtd);
> +      func_map->put (dtd, func_dtd);
>       }
>   }
>   
> -/* Process and output all BTF data. Entry point of btfout.  */
> +/* 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
> +   BTF_KIND_FUNC records.  */
>   
>   void
> -btf_output (const char * filename)
> +btf_early_finish (void)
>   {
>     ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
>   
> -  init_btf_sections ();
> -
> -  datasecs.create (0);
>     vec_alloc (funcs, 16);
> +  func_map = hash_map<ctf_dtdef_ref, ctf_dtdef_ref>::create_ggc (16);
>   
> -  ctf_add_cuname (tu_ctfc, filename);
> +  btf_early_add_const_void (tu_ctfc);
> +  btf_early_add_func_records (tu_ctfc);
> +}
>   
> -  btf_emit_preprocess (tu_ctfc);
> +/* Push a BTF datasec entry ENTRY into the datasec named SECNAME,
> +   creating the datasec record if it does not already exist.  */
>   
> -  output_btf_header (tu_ctfc);
> -  output_btf_types (tu_ctfc);
> -  output_btf_vars (tu_ctfc);
> -  output_btf_func_types (tu_ctfc);
> -  output_btf_datasec_types (tu_ctfc);
> -  output_btf_strs (tu_ctfc);
> +static void
> +btf_datasec_push_entry (ctf_container_ref ctfc, const char *secname,
> +			struct btf_datasec_entry entry)
> +{
> +  if (secname == NULL)
> +    return;
> +
> +  /* If we already have a datasec record for the appropriate section,
> +     append the new entry to it.  */
> +  for (size_t i = 0; i < datasecs.length (); i++)
> +    if (strcmp (datasecs[i].name, secname) == 0)
> +      {
> +	datasecs[i].entries.safe_push (entry);
> +	return;
> +      }
> +
> +  /* If we don't already have a datasec record for secname, make one.  */
> +  uint32_t str_off;
> +  ctf_add_string (ctfc, secname, &str_off, CTF_AUX_STRTAB);
> +  if (strcmp (secname, ""))
> +    ctfc->ctfc_aux_strlen += strlen (secname) + 1;
> +
> +  /* Note: ID will be assigned just before output.  */
> +  btf_datasec_t ds;
> +  ds.name = secname;
> +  ds.name_offset = str_off;
> +
> +  /* Insert the entry into the new datasec record.  */
> +  ds.entries.create (1);
> +  ds.entries.quick_push (entry);
> +
> +  /* Insert the datasec record itself.  */
> +  datasecs.safe_push (ds);
>   }
>   
> -/* Reset all state for BTF generation so that we can rerun the compiler within
> -   the same process.  */
> +/* Create a datasec entry for a function, and insert it into the datasec
> +   record for the appropriate section.  Create the record if it does not
> +   yet exist.  */
>   
> -void
> -btf_finalize (void)
> +static void
> +btf_datasec_add_func (ctf_container_ref ctfc, cgraph_node *func,
> +		      ctf_dtdef_ref func_dtd)
>   {
> -  btf_info_section = NULL;
> +  const char *section_name = get_section_name (func);
>   
> -  /* Clear preprocessing state.  */
> -  num_vars_added = 0;
> -  num_types_added = 0;
> -  num_types_created = 0;
> +  /* Note: get_section_name () returns NULL for functions in text
> +     section.  This is intentional, since we do not want to generate
> +     DATASEC entries for them.  */
> +  if (section_name == NULL)
> +    return;
>   
> -  holes.release ();
> -  voids.release ();
> -  for (size_t i = 0; i < datasecs.length (); i++)
> -    datasecs[i].entries.release ();
> -  datasecs.release ();
> +  struct btf_datasec_entry entry;
> +  gcc_assert (func_dtd);
> +  entry.dtd = func_dtd;
> +  entry.is_var = false;
>   
> -  funcs = NULL;
> +  /* Size is left as zero at compile time, to be filled in by loaders
> +     such as libbpf.  */
> +  entry.size = 0;
>   
> -  btf_var_ids->empty ();
> -  btf_var_ids = NULL;
> +  btf_datasec_push_entry (ctfc, section_name, entry);
> +}
>   
> -  free (btf_id_map);
> -  btf_id_map = NULL;
> +/* Create a datasec entry for a variable, and insert it into the datasec
> +   record for the appropriate section.  Create the record if it does not
> +   yet exist.  */
>   
> -  ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
> -  ctfc_delete_container (tu_ctfc);
> -  tu_ctfc = NULL;
> +static void
> +btf_datasec_add_var (ctf_container_ref ctfc, varpool_node *var,
> +		     ctf_dvdef_ref dvd)
> +{
> +  /* PR112849: avoid assuming a section for extern decls without
> +     an explicit section, which would result in incorrectly
> +     emitting a BTF_KIND_DATASEC entry for them.  */
> +  if (DECL_EXTERNAL (var->decl) && var->get_section () == NULL)
> +    return;
> +
> +  const char *section_name = get_section_name (var);
> +  if (section_name == NULL)
> +    return;
> +
> +  gcc_assert (dvd);
> +  struct btf_datasec_entry entry;
> +  entry.dvd = dvd;
> +  entry.is_var = true;
> +  entry.size = 0;
> +
> +  tree size = DECL_SIZE_UNIT (var->decl);
> +  if (tree_fits_uhwi_p (size))
> +    entry.size = tree_to_uhwi (size);
> +  else if (VOID_TYPE_P (TREE_TYPE (var->decl)))
> +    entry.size = 1;
> +
> +  btf_datasec_push_entry (ctfc, section_name, entry);
>   }
>   
> -/* 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
> -   BTF_KIND_FUNC records.  */
> +/* Add datasec entries for functions to CTFC.  */
>   
> -void
> -btf_early_finish (void)
> +static void
> +btf_late_add_func_datasec_entries (ctf_container_ref ctfc)
> +{
> +  /* We need to create FUNC records at early_finish, so that we have them
> +     even for functions which are later inlined by optimization passes.
> +     But on the other hand, we do not want datasec entries for such functions,
> +     so only create the datasec entries for them late.  This loop will not
> +     hit functions which have already been inlined.  */
> +  cgraph_node *func;
> +  FOR_EACH_FUNCTION (func)
> +    {
> +      dw_die_ref die = lookup_decl_die (func->decl);
> +      if (die == NULL)
> +	continue;
> +
> +      ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, die);
> +      if (dtd == NULL)
> +	continue;
> +
> +      ctf_dtdef_ref *pdtd = func_map->get (dtd);
> +      if (pdtd && DECL_EXTERNAL (func->decl))
> +	btf_datasec_add_func (ctfc, func, *pdtd);
> +    }
> +}
> +
> +/* Helper function used to determine whether or not a BTF_KIND_VAR record
> +   for the variable VAR shall be emitted.  */
> +
> +static bool
> +btf_emit_variable_p (ctf_container_ref ctfc, varpool_node *var,
> +		     ctf_dvdef_ref *pdvd)
> +{
> +  dw_die_ref die = lookup_decl_die (var->decl);
> +  if (die == NULL)
> +    return false;
> +
> +  ctf_dvdef_ref dvd = ctf_dvd_lookup (ctfc, die);
> +  if (dvd == NULL)
> +    return false;
> +
> +  /* If this is an extern variable declaration with a defining declaration
> +     later, skip it so that only the defining declaration is emitted.
> +     This is the same case, fix and reasoning as in CTF; see PR105089.  */
> +  if (ctf_dvd_ignore_lookup (ctfc, dvd->dvd_key))
> +    return false;
> +
> +  /* Skip variables with unrepresentable types.  */
> +  if (!btf_emit_type_p (dvd->dvd_type))
> +    return false;
> +
> +  *pdvd = dvd;
> +  return true;
> +}
> +
> +/* Add BTF_KIND_VAR records for variables.  */
> +
> +static void
> +btf_late_add_vars (ctf_container_ref ctfc)
>   {
> -  btf_init_postprocess ();
> +  size_t num_ctf_vars = ctfc->ctfc_vars->elements ();
> +
> +  ctfc->ctfc_vars_list = ggc_vec_alloc<ctf_dvdef_ref>(num_ctf_vars);
> +
> +  varpool_node *var;
> +  ctf_dvdef_ref dvd;
> +  FOR_EACH_VARIABLE (var)
> +    {
> +      if (!btf_emit_variable_p (ctfc, var, &dvd))
> +	continue;
> +
> +      /* Mark 'extern' variables.  */
> +      if (DECL_EXTERNAL (var->decl))
> +	dvd->dvd_visibility = BTF_VAR_GLOBAL_EXTERN;
> +
> +      /* Add the variable to the vars list.  */
> +      ctfc->ctfc_vars_list[ctfc->ctfc_vars_list_count++] = dvd;
> +
> +      /* Add a BTF_KIND_DATASEC entry for the variable.  */
> +      btf_datasec_add_var (ctfc, var, dvd);
> +    }
> +}
> +
> +/* Callback used by btf_late_assign_type_ids to insert types into their initial
> +   positions in the type list.  */
> +
> +static int
> +btf_type_list_cb (ctf_dtdef_ref *slot, ctf_container_ref ctfc)
> +{
> +  ctf_dtdef_ref dtd = *slot;
> +  ctfc->ctfc_types_list[dtd->dtd_type] = dtd;
> +  return 1;
> +}
> +
> +/* Construct the initial type list and assign BTF IDs for all types translated
> +   from CTF.  */
> +
> +static void
> +btf_late_collect_translated_types (ctf_container_ref ctfc)
> +{
> +  size_t num_ctf_types = ctfc->ctfc_types->elements ();
> +
> +  /* First, place each type at its CTF-assigned index in the list.
> +     The '+1' here and below is to account for the implicit void type with
> +     ID 0.  There is no real type at index 0 in the list.  */
> +  ctfc->ctfc_types_list = ggc_vec_alloc<ctf_dtdef_ref>(num_ctf_types + 1);
> +  ctfc->ctfc_types->traverse<ctf_container_ref, btf_type_list_cb> (ctfc);
> +
> +  /* Now, pass through the list and adjust IDs to account for types which will
> +     not be emitted.  This results in each type that will be emitted in BTF
> +     being assigned an appropriate ID.  Note that types which will not be
> +     emitted remain in the list; they are skipped at output time.  */
> +  unsigned int skip = 0;
> +  for (size_t i = 1; i <= num_ctf_types; i++)
> +    {
> +      ctf_dtdef_ref dtd = ctfc->ctfc_types_list[i];
> +      if (!btf_emit_type_p (dtd))
> +	{
> +	  dtd->dtd_type = BTF_INVALID_TYPEID;
> +	  skip += 1;
> +	  continue;
> +	}
> +
> +      dtd->dtd_type -= skip;
> +      ctfc->ctfc_num_types++;
> +      ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (dtd);
> +    }
> +
> +  max_translated_id = ctfc->ctfc_num_types;
> +  ctfc->ctfc_nextid = ctfc->ctfc_num_types + 1;
> +}
> +
> +/* Assign BTF IDs for FUNC records and account for their size.  */
> +
> +static void
> +btf_late_assign_func_ids (ctf_container_ref ctfc)
> +{
> +  ctf_dtdef_ref dtd;
> +  unsigned int i;
> +  FOR_EACH_VEC_ELT (*funcs, i, dtd)
> +    {
> +      dtd->dtd_type = ctfc->ctfc_nextid++;
> +      ctfc->ctfc_num_types++;
> +    }
> +}
> +
> +/* Assign BTF IDs for variables and account for their size.  */
> +
> +static void
> +btf_late_assign_var_ids (ctf_container_ref ctfc)
> +{
> +  for (size_t i = 0; i < ctfc->ctfc_vars_list_count; i++)
> +    {
> +      ctf_dvdef_ref dvd = ctfc->ctfc_vars_list[i];
> +      ctf_id_t id = ctfc->ctfc_nextid++;
> +      gcc_assert (id <= BTF_MAX_TYPE);
> +      dvd->dvd_id = id;
> +
> +      ctfc->ctfc_num_types++;
> +      ctfc->ctfc_num_vlen_bytes += sizeof (struct btf_var);
> +    }
> +}
> +
> +/* Assign BTF IDs for datasec records and account for their size.  */
> +
> +static void
> +btf_late_assign_datasec_ids (ctf_container_ref ctfc)
> +{
> +  for (size_t i = 0; i < datasecs.length (); i++)
> +    {
> +      datasecs[i].id = ctfc->ctfc_nextid++;
> +      datasecs[i].name_offset += ctfc_get_strtab_len (ctfc, CTF_STRTAB);
> +      ctfc->ctfc_num_types++;
> +      ctfc->ctfc_num_vlen_bytes += (datasecs[i].entries.length ()
> +				    * sizeof (struct btf_var_secinfo));
> +    }
>   }
>   
>   /* Late entry point for BTF generation, called from dwarf2out_finish ().
>      Complete and emit BTF information.  */
>   
>   void
> -btf_finish (const char * filename)
> +btf_finish (void)
>   {
> -  btf_output (filename);
> +  ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
> +  init_btf_sections ();
> +
> +  datasecs.create (0);
> +
> +  tu_ctfc->ctfc_num_types = 0;
> +  tu_ctfc->ctfc_num_vlen_bytes = 0;
> +  tu_ctfc->ctfc_vars_list_count = 0;
> +
> +  btf_late_add_vars (tu_ctfc);
> +  btf_late_collect_translated_types (tu_ctfc);
> +  btf_late_add_func_datasec_entries (tu_ctfc);
> +  btf_late_assign_var_ids (tu_ctfc);
> +  btf_late_assign_func_ids (tu_ctfc);
> +  btf_late_assign_datasec_ids (tu_ctfc);
> +

The "late" keyword in the function names is somewhat confusing. 
btf_finish () is also be called from early_finish () (this will happen 
with LTO builds in this patch series).

> +  /* Finally, write out the complete .BTF section.  */
> +  btf_output (tu_ctfc);
>   
>     /* If compiling for BPF with CO-RE info, we cannot deallocate until after
>        CO-RE information is created, which happens very late in BPF backend.
> @@ -1516,6 +1285,27 @@ btf_finish (const char * filename)
>       btf_finalize ();
>   }
>   
> +/* Reset all state for BTF generation so that we can rerun the compiler within
> +   the same process.  */
> +
> +void
> +btf_finalize (void)
> +{
> +  btf_info_section = NULL;
> +  max_translated_id = 0;
> +
> +  for (size_t i = 0; i < datasecs.length (); i++)
> +    datasecs[i].entries.release ();
> +  datasecs.release ();
> +
> +  funcs = NULL;
> +  func_map->empty ();
> +  func_map = NULL;
> +
> +  ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
> +  ctfc_delete_container (tu_ctfc);
> +  tu_ctfc = NULL;
> +}
>   
>   /* Traversal function for all BTF_KIND_FUNC type records.  */
>   
> diff --git a/gcc/ctfc.h b/gcc/ctfc.h
> index b2060eaf7e9b..d0b724817a7f 100644
> --- a/gcc/ctfc.h
> +++ b/gcc/ctfc.h
> @@ -164,10 +164,14 @@ struct GTY ((for_user)) ctf_dtdef
>     ctf_id_t dtd_type;	      /* Type identifier for this definition.  */
>     ctf_dtdef_ref ref_type;     /* Type referred to by this type (if any).  */
>     ctf_itype_t dtd_data;	      /* Type node.  */
> -  bool from_global_func; /* Whether this type was added from a global
> -			    function.  */
>     uint32_t linkage;           /* Used in function types.  0=local, 1=global.  */
> -  bool dtd_enum_unsigned;     /* Enum signedness.  */
> +
> +  /* Whether this type was added from a global function.  */
> +  BOOL_BITFIELD from_global_func : 1;
> +  /* Enum signedness.  */
> +  BOOL_BITFIELD dtd_enum_unsigned : 1;
> +  /* Lots of spare bits.  */
> +
>     union GTY ((desc ("ctf_dtu_d_union_selector (&%1)")))
>     {
>       /* struct, union, or enum.  */
> @@ -194,6 +198,7 @@ struct GTY ((for_user)) ctf_dvdef
>     uint32_t dvd_name_offset;	/* Offset of the name in str table.  */
>     unsigned int dvd_visibility;	/* External visibility.  0=static,1=global.  */
>     ctf_dtdef_ref dvd_type;	/* Type of variable.  */
> +  ctf_id_t dvd_id;		/* ID of this variable.  Only used for BTF.  */
>   };
>   
>   typedef struct ctf_dvdef ctf_dvdef_t;
> @@ -388,7 +393,7 @@ extern void ctf_output (const char * filename);
>   extern void ctf_finalize (void);
>   
>   extern void btf_early_finish (void);
> -extern void btf_finish (const char * filename);
> +extern void btf_finish (void);
>   extern void btf_finalize (void);
>   
>   extern ctf_container_ref ctf_get_tu_ctfc (void);
> @@ -443,7 +448,9 @@ extern int 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);
> -extern ctf_id_t get_btf_id (ctf_id_t);
> +
> +/* Callback and traversal function for BTF_KIND_FUNC records.  Used by BPF
> +   target for BPF CO-RE implementation.  */
>   
>   typedef bool (*funcs_traverse_callback) (ctf_dtdef_ref, void *);
>   bool traverse_btf_func_types (funcs_traverse_callback, void *);
> diff --git a/gcc/dwarf2ctf.cc b/gcc/dwarf2ctf.cc
> index a6c8541ef2b4..910f523f9c5c 100644
> --- a/gcc/dwarf2ctf.cc
> +++ b/gcc/dwarf2ctf.cc
> @@ -976,7 +976,7 @@ ctf_debug_early_finish (const char * filename)
>   
>         /* For LTO builds, also emit BTF now.  */
>         if (flag_lto && !in_lto_p)
> -	btf_finish (filename);
> +	btf_finish ();
>       }
>     else
>       /* Otherwise, done with the CTF container.  */
> @@ -986,12 +986,12 @@ ctf_debug_early_finish (const char * filename)
>   /* Finish CTF/BTF debug info emission.  */
>   
>   void
> -ctf_debug_finish (const char * filename)
> +ctf_debug_finish ()
>   {
>     /* Emit BTF late, unless this is an LTO build in which case it was
>        already done early.  */
>     if (btf_debuginfo_p () && !flag_lto)
> -    btf_finish (filename);
> +    btf_finish ();
>   }
>   
>   #include "gt-dwarf2ctf.h"
> diff --git a/gcc/dwarf2ctf.h b/gcc/dwarf2ctf.h
> index 46184325bae6..f8a181a97625 100644
> --- a/gcc/dwarf2ctf.h
> +++ b/gcc/dwarf2ctf.h
> @@ -32,7 +32,7 @@ extern void ctf_debug_init (void);
>   extern void ctf_debug_init_postprocess (bool);
>   extern bool ctf_do_die (dw_die_ref);
>   extern void ctf_debug_early_finish (const char *);
> -extern void ctf_debug_finish (const char *);
> +extern void ctf_debug_finish (void);
>   
>   /* Wrappers for CTF/BTF to fetch information from GCC DWARF DIE.  Used in
>      ctfc.cc.
> diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
> index e406df8673ce..47e73629e3aa 100644
> --- a/gcc/dwarf2out.cc
> +++ b/gcc/dwarf2out.cc
> @@ -32305,7 +32305,7 @@ dwarf2out_finish (const char *filename)
>     /* Generate CTF/BTF debug info.  */
>     if ((ctf_debug_info_level > CTFINFO_LEVEL_NONE
>          || btf_debuginfo_p ()) && lang_GNU_C ())
> -    ctf_debug_finish (filename);
> +    ctf_debug_finish ();
>   
>   #ifdef CODEVIEW_DEBUGGING_INFO
>     if (codeview_debuginfo_p ())


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

* Re: [PATCH v3 4/6] btf: add -fprune-btf option
  2024-05-30 21:32 ` [PATCH v3 4/6] btf: add -fprune-btf option David Faust
  2024-05-31  7:07   ` Richard Biener
@ 2024-06-05  7:56   ` Indu Bhagat
  1 sibling, 0 replies; 16+ messages in thread
From: Indu Bhagat @ 2024-06-05  7:56 UTC (permalink / raw)
  To: David Faust, gcc-patches; +Cc: jose.marchesi, cupertino.miranda

On 5/30/24 14:32, David Faust wrote:
> This patch adds a new option, -fprune-btf, to control BTF debug info
> generation.
> 
> As the name implies, this option enables a kind of "pruning" of the BTF
> information before it is emitted.  When enabled, rather than emitting
> all type information translated from DWARF, only information for types
> directly used in the source program is emitted.
> 
> The primary purpose of this pruning is to reduce the amount of
> unnecessary BTF information emitted, especially for BPF programs.  It is
> very common for BPF programs to incldue Linux kernel internal headers in

typo: incldue

> order to have access to kernel data structures.  However, doing so often
> has the side effect of also adding type definitions for a large number
> of types which are not actually used by nor relevant to the program.
> In these cases, -fprune-btf commonly reduces the size of the resulting
> BTF information by 10x or more, as seen on average when compiling Linux
> kernel BPF selftests.  This both slims down the size of the resulting
> object and reduces the time required by the BPF loader to verify the
> program and its BTF information.
> 
> Note that the pruning implemented in this patch follows the same rules
> as the BTF pruning performed unconditionally by LLVM's BPF backend when
> generating BTF.  In particular, the main sources of pruning are:
> 
>    1) Only generate BTF for types used by variables and functions at
>       the file scope.  Note that with or without pruning, BTF_KIND_VAR
>       entries are only generated for variables present in the final
>       object - unused static variables or variables completely optimized
>       away must not have VAR entries in BTF.
> 

This needs adjusting. I think you mean to convey that the above is 
expected behavior of -fprune-btf (-gprune-btf) for BPF backend.  But the 
option as such is also available for non-BPF backends, where its 
behavior will not be the one stated here (due to BTF creation, pruning 
and output work being at the time of early_finish () when LTO is enabled)

Perhaps we clearly specify the behavior of -fprune-btf for BPF and 
non-BPF backends ? I wonder if the right approach is to instead disallow 
-fprune-btf  with -flto, however until it is clear what that effectively 
means.

One nit below.

>    2) Avoid emitting full BTF for struct and union types which are only
>       pointed-to by members of other struct/union types.  In these cases,
>       the full BTF_KIND_STRUCT or BTF_KIND_UNION which would normally
>       be emitted is replaced with a BTF_KIND_FWD, as though the
>       underlying type was a forward-declared struct or union type.
> 
> gcc/
> 	* btfout.cc (btf_used_types): New hash set.
> 	(struct btf_fixup): New.
> 	(fixups, forwards): New vecs.
> 	(btf_output): Calculate num_types depending on flag_prune_btf.
> 	(btf_early_finsih): New initialization for flag_prune_btf.
> 	(btf_add_used_type): New function.
> 	(btf_used_type_list_cb): Likewise.
> 	(btf_late_collect_pruned_types): Likewise.
> 	(btf_late_add_vars): Handle special case for variables in ".maps"
> 	section when generating BTF for BPF CO-RE target.
> 	(btf_late_finish): Use btf_late_collect_pruned_types when
> 	flag_prune_btf in effect.  Move some initialization to btf_early_finish.
> 	(btf_finalize): Additional deallocation for flag_prune_btf.
> 	* common.opt (fprune-btf): New flag.
> 	* ctfc.cc (init_ctf_strtable): Make non-static.
> 	* ctfc.h (struct ctf_dtdef): Add visited_children_p boolean flag.
> 	(init_ctf_strtable, ctfc_delete_strtab): Make extern.
> 	* doc/invoke.texi (Debugging Options): Document -fprune-btf.
> 
> gcc/testsuite/
> 	* gcc.dg/debug/btf/btf-prune-1.c: New test.
> 	* gcc.dg/debug/btf/btf-prune-2.c: Likewise.
> 	* gcc.dg/debug/btf/btf-prune-3.c: Likewise.
> 	* gcc.dg/debug/btf/btf-prune-maps.c: Likewise.
> ---
>   gcc/btfout.cc                                 | 359 +++++++++++++++++-
>   gcc/common.opt                                |   4 +
>   gcc/ctfc.cc                                   |   2 +-
>   gcc/ctfc.h                                    |   3 +
>   gcc/doc/invoke.texi                           |  20 +
>   gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c  |  25 ++
>   gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c  |  33 ++
>   gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c  |  35 ++
>   .../gcc.dg/debug/btf/btf-prune-maps.c         |  20 +
>   9 files changed, 494 insertions(+), 7 deletions(-)
>   create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c
>   create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c
>   create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c
>   create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c
> 
> diff --git a/gcc/btfout.cc b/gcc/btfout.cc
> index 32fda14f704b..a7da164f6b31 100644
> --- a/gcc/btfout.cc
> +++ b/gcc/btfout.cc
> @@ -828,7 +828,10 @@ output_btf_types (ctf_container_ref ctfc)
>   {
>     size_t i;
>     size_t num_types;
> -  num_types = ctfc->ctfc_types->elements ();
> +  if (flag_prune_btf)
> +    num_types = max_translated_id;
> +  else
> +    num_types = ctfc->ctfc_types->elements ();
>   
>     if (num_types)
>       {
> @@ -957,6 +960,212 @@ btf_early_add_func_records (ctf_container_ref ctfc)
>       }
>   }
>   
> +/* The set of types used directly in the source program, and any types manually
> +   marked as used.  This is the set of types which will be emitted when
> +   flag_prune_btf is set.  */
> +static GTY (()) hash_set<ctf_dtdef_ref> *btf_used_types;
> +
> +/* Fixup used to avoid unnecessary pointer chasing for types.  A fixup is
> +   created when a structure or union member is a pointer to another struct
> +   or union type.  In such cases, avoid emitting full type information for
> +   the pointee struct or union type (which may be quite large), unless that
> +   type is used directly elsewhere.  */
> +struct btf_fixup
> +{
> +  ctf_dtdef_ref pointer_dtd; /* Type node to which the fixup is applied.  */
> +  ctf_dtdef_ref pointee_dtd; /* Original type node referred to by pointer_dtd.
> +				If this concrete type is not otherwise used,
> +				then a forward is created.  */
> +};
> +
> +/* Stores fixups while processing types.  */
> +static vec<struct btf_fixup> fixups;
> +
> +/* For fixups where the underlying type is not used in the end, a BTF_KIND_FWD
> +   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.
> +   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
> +   or union member.  SEEN_PTR is true if CHECK_PTR is true AND one of the
> +   predecessors was a pointer type.  These two flags are used to avoid chasing
> +   pointers to struct/union only used from pointer members.  For such types, we
> +   will emit a forward instead of the full type information, unless
> +   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)
> +{
> +  if (dtd == NULL)
> +    return NULL;
> +
> +  uint32_t ctf_kind = CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info);
> +  uint32_t kind = get_btf_kind (ctf_kind);
> +
> +  /* Check whether the type has already been added.  */
> +  if (btf_used_types->contains (dtd))
> +    {
> +      /* It's possible the type was already added as a fixup, but that we now
> +	 have a concrete use of it.  */
> +      switch (kind)
> +	{
> +	case BTF_KIND_PTR:
> +	case BTF_KIND_TYPEDEF:
> +	case BTF_KIND_CONST:
> +	case BTF_KIND_VOLATILE:
> +	case BTF_KIND_RESTRICT:
> +	  if (check_ptr)
> +	    /* Type was previously added as a fixup, and that's OK.  */
> +	    return dtd;
> +	  else
> +	    {
> +	      /* The type was previously added as a fixup, but now we have
> +		 a concrete use of it.  Remove the fixup.  */
> +	      for (size_t i = 0; i < fixups.length (); i++)
> +		if (fixups[i].pointer_dtd == 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);
> +	      return dtd;
> +	    }
> +	default:
> +	  return dtd;
> +	}
> +    }
> +
> +  if (ctf_kind == CTF_K_SLICE)
> +    {
> +      /* Bitfield.  Add the underlying type to the used set, but leave
> +	 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);
> +      return dtd;
> +    }
> +
> +  /* Skip redundant definitions of void and types with no BTF encoding.  */
> +  if ((kind == BTF_KIND_INT && dtd->dtd_data.ctti_size == 0)
> +      || (kind == BTF_KIND_UNKN))
> +    return NULL;
> +
> +  /* Add the type itself, and assign its id.
> +     Do this before recursing to handle things like linked list structures.  */
> +  gcc_assert (ctfc->ctfc_nextid <= BTF_MAX_TYPE);
> +  dtd->dtd_type = ctfc->ctfc_nextid++;
> +  btf_used_types->add (dtd);
> +  ctf_add_string (ctfc, dtd->dtd_name, &(dtd->dtd_data.ctti_name), CTF_STRTAB);
> +  ctfc->ctfc_num_types++;
> +  ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (dtd);
> +
> +  /* Recursively add types referenced by this type.  */
> +  switch (kind)
> +    {
> +    case BTF_KIND_INT:
> +    case BTF_KIND_FLOAT:
> +    case BTF_KIND_FWD:
> +      /* Leaf kinds which do not refer to any other types.  */
> +      break;
> +
> +    case BTF_KIND_FUNC:
> +    case BTF_KIND_VAR:
> +      /* Root kinds; no type we are visiting may refer to these.  */
> +      gcc_unreachable ();
> +
> +    case BTF_KIND_PTR:
> +    case BTF_KIND_TYPEDEF:
> +    case BTF_KIND_CONST:
> +    case BTF_KIND_VOLATILE:
> +    case BTF_KIND_RESTRICT:
> +      {
> +	/* These type kinds refer to exactly one other type.  */
> +	if (check_ptr && !seen_ptr)
> +	  seen_ptr = (kind == BTF_KIND_PTR);
> +
> +	/* Try to avoid chasing pointers to struct/union types if the
> +	   underlying type isn't used.  */
> +	if (check_ptr && seen_ptr && create_fixups)
> +	  {
> +	    ctf_dtdef_ref ref = dtd->ref_type;
> +	    uint32_t ref_kind = btf_dtd_kind (ref);
> +
> +	    if ((ref_kind == BTF_KIND_STRUCT || ref_kind == BTF_KIND_UNION)
> +		&& !btf_used_types->contains (ref))
> +	      {
> +		struct btf_fixup fixup;
> +		fixup.pointer_dtd = dtd;
> +		fixup.pointee_dtd = ref;
> +		fixups.safe_push (fixup);
> +		break;
> +	      }
> +	  }
> +
> +	/* 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);
> +	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);
> +	break;
> +      }
> +    case BTF_KIND_STRUCT:
> +    case BTF_KIND_UNION:
> +    case BTF_KIND_ENUM:
> +    case BTF_KIND_ENUM64:
> +      {
> +	/* Add members.  */
> +	ctf_dmdef_t *dmd;
> +	for (dmd = dtd->dtd_u.dtu_members;
> +	     dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
> +	  {
> +	    /* 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);
> +	    ctf_add_string (ctfc, dmd->dmd_name, &(dmd->dmd_name_offset),
> +			    CTF_STRTAB);
> +	  }
> +	break;
> +      }
> +    case BTF_KIND_FUNC_PROTO:
> +      {
> +	/* Add return type.  */
> +	dtd->ref_type = btf_add_used_type (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);
> +	    /* 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.  */
> +	  }
> +	break;
> +      }
> +    default:
> +      return NULL;
> +    }
> +
> +  return dtd;
> +}
> +
>   /* 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
> @@ -972,6 +1181,27 @@ btf_early_finish (void)
>   
>     btf_early_add_const_void (tu_ctfc);
>     btf_early_add_func_records (tu_ctfc);
> +
> +  /* Note: from here on, destructive changes are made to the TU CTFC to
> +     translate CTF to BTF.  These fields are reset to count BTF types etc.  */
> +  tu_ctfc->ctfc_num_types = 0;
> +  tu_ctfc->ctfc_num_vlen_bytes = 0;
> +  tu_ctfc->ctfc_vars_list_count = 0;
> +
> +  if (flag_prune_btf)
> +    {
> +      btf_used_types
> +	= hash_set<ctf_dtdef_ref>::create_ggc (tu_ctfc->ctfc_types->elements ());
> +      tu_ctfc->ctfc_nextid = 1;
> +      fixups.create (1);
> +
> +      /* Empty the string table, which was already populated with strings for
> +	 all types translated from DWARF.  We may only need a very small subset
> +	 of these strings; those will be re-added below.  */
> +      ctfc_delete_strtab (&tu_ctfc->ctfc_strtable);
> +      init_ctf_strtable (&tu_ctfc->ctfc_strtable);
> +      tu_ctfc->ctfc_strlen++;
> +    }
>   }
>   
>   /* Push a BTF datasec entry ENTRY into the datasec named SECNAME,
> @@ -1154,6 +1384,25 @@ btf_late_add_vars (ctf_container_ref ctfc)
>   
>         /* Add a BTF_KIND_DATASEC entry for the variable.  */
>         btf_datasec_add_var (ctfc, var, dvd);
> +
> +      const char *section = var->get_section ();
> +      if (section && (strcmp (section, ".maps") == 0) && flag_prune_btf)
> +	{
> +	  /* The .maps section has special meaning in BTF: it is used for BPF
> +	     map definitions.  These definitions should be structs.  We must
> +	     collect pointee types used in map members as though they are used
> +	     directly, effectively ignoring (from the pruning perspective) that
> +	     they are struct members.  */
> +	  ctf_dtdef_ref dtd = dvd->dvd_type;
> +	  uint32_t kind = btf_dtd_kind (dvd->dvd_type);
> +	  if (kind == BTF_KIND_STRUCT)
> +	    {
> +	      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);
> +	    }
> +	}
>       }
>   }
>   
> @@ -1252,6 +1501,86 @@ btf_late_assign_datasec_ids (ctf_container_ref ctfc)
>       }
>   }
>   
> +/* Callback used for assembling the only-used-types list.  Note that this is
> +   the same as btf_type_list_cb above, but the hash_set traverse requires a
> +   different function signature.  */
> +
> +static bool
> +btf_used_type_list_cb (const ctf_dtdef_ref& dtd, ctf_container_ref ctfc)
> +{
> +  ctfc->ctfc_types_list[dtd->dtd_type] = dtd;
> +  return true;
> +}
> +
> +/* Collect the set of types reachable from global variables and functions.
> +   This is the minimal set of types, used when generating pruned BTF.  */
> +
> +static void
> +btf_late_collect_pruned_types (ctf_container_ref ctfc)
> +{
> +  vec_alloc (forwards, 1);
> +
> +  /* Add types used from functions.  */
> +  ctf_dtdef_ref dtd;
> +  size_t i;
> +  FOR_EACH_VEC_ELT (*funcs, i, dtd)
> +    {
> +      btf_add_used_type (ctfc, dtd->ref_type, false, false, true);
> +      ctf_add_string (ctfc, dtd->dtd_name, &(dtd->dtd_data.ctti_name),
> +		      CTF_STRTAB);
> +    }
> +
> +  /* Add types used from global variables.  */
> +  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);
> +      ctf_add_string (ctfc, dvd->dvd_name, &(dvd->dvd_name_offset), CTF_STRTAB);
> +    }
> +
> +  /* 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.  */
> +  for (i = 0; i < fixups.length (); i++)
> +    {
> +      struct btf_fixup *fx = &fixups[i];
> +      if (!btf_used_types->contains (fx->pointee_dtd))
> +	{
> +	  /* The underlying type is not used.  Create a forward.  */
> +	  ctf_dtdef_ref fwd = ggc_cleared_alloc<ctf_dtdef_t> ();
> +	  ctf_id_t id = ctfc->ctfc_nextid++;
> +	  gcc_assert (id <= BTF_MAX_TYPE);
> +
> +	  bool union_p = (btf_dtd_kind (fx->pointee_dtd) == BTF_KIND_UNION);
> +
> +	  fwd->dtd_name = fx->pointee_dtd->dtd_name;
> +	  fwd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_FORWARD, union_p, 0);
> +	  fwd->dtd_type = id;
> +	  ctfc->ctfc_num_types++;
> +	  ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (fwd);
> +	  ctf_add_string (ctfc, fwd->dtd_name, &(fwd->dtd_data.ctti_name),
> +			  CTF_STRTAB);
> +
> +	  /* Update the pointer to point to the forward.  */
> +	  fx->pointer_dtd->ref_type = fwd;
> +	  vec_safe_push (forwards, fwd);
> +	}
> +    }
> +
> +  /* Construct the resulting pruned type list.  */
> +  ctfc->ctfc_types_list
> +    = ggc_vec_alloc<ctf_dtdef_ref> (btf_used_types->elements () + 1
> +				    + vec_safe_length (forwards));
> +
> +  btf_used_types->traverse<ctf_container_ref, btf_used_type_list_cb> (ctfc);
> +
> +  /* Insert the newly created forwards into the regular types list too.  */
> +  FOR_EACH_VEC_ELT (*forwards, i, dtd)
> +    ctfc->ctfc_types_list[dtd->dtd_type] = dtd;
> +
> +  max_translated_id = btf_used_types->elements () + vec_safe_length (forwards);
> +}
> +
>   /* Late entry point for BTF generation, called from dwarf2out_finish ().
>      Complete and emit BTF information.  */
>   
> @@ -1263,13 +1592,22 @@ btf_finish (void)
>   
>     datasecs.create (0);
>   
> -  tu_ctfc->ctfc_num_types = 0;
> -  tu_ctfc->ctfc_num_vlen_bytes = 0;
> -  tu_ctfc->ctfc_vars_list_count = 0;
> -
>     btf_late_add_vars (tu_ctfc);
> -  btf_late_collect_translated_types (tu_ctfc);
> +  if (flag_prune_btf)
> +    {
> +      /* Collect pruned set of BTF types and prepare for emission.
> +	 This includes only types directly used in file-scope variables and
> +	 function return/argument types.  */
> +      btf_late_collect_pruned_types (tu_ctfc);
> +    }
> +  else
> +    {
> +      /* Collect all BTF types and prepare for emission.
> +	 This includes all types translated from DWARF.  */
> +      btf_late_collect_translated_types (tu_ctfc);
> +    }
>     btf_late_add_func_datasec_entries (tu_ctfc);
> +
>     btf_late_assign_var_ids (tu_ctfc);
>     btf_late_assign_func_ids (tu_ctfc);
>     btf_late_assign_datasec_ids (tu_ctfc);
> @@ -1302,6 +1640,15 @@ btf_finalize (void)
>     func_map->empty ();
>     func_map = NULL;
>   
> +  if (flag_prune_btf)
> +    {
> +      btf_used_types->empty ();
> +      btf_used_types = NULL;
> +
> +      fixups.release ();
> +      forwards = NULL;
> +    }
> +
>     ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
>     ctfc_delete_container (tu_ctfc);
>     tu_ctfc = NULL;
> diff --git a/gcc/common.opt b/gcc/common.opt
> index 2c078fdd1f86..3d4a55d02c26 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -2588,6 +2588,10 @@ fpatchable-function-entry=
>   Common Var(flag_patchable_function_entry) Joined Optimization
>   Insert NOP instructions at each function entry.
>   
> +fprune-btf
> +Common Var(flag_prune_btf) Init(0)
> +Generate minimal BTF debug info.
> +

Should we switch this to "Generate pruned BTF debug info" ?

Although saying "Generate pruned BTF debug info" is not informative, I 
find saying "Generate minimal BTF debug info" somewhat ambiguous. Hmm, 
may be I am overthinking here :)

>   frandom-seed
>   Common Var(common_deferred_options) Defer
>   
> diff --git a/gcc/ctfc.cc b/gcc/ctfc.cc
> index 678ca2a072cf..27fc3a6d5851 100644
> --- a/gcc/ctfc.cc
> +++ b/gcc/ctfc.cc
> @@ -913,7 +913,7 @@ ctfc_get_dvd_srcloc (ctf_dvdef_ref dvd, ctf_srcloc_ref loc)
>   /* Initialize the CTF string table.
>      The first entry in the CTF string table (empty string) is added.  */
>   
> -static void
> +void
>   init_ctf_strtable (ctf_strtable_t * strtab)
>   {
>     strtab->ctstab_head = NULL;
> diff --git a/gcc/ctfc.h b/gcc/ctfc.h
> index d0b724817a7f..29267dc036d1 100644
> --- a/gcc/ctfc.h
> +++ b/gcc/ctfc.h
> @@ -369,6 +369,9 @@ extern unsigned int ctfc_get_num_ctf_vars (ctf_container_ref);
>   
>   extern ctf_strtable_t * ctfc_get_strtab (ctf_container_ref, int);
>   
> +extern void init_ctf_strtable (ctf_strtable_t *);
> +extern void ctfc_delete_strtab (ctf_strtable_t *);
> +
>   /* Get the length of the specified string table in the CTF container.  */
>   
>   extern size_t ctfc_get_strtab_len (ctf_container_ref, int);
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 517a782987d9..6dac00d14381 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -534,6 +534,7 @@ Objective-C and Objective-C++ Dialects}.
>   -gvms -gz@r{[}=@var{type}@r{]}
>   -gsplit-dwarf  -gdescribe-dies  -gno-describe-dies
>   -fdebug-prefix-map=@var{old}=@var{new}  -fdebug-types-section
> +-fprune-btf -fno-prune-btf
>   -fno-eliminate-unused-debug-types
>   -femit-struct-debug-baseonly  -femit-struct-debug-reduced
>   -femit-struct-debug-detailed@r{[}=@var{spec-list}@r{]}
> @@ -12292,6 +12293,25 @@ compressed debug sections, the option is rejected.  Otherwise, if the
>   assembler does not support them, @option{-gz} is silently ignored when
>   producing object files.
>   
> +@opindex fprune-btf
> +@opindex fno-prune-btf
> +@item -fprune-btf
> +@itemx -fno-prune-btf
> +Prune BTF information before emission.  When pruning, only type
> +information for types used by global variables and file-scope functions
> +will be emitted.  If compiling for the BPF target with BPF CO-RE
> +enabled, type information will also be emitted for types used in BPF
> +CO-RE relocations.  In addition, struct and union types which are only
> +referred to via pointers from members of other struct or union types
> +shall be pruned and replaced with BTF_KIND_FWD, as though those types
> +were only present in the input as forward declarations.
> +
> +This option substantially reduces the size of produced BTF information,
> +but at significant loss in the amount of detailed type information.
> +It is primarily useful when compiling for the BPF target, to minimize
> +the size of the resulting object, and to eliminate BTF information
> +which is not immediately relevant to the BPF program loading process.
> +
>   @opindex femit-struct-debug-baseonly
>   @item -femit-struct-debug-baseonly
>   Emit debug information for struct-like types
> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c
> new file mode 100644
> index 000000000000..3c9b59a07ecf
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-1.c
> @@ -0,0 +1,25 @@
> +/* Simple test of -fprune-btf option operation.
> +   Since 'struct foo' is not used, no BTF shall be emitted for it.  */
> +
> +/* { dg-do compile } */
> +/* { dg-options "-gbtf -fprune-btf -dA" } */
> +
> +/* No BTF info for 'struct foo' nor types used only by it.  */
> +/* { dg-final { scan-assembler-not "BTF_KIND_STRUCT 'foo'" } } */
> +/* { dg-final { scan-assembler-not "BTF_KIND_INT 'char'" } } */
> +
> +/* We should get BTF info for 'struct bar' since it is used.  */
> +/* { dg-final { scan-assembler "BTF_KIND_STRUCT 'bar'"} } */
> +
> +struct foo {
> +  int a;
> +  char c;
> +};
> +
> +struct bar {
> +  int x;
> +  long z[4];
> +};
> +
> +struct bar a_bar;
> +
> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c
> new file mode 100644
> index 000000000000..20183dffcc7f
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-2.c
> @@ -0,0 +1,33 @@
> +/* Test that -fprune-btf does not chase pointer-to-struct members.  */
> +
> +/* { dg-do compile } */
> +/* { dg-options "-gbtf -fprune-btf -dA" } */
> +
> +/* Only use of B is via a pointer member of C.
> +   Full BTF for B is replaced with a forward.  */
> +/* { dg-final { scan-assembler-not "BTF_KIND_STRUCT 'B'" } } */
> +/* { dg-final { scan-assembler-times "TYPE \[0-9\]+ BTF_KIND_FWD 'B'" 1 } } */
> +
> +/* Detailed info for B is omitted, and A is otherwise unused.  */
> +/* { dg-final { scan-assembler-not "BTF_KIND_\[A-Z\]+ 'A'" } } */
> +
> +/* { dg-final { scan-assembler "BTF_KIND_STRUCT 'C'" } } */
> +
> +struct A;
> +
> +struct B {
> +  int x;
> +  int (*do_A_thing) (int, int);
> +  struct A *other;
> +};
> +
> +struct C {
> +  unsigned int x;
> +  struct B * a;
> +};
> +
> +int
> +foo (struct C *c)
> +{
> +  return c->x;
> +}
> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c
> new file mode 100644
> index 000000000000..57a079cf0b4d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-3.c
> @@ -0,0 +1,35 @@
> +/* Test that -fprune-btf */
> +
> +/* { dg-do compile } */
> +/* { dg-options "-gbtf -fprune-btf -dA" } */
> +
> +/* We expect full BTF information each struct.  */
> +/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_FWD 'file'" } } */
> +/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_STRUCT 'A'" } } */
> +/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_STRUCT 'B'" } } */
> +/* { dg-final { scan-assembler "TYPE \[0-9\]+ BTF_KIND_STRUCT 'C'" } } */
> +
> +struct file;
> +
> +struct A {
> +  void *private;
> +  long (*read)(struct file *, char *, unsigned long);
> +  long (*write)(struct file *, const char *, unsigned long);
> +};
> +
> +struct B {
> +  unsigned int x;
> +  struct A **as;
> +};
> +
> +struct C {
> +  struct A *arr_a[4];
> +  struct A *lone_a;
> +  unsigned int z;
> +};
> +
> +unsigned int
> +foo (struct B *b, struct C *c)
> +{
> +  return b->x + c->z;
> +}
> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c
> new file mode 100644
> index 000000000000..bf3a25e984ff
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-prune-maps.c
> @@ -0,0 +1,20 @@
> +/* Test special meaning of .maps section for BTF when pruning.  For global
> +   variables of struct type placed in this section, we must treat members as
> +   though they are used directly, always collecting pointee types.
> +   Therefore, full type information for struct keep_me should be emitted.  */
> +
> +/* { dg-do compile } */
> +/* { dg-options "-gbtf -fprune-btf -dA" } */
> +
> +/* { dg-final { scan-assembler-not "BTF_KIND_FWD 'keep_me'" } } */
> +/* { dg-final { scan-assembler "BTF_KIND_STRUCT 'keep_me'" } } */
> +
> +struct keep_me {
> +  int a;
> +  char c;
> +};
> +
> +struct {
> +  int *key;
> +  struct keep_me *value;
> +} my_map __attribute__((section (".maps")));


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

* Re: [PATCH v3 5/6] bpf,btf: enable BTF pruning by default for BPF
  2024-05-30 21:32 ` [PATCH v3 5/6] bpf,btf: enable BTF pruning by default for BPF David Faust
@ 2024-06-05 20:49   ` Indu Bhagat
  0 siblings, 0 replies; 16+ messages in thread
From: Indu Bhagat @ 2024-06-05 20:49 UTC (permalink / raw)
  To: David Faust, gcc-patches; +Cc: jose.marchesi, cupertino.miranda

On 5/30/24 14:32, David Faust wrote:
> This patch enables -fprune-btf by default in the BPF backend when
> generating BTF information, and fixes BPF CO-RE generation when using
> -fprune-btf.
> 
> When generating BPF CO-RE information, we must ensure that types used
> in CO-RE relocations always have sufficient BTF information emited so
> that the CO-RE relocations can be processed by a BPF loader.  The BTF
> pruning algorithm on its own does not have sufficient information to
> determine which types are used in a BPF CO-RE relocation, so this
> information must be supplied by the BPF backend, using a new
> btf_mark_type_used function.
> 
> Co-authored-by: Cupertino Miranda <cupertino.miranda@oracle.com>
> 
> gcc/
> 	* btfout.cc (btf_mark_type_used): New.
> 	* ctfc.h (btf_mark_type_used): Declare it here.
> 	* config/bpf/bpf.cc (bpf_option_override): Enable -fprune-btf
> 	by default if -gbtf is enabled.
> 	* config/bpf/core-builtins.cc (extra_fn): New typedef.
> 	(compute_field_expr): Add callback parameter, and call it if supplied.
> 	Fix computation for MEM_REF.
> 	(mark_component_type_as_used): New.
> 	(bpf_mark_types_as_used): Likewise.
> 	(bpf_expand_core_builtin): Call here.
> 	* doc/invoke.texi (Debugging Options): Note that -fprune-btf is
> 	enabled by default for BPF target when generating BTF.
> 
> gcc/testsuite/
> 	* gcc.dg/debug/btf/btf-variables-5.c: Add -fno-prune-btf to dg-options.

OK for the CTF/BTF generic parts in btfout.cc/ctfc.h.

One minor comment below.

> ---
>   gcc/btfout.cc                                 | 22 ++++++
>   gcc/config/bpf/bpf.cc                         |  5 ++
>   gcc/config/bpf/core-builtins.cc               | 71 +++++++++++++++++--
>   gcc/ctfc.h                                    |  1 +
>   gcc/doc/invoke.texi                           |  3 +
>   .../gcc.dg/debug/btf/btf-variables-5.c        |  6 +-
>   6 files changed, 100 insertions(+), 8 deletions(-)
> 
> diff --git a/gcc/btfout.cc b/gcc/btfout.cc
> index a7da164f6b31..35d2875e3f61 100644
> --- a/gcc/btfout.cc
> +++ b/gcc/btfout.cc
> @@ -1501,6 +1501,28 @@ btf_late_assign_datasec_ids (ctf_container_ref ctfc)
>       }
>   }
>   
> +
> +/* Manually mark that type T is used to ensure it will not be pruned.
> +   Used by the BPF backend when generating BPF CO-RE to mark types used
> +   in CO-RE relocations.  */
> +
> +void
> +btf_mark_type_used (tree t)
> +{
> +  /* If we are not going to prune anyway, this is a no-op.  */
> +  if (!flag_prune_btf)
> +    return;
> +
> +  gcc_assert (TYPE_P (t));
> +  ctf_container_ref ctfc = ctf_get_tu_ctfc ();
> +  ctf_dtdef_ref dtd = ctf_lookup_tree_type (ctfc, t);
> +
> +  if (!dtd)
> +    return;
> +
> +  btf_add_used_type (ctfc, dtd, false, false, true);
> +}
> +
>   /* Callback used for assembling the only-used-types list.  Note that this is
>      the same as btf_type_list_cb above, but the hash_set traverse requires a
>      different function signature.  */
> diff --git a/gcc/config/bpf/bpf.cc b/gcc/config/bpf/bpf.cc
> index dd1bfe38d29b..775730700eba 100644
> --- a/gcc/config/bpf/bpf.cc
> +++ b/gcc/config/bpf/bpf.cc
> @@ -221,6 +221,11 @@ bpf_option_override (void)
>         && !(target_flags_explicit & MASK_BPF_CORE))
>       target_flags |= MASK_BPF_CORE;
>   
> +  /* -gbtf implies -fprune-btf for BPF target.  */

-g also will imply -fprune-btf for BPF target now.  So perhaps "BTF, if 
generated, for BPF target is pruned by default" or something similar ?

> +  if (btf_debuginfo_p ())
> +    SET_OPTION_IF_UNSET (&global_options, &global_options_set,
> +			 flag_prune_btf, true);
> +
>     /* Determine available features from ISA setting (-mcpu=).  */
>     if (bpf_has_jmpext == -1)
>       bpf_has_jmpext = (bpf_isa >= ISA_V2);
> diff --git a/gcc/config/bpf/core-builtins.cc b/gcc/config/bpf/core-builtins.cc
> index 232bebcadbd5..86e2e9d6e39f 100644
> --- a/gcc/config/bpf/core-builtins.cc
> +++ b/gcc/config/bpf/core-builtins.cc
> @@ -624,13 +624,20 @@ bpf_core_get_index (const tree node, bool *valid)
>   
>      ALLOW_ENTRY_CAST is an input arguments and specifies if the function should
>      consider as valid expressions in which NODE entry is a cast expression (or
> -   tree code nop_expr).  */
> +   tree code nop_expr).
> +
> +   EXTRA_FN is a callback function to allow extra functionality with this
> +   function traversal.  Currently used for marking used type during expand
> +   pass.  */
> +
> +typedef void (*extra_fn) (tree);
>   
>   static unsigned char
>   compute_field_expr (tree node, unsigned int *accessors,
>   		    bool *valid,
>   		    tree *access_node,
> -		    bool allow_entry_cast = true)
> +		    bool allow_entry_cast = true,
> +		    extra_fn callback = NULL)
>   {
>     unsigned char n = 0;
>     unsigned int fake_accessors[MAX_NR_ACCESSORS];
> @@ -647,6 +654,9 @@ compute_field_expr (tree node, unsigned int *accessors,
>   
>     *access_node = node;
>   
> +  if (callback != NULL)
> +    callback (node);
> +
>     switch (TREE_CODE (node))
>       {
>       case INDIRECT_REF:
> @@ -664,17 +674,19 @@ compute_field_expr (tree node, unsigned int *accessors,
>       case COMPONENT_REF:
>         n = compute_field_expr (TREE_OPERAND (node, 0), accessors,
>   			      valid,
> -			      access_node, false);
> +			      access_node, false, callback);
>         accessors[n] = bpf_core_get_index (TREE_OPERAND (node, 1), valid);
>         return n + 1;
>       case ARRAY_REF:
>       case ARRAY_RANGE_REF:
> -    case MEM_REF:
>         n = compute_field_expr (TREE_OPERAND (node, 0), accessors,
>   			      valid,
> -			      access_node, false);
> +			      access_node, false, callback);
>         accessors[n++] = bpf_core_get_index (node, valid);
>         return n;
> +    case MEM_REF:
> +      accessors[0] = bpf_core_get_index (node, valid);
> +      return 1;
>       case NOP_EXPR:
>         if (allow_entry_cast == true)
>   	{
> @@ -683,7 +695,7 @@ compute_field_expr (tree node, unsigned int *accessors,
>   	}
>         n = compute_field_expr (TREE_OPERAND (node, 0), accessors,
>   			      valid,
> -			      access_node, false);
> +			      access_node, false, callback);
>         return n;
>   
>       case ADDR_EXPR:
> @@ -1549,6 +1561,51 @@ bpf_resolve_overloaded_core_builtin (location_t loc, tree fndecl,
>     return construct_builtin_core_reloc (loc, fndecl, args, argsvec->length ());
>   }
>   
> +/* Callback function for bpf_mark_field_expr_types_as_used.  */
> +
> +static void
> +mark_component_type_as_used (tree node)
> +{
> +  if (TREE_CODE (node) == COMPONENT_REF)
> +    btf_mark_type_used (TREE_TYPE (TREE_OPERAND (node, 0)));
> +}
> +
> +/* Mark types needed for BPF CO-RE relocations as used.  Doing so ensures that
> +   these types do not get pruned from the BTF information.  */
> +
> +static void
> +bpf_mark_types_as_used (struct cr_builtins *data)
> +{
> +  tree expr = data->expr;
> +  switch (data->kind)
> +    {
> +    case BPF_RELO_FIELD_BYTE_OFFSET:
> +    case BPF_RELO_FIELD_BYTE_SIZE:
> +    case BPF_RELO_FIELD_EXISTS:
> +    case BPF_RELO_FIELD_SIGNED:
> +    case BPF_RELO_FIELD_LSHIFT_U64:
> +    case BPF_RELO_FIELD_RSHIFT_U64:
> +      if (TREE_CODE (expr) == ADDR_EXPR)
> +	expr = TREE_OPERAND (expr, 0);
> +
> +      expr = root_for_core_field_info (expr);
> +      compute_field_expr (data->expr, NULL, NULL, NULL, false,
> +			  mark_component_type_as_used);
> +      break;
> +    case BPF_RELO_TYPE_ID_LOCAL:
> +    case BPF_RELO_TYPE_ID_TARGET:
> +    case BPF_RELO_TYPE_EXISTS:
> +    case BPF_RELO_TYPE_SIZE:
> +    case BPF_RELO_ENUMVAL_EXISTS:
> +    case BPF_RELO_ENUMVAL_VALUE:
> +    case BPF_RELO_TYPE_MATCHES:
> +      btf_mark_type_used (data->type);
> +      break;
> +    default:
> +      gcc_unreachable ();
> +    }
> +}
> +
>   /* Used in bpf_expand_builtin.  This function is called in RTL expand stage to
>      convert the internal __builtin_core_reloc in unspec:UNSPEC_CORE_RELOC RTL,
>      which will contain a third argument that is the index in the vec collected
> @@ -1567,6 +1624,8 @@ bpf_expand_core_builtin (tree exp, enum bpf_builtins code)
>   	tree index = CALL_EXPR_ARG (exp, 0);
>   	struct cr_builtins *data = get_builtin_data (TREE_INT_CST_LOW (index));
>   
> +	bpf_mark_types_as_used (data);
> +
>   	rtx v = expand_normal (data->default_value);
>   	rtx i = expand_normal (index);
>   	  return gen_rtx_UNSPEC (DImode,
> diff --git a/gcc/ctfc.h b/gcc/ctfc.h
> index 29267dc036d1..41e1169f271d 100644
> --- a/gcc/ctfc.h
> +++ b/gcc/ctfc.h
> @@ -457,6 +457,7 @@ extern ctf_dtdef_ref ctf_lookup_tree_type (ctf_container_ref, const tree);
>   
>   typedef bool (*funcs_traverse_callback) (ctf_dtdef_ref, void *);
>   bool traverse_btf_func_types (funcs_traverse_callback, void *);
> +extern void btf_mark_type_used (tree);
>   
>   /* CTF section does not emit location information; at this time, location
>      information is needed for BTF CO-RE use-cases.  */
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 6dac00d14381..8edf0914fddb 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -12312,6 +12312,9 @@ It is primarily useful when compiling for the BPF target, to minimize
>   the size of the resulting object, and to eliminate BTF information
>   which is not immediately relevant to the BPF program loading process.
>   
> +This option is enabled by default for the BPF target when generating
> +BTF information.
> +
>   @opindex femit-struct-debug-baseonly
>   @item -femit-struct-debug-baseonly
>   Emit debug information for struct-like types
> diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-variables-5.c b/gcc/testsuite/gcc.dg/debug/btf/btf-variables-5.c
> index 8aae76cacabd..7cd8728ecaf6 100644
> --- a/gcc/testsuite/gcc.dg/debug/btf/btf-variables-5.c
> +++ b/gcc/testsuite/gcc.dg/debug/btf/btf-variables-5.c
> @@ -11,9 +11,11 @@
>   /* { dg-final { scan-assembler-times "\[\t \]0xe000000\[\t \]+\[^\n\]*btv_info" 1 } } */
>   /* { dg-final { scan-assembler-times "\[\t \]0x1\[\t \]+\[^\n\]*btv_linkage" 1 } } */
>   
> -/* Expect 2 array types, one of which is unsized.  */
> +/* Expect 2 array types, one of which is unsized.  For BPF target, -fprune-btf
> +   is the default and will remove the unsized array type.  */
>   /* { dg-final { scan-assembler-times "\[\t \]0x4\[\t \]+\[^\n\]*bta_nelems" 1 } } */
> -/* { dg-final { scan-assembler-times "\[\t \]0\[\t \]+\[^\n\]*bta_nelems" 1 } } */
> +/* { dg-final { scan-assembler-times "\[\t \]0\[\t \]+\[^\n\]*bta_nelems" 1 { target { !bpf-*-* } } } } */
> +/* { dg-final { scan-assembler-times "\[\t \]0\[\t \]+\[^\n\]*bta_nelems" 0 { target { bpf-*-* } } } } */
>   
>   extern const char FOO[];
>   const char FOO[] = "foo";


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

* Re: [PATCH v3 6/6] opts: allow any combination of DWARF, CTF, BTF
  2024-05-30 21:32 ` [PATCH v3 6/6] opts: allow any combination of DWARF, CTF, BTF David Faust
@ 2024-06-05 22:16   ` Indu Bhagat
  0 siblings, 0 replies; 16+ messages in thread
From: Indu Bhagat @ 2024-06-05 22:16 UTC (permalink / raw)
  To: David Faust, gcc-patches; +Cc: jose.marchesi, cupertino.miranda

On 5/30/24 14:32, David Faust wrote:
> Previously it was not supported to generate both CTF and BTF debug info
> in the same compiler run, as both formats made incompatible changes to
> the same internal data structures.
> 
> With the structural change in the prior patches, in particular the
> guarantee that CTF will always be fully emitted before any BTF
> translation occurs, there is no longer anything preventing generation
> of both CTF and BTF at the same time.  This patch changes option parsing
> to allow any combination of -gdwarf, -gctf, and -gbtf at the same time.
> 

I am not an approver for this change, but I have a comment below.

> gcc/
> 	* opts.cc (set_debug_level): Allow any combination of -gdwarf,
> 	-gctf and -gbtf at the same time.
> ---
>   gcc/opts.cc | 20 +++++++-------------
>   1 file changed, 7 insertions(+), 13 deletions(-)
> 
> diff --git a/gcc/opts.cc b/gcc/opts.cc
> index f80d5d4ba8f9..d58bea096a5f 100644
> --- a/gcc/opts.cc
> +++ b/gcc/opts.cc
> @@ -3505,21 +3505,15 @@ set_debug_level (uint32_t dinfo, int extended, const char *arg,
>       }
>     else
>       {
> -      /* Make and retain the choice if both CTF and DWARF debug info are to
> -	 be generated.  */
> -      if (((dinfo == DWARF2_DEBUG) || (dinfo == CTF_DEBUG))
> +      /* Any combination of DWARF, CTF and BTF is allowed together.  */
> +      if (((dinfo == DWARF2_DEBUG) || (dinfo == CTF_DEBUG)
> +	   || (dinfo == BTF_DEBUG))
>   	  && ((opts->x_write_symbols == (DWARF2_DEBUG|CTF_DEBUG))
> +	      || (opts->x_write_symbols == (DWARF2_DEBUG|BTF_DEBUG))
> +	      || (opts->x_write_symbols == (CTF_DEBUG|BTF_DEBUG))
>   	      || (opts->x_write_symbols == DWARF2_DEBUG)
> -	      || (opts->x_write_symbols == CTF_DEBUG)))
> -	{
> -	  opts->x_write_symbols |= dinfo;
> -	  opts_set->x_write_symbols |= dinfo;
> -	}
> -      /* However, CTF and BTF are not allowed together at this time.  */
> -      else if (((dinfo == DWARF2_DEBUG) || (dinfo == BTF_DEBUG))
> -	       && ((opts->x_write_symbols == (DWARF2_DEBUG|BTF_DEBUG))
> -		   || (opts->x_write_symbols == DWARF2_DEBUG)
> -		   || (opts->x_write_symbols == BTF_DEBUG)))
> +	      || (opts->x_write_symbols == CTF_DEBUG)
> +	      || (opts->x_write_symbols == BTF_DEBUG)))

I realised that this check will cause failures on double occurrences of 
the command line flags:

$ gcc -c sort.c -g3 -gctf -flto -gbtf -gctf
gcc: error: debug format ‘ctf’ conflicts with prior selection

What do you think about the following check instead ?

if ((dinfo == DWARF2_DEBUG || dinfo == CTF_DEBUG || dinfo == BTF_DEBUG)
     && ((opts->x_write_symbols | (DWARF2_DEBUG|CTF_DEBUG|BTF_DEBUG))
         == (DWARF2_DEBUG|CTF_DEBUG|BTF_DEBUG)))
   {

We have some testcases for checking -gctf -gdwarf (in 
debug/ctf/ctf-debug*), adding something for -gctf -gbtf will be great.

>   	{
>   	  opts->x_write_symbols |= dinfo;
>   	  opts_set->x_write_symbols |= dinfo;


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

end of thread, other threads:[~2024-06-05 22:16 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-05-30 21:32 [PATCH v3 0/6] btf: refactor and add pruning option David Faust
2024-05-30 21:32 ` [PATCH v3 1/6] ctf, btf: restructure CTF/BTF emission David Faust
2024-06-05  7:27   ` Indu Bhagat
2024-05-30 21:32 ` [PATCH v3 2/6] ctf: use pointers instead of IDs internally David Faust
2024-06-05  7:31   ` Indu Bhagat
2024-05-30 21:32 ` [PATCH v3 3/6] btf: refactor and simplify implementation David Faust
2024-06-05  7:41   ` Indu Bhagat
2024-05-30 21:32 ` [PATCH v3 4/6] btf: add -fprune-btf option David Faust
2024-05-31  7:07   ` Richard Biener
2024-05-31 15:57     ` David Faust
2024-05-31 16:24       ` Richard Biener
2024-06-05  7:56   ` Indu Bhagat
2024-05-30 21:32 ` [PATCH v3 5/6] bpf,btf: enable BTF pruning by default for BPF David Faust
2024-06-05 20:49   ` Indu Bhagat
2024-05-30 21:32 ` [PATCH v3 6/6] opts: allow any combination of DWARF, CTF, BTF David Faust
2024-06-05 22:16   ` Indu Bhagat

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).