> On 6/12/24 09:55, Jose E. Marchesi wrote: >> >> Hi Faust. >> Thanks for the patch. >> Please see a question below. >> >>> This patch enables -gprune-btf by default in the BPF backend when >>> generating BTF information, and fixes BPF CO-RE generation when using >>> -gprune-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.mira...@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 -gprune-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 -gprune-btf is >>> enabled by default for BPF target when generating BTF. >>> >>> gcc/testsuite/ >>> * gcc.dg/debug/btf/btf-variables-5.c: Adjust one test for bpf-*-* >>> target. >>> --- >>> 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 34d8cec0a2e3..083ca48d6279 100644 >>> --- a/gcc/btfout.cc >>> +++ b/gcc/btfout.cc >>> @@ -1503,6 +1503,28 @@ btf_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 (!debug_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..c62af7a6efa7 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 -gprune-btf for BPF target. */ >>> + if (btf_debuginfo_p ()) >>> + SET_OPTION_IF_UNSET (&global_options, &global_options_set, >>> + debug_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))); >>> +} >> >> This means that the callback is only marking as used these types >> reachable from the CO-RE builtin arguments that are referenced by >> indexation or field name or pointer? > The callback is only marking as used the types which are present in the > CO-RE builtin argument by the chain of field accesses. It is not > marking other types which may be part of those structures but are not > used by the CO-RE builtin. The intent is to ensure type information > needed to perform the CO-RE relocation is present, while allowing other > extraneous types to be pruned. > > For example if we have: > > struct A { > struct B *b; > struct C *c; > }; > > struct B { > int x; > int y; > }; > > struct C { > int w; > int z; > char stuff[400]; > struct some_other_huge_struct *ptr_to_huge; > ... > int member_number_800_at_offset_10620; > }; > > struct A my_a = ...; > __builtin_preserve_access_index (my_a->b.x); > > In this case, we will mark struct B as used, but not struct C. > > Supposing struct C is otherwise unused, then in the pruned BTF > we may get like: > > [1] BTF_KIND_INT "int" bits=32 .. > [2] BTF_KIND_STRUCT "A" > member "b" type=3 > member "c" type=5 > [3] BTF_KIND_PTR type=4 > [4] BTF_KIND_STRUCT "B" > member "x" type=1 > member "y" type=1 > [5] BTF_KIND_PTR type=6 > [6] BTF_KIND_FWD "C" fwd_kind=struct > > Where the full type information for C is pruned because it is not > necessary for this program, yet everything necessary to perform the > CO-RE relocation correctly is present. > > Does that make sense to answer your question?
Yes, thats what I understood. OK for this particular patch for master. Thanks! I believe you need some global maintainer to chime in for patches 4/6 and 6/6. > >> >>> + >>> +/* 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 8479fd5cf2b8..0afd686733d0 100644 >>> --- a/gcc/doc/invoke.texi >>> +++ b/gcc/doc/invoke.texi >>> @@ -12023,6 +12023,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 gctf >>> @item -gctf >>> @itemx -gctf@var{level} >>> 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..a08130cfc072 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, >>> -gprune-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";