btf: generate and output DECL_TAG and TYPE_TAG records

Support the btf_decl_tag and btf_type_tag attributes in BTF by creating
and emitting BTF_KIND_DECL_TAG and BTF_KIND_TYPE_TAG records,
respectively, for them.

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

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

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

include/
	* btf.h (BTF_KIND_DECL_TAG, BTF_KIND_TYPE_TAG) New defines.
	(struct btf_decl_tag): New.
This commit is contained in:
David Faust 2025-02-05 10:41:23 -08:00
parent 9c862a593b
commit 43dcea48b8
11 changed files with 366 additions and 24 deletions

View File

@ -141,6 +141,8 @@ get_btf_kind (uint32_t ctf_kind)
case CTF_K_VOLATILE: return BTF_KIND_VOLATILE; case CTF_K_VOLATILE: return BTF_KIND_VOLATILE;
case CTF_K_CONST: return BTF_KIND_CONST; case CTF_K_CONST: return BTF_KIND_CONST;
case CTF_K_RESTRICT: return BTF_KIND_RESTRICT; case CTF_K_RESTRICT: return BTF_KIND_RESTRICT;
case CTF_K_DECL_TAG: return BTF_KIND_DECL_TAG;
case CTF_K_TYPE_TAG: return BTF_KIND_TYPE_TAG;
default:; default:;
} }
return BTF_KIND_UNKN; return BTF_KIND_UNKN;
@ -217,6 +219,7 @@ btf_calc_num_vbytes (ctf_dtdef_ref dtd)
case BTF_KIND_CONST: case BTF_KIND_CONST:
case BTF_KIND_RESTRICT: case BTF_KIND_RESTRICT:
case BTF_KIND_FUNC: case BTF_KIND_FUNC:
case BTF_KIND_TYPE_TAG:
/* These kinds have no vlen data. */ /* These kinds have no vlen data. */
break; break;
@ -256,6 +259,10 @@ btf_calc_num_vbytes (ctf_dtdef_ref dtd)
vlen_bytes += vlen * sizeof (struct btf_var_secinfo); vlen_bytes += vlen * sizeof (struct btf_var_secinfo);
break; break;
case BTF_KIND_DECL_TAG:
vlen_bytes += sizeof (struct btf_decl_tag);
break;
default: default:
break; break;
} }
@ -452,6 +459,20 @@ btf_asm_type (ctf_dtdef_ref dtd)
and should write 0. */ and should write 0. */
dw2_asm_output_data (4, 0, "(unused)"); dw2_asm_output_data (4, 0, "(unused)");
return; return;
case BTF_KIND_DECL_TAG:
{
if (dtd->ref_type)
break;
else if (dtd->dtd_u.dtu_tag.ref_var)
{
/* ref_type is NULL for decl tag attached to a variable. */
ctf_dvdef_ref dvd = dtd->dtd_u.dtu_tag.ref_var;
dw2_asm_output_data (4, dvd->dvd_id,
"btt_type: (BTF_KIND_VAR '%s')",
dvd->dvd_name);
return;
}
}
default: default:
break; break;
} }
@ -801,6 +822,12 @@ output_asm_btf_vlen_bytes (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
at this point. */ at this point. */
gcc_unreachable (); gcc_unreachable ();
case BTF_KIND_DECL_TAG:
dw2_asm_output_data (4, dtd->dtd_u.dtu_tag.component_idx,
"component_idx=%d",
dtd->dtd_u.dtu_tag.component_idx);
break;
default: default:
/* All other BTF type kinds have no variable length data. */ /* All other BTF type kinds have no variable length data. */
break; break;
@ -851,6 +878,20 @@ output_btf_func_types (void)
btf_asm_func_type (ref); btf_asm_func_type (ref);
} }
static void
output_btf_tags (ctf_container_ref ctfc)
{
/* If pruning, tags which are not pruned have already been added to
the used list and output by output_btf_types. */
if (debug_prune_btf)
return;
ctf_dtdef_ref dtd;
unsigned i;
FOR_EACH_VEC_ELT (*ctfc->ctfc_tags, i, dtd)
output_asm_btf_type (ctfc, dtd);
}
/* Output all BTF_KIND_DATASEC records. */ /* Output all BTF_KIND_DATASEC records. */
static void static void
@ -869,6 +910,7 @@ btf_output (ctf_container_ref ctfc)
output_btf_types (ctfc); output_btf_types (ctfc);
output_btf_vars (ctfc); output_btf_vars (ctfc);
output_btf_func_types (); output_btf_func_types ();
output_btf_tags (ctfc);
output_btf_datasec_types (); output_btf_datasec_types ();
output_btf_strs (ctfc); output_btf_strs (ctfc);
} }
@ -985,7 +1027,8 @@ static vec<struct btf_fixup> fixups;
is created and emitted. This vector stores them. */ is created and emitted. This vector stores them. */
static GTY (()) vec<ctf_dtdef_ref, va_gc> *forwards; static GTY (()) vec<ctf_dtdef_ref, va_gc> *forwards;
/* Recursively add type DTD and any types it references to the used set. /* Implementation of btf_add_used_type.
Recursively add type DTD and any types it references to the used set.
Return a type that should be used for references to DTD - usually DTD itself, 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. 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 CHECK_PTR is true if one of the predecessors in recursive calls is a struct
@ -996,8 +1039,8 @@ static GTY (()) vec<ctf_dtdef_ref, va_gc> *forwards;
CREATE_FIXUPS is false. */ CREATE_FIXUPS is false. */
static ctf_dtdef_ref static ctf_dtdef_ref
btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd, btf_add_used_type_1 (ctf_container_ref ctfc, ctf_dtdef_ref dtd,
bool check_ptr, bool seen_ptr, bool create_fixups) bool check_ptr, bool seen_ptr, bool create_fixups)
{ {
if (dtd == NULL) if (dtd == NULL)
return NULL; return NULL;
@ -1029,8 +1072,9 @@ btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd,
fixups.unordered_remove (i); fixups.unordered_remove (i);
/* Add the concrete base type. */ /* Add the concrete base type. */
dtd->ref_type = btf_add_used_type (ctfc, dtd->ref_type, check_ptr, dtd->ref_type = btf_add_used_type_1 (ctfc, dtd->ref_type,
seen_ptr, create_fixups); check_ptr, seen_ptr,
create_fixups);
return dtd; return dtd;
} }
default: default:
@ -1044,8 +1088,8 @@ btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd,
the reference to the bitfield. The slice type won't be emitted, the reference to the bitfield. The slice type won't be emitted,
but we need the information in it when writing out the bitfield but we need the information in it when writing out the bitfield
encoding. */ encoding. */
btf_add_used_type (ctfc, dtd->dtd_u.dtu_slice.cts_type, btf_add_used_type_1 (ctfc, dtd->dtd_u.dtu_slice.cts_type,
check_ptr, seen_ptr, create_fixups); check_ptr, seen_ptr, create_fixups);
return dtd; return dtd;
} }
@ -1069,7 +1113,11 @@ btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd,
case BTF_KIND_INT: case BTF_KIND_INT:
case BTF_KIND_FLOAT: case BTF_KIND_FLOAT:
case BTF_KIND_FWD: case BTF_KIND_FWD:
/* Leaf kinds which do not refer to any other types. */ case BTF_KIND_DECL_TAG:
/* Leaf kinds which do not refer to any other types.
BTF_KIND_DECL_TAG is a special case: we treat it as though it does not
refer to any other types, since we only want the DECL_TAG to be added
if the type to which it refers has already been added. */
break; break;
case BTF_KIND_FUNC: case BTF_KIND_FUNC:
@ -1082,6 +1130,7 @@ btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd,
case BTF_KIND_CONST: case BTF_KIND_CONST:
case BTF_KIND_VOLATILE: case BTF_KIND_VOLATILE:
case BTF_KIND_RESTRICT: case BTF_KIND_RESTRICT:
case BTF_KIND_TYPE_TAG:
{ {
/* These type kinds refer to exactly one other type. */ /* These type kinds refer to exactly one other type. */
if (check_ptr && !seen_ptr) if (check_ptr && !seen_ptr)
@ -1106,18 +1155,18 @@ btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd,
} }
/* Add the type to which this type refers. */ /* Add the type to which this type refers. */
dtd->ref_type = btf_add_used_type (ctfc, dtd->ref_type, check_ptr, dtd->ref_type = btf_add_used_type_1 (ctfc, dtd->ref_type, check_ptr,
seen_ptr, create_fixups); seen_ptr, create_fixups);
break; break;
} }
case BTF_KIND_ARRAY: case BTF_KIND_ARRAY:
{ {
/* Add element and index types. */ /* Add element and index types. */
ctf_arinfo_t *arr = &(dtd->dtd_u.dtu_arr); ctf_arinfo_t *arr = &(dtd->dtd_u.dtu_arr);
arr->ctr_contents = btf_add_used_type (ctfc, arr->ctr_contents, false, arr->ctr_contents = btf_add_used_type_1 (ctfc, arr->ctr_contents,
false, create_fixups); false, false, create_fixups);
arr->ctr_index = btf_add_used_type (ctfc, arr->ctr_index, false, false, arr->ctr_index = btf_add_used_type_1 (ctfc, arr->ctr_index, false,
create_fixups); false, create_fixups);
break; break;
} }
case BTF_KIND_STRUCT: case BTF_KIND_STRUCT:
@ -1133,8 +1182,8 @@ btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd,
/* Add member type for struct/union members. For enums, only the /* Add member type for struct/union members. For enums, only the
enumerator names are needed. */ enumerator names are needed. */
if (kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION) if (kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION)
dmd->dmd_type = btf_add_used_type (ctfc, dmd->dmd_type, true, dmd->dmd_type = btf_add_used_type_1 (ctfc, dmd->dmd_type, true,
false, create_fixups); false, create_fixups);
ctf_add_string (ctfc, dmd->dmd_name, &(dmd->dmd_name_offset), ctf_add_string (ctfc, dmd->dmd_name, &(dmd->dmd_name_offset),
CTF_STRTAB); CTF_STRTAB);
} }
@ -1143,16 +1192,17 @@ btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd,
case BTF_KIND_FUNC_PROTO: case BTF_KIND_FUNC_PROTO:
{ {
/* Add return type. */ /* Add return type. */
dtd->ref_type = btf_add_used_type (ctfc, dtd->ref_type, false, false, dtd->ref_type = btf_add_used_type_1 (ctfc, dtd->ref_type, false, false,
create_fixups); create_fixups);
/* Add arg types. */ /* Add arg types. */
ctf_func_arg_t * farg; ctf_func_arg_t * farg;
for (farg = dtd->dtd_u.dtu_argv; for (farg = dtd->dtd_u.dtu_argv;
farg != NULL; farg = (ctf_func_arg_t *) ctf_farg_list_next (farg)) farg != NULL; farg = (ctf_func_arg_t *) ctf_farg_list_next (farg))
{ {
farg->farg_type = btf_add_used_type (ctfc, farg->farg_type, false, farg->farg_type = btf_add_used_type_1 (ctfc, farg->farg_type,
false, create_fixups); false, false,
create_fixups);
/* Note: argument names are stored in the auxilliary string table, /* Note: argument names are stored in the auxilliary string table,
since CTF does not include arg names. That table has not been since CTF does not include arg names. That table has not been
cleared, so no need to re-add argument names here. */ cleared, so no need to re-add argument names here. */
@ -1166,6 +1216,16 @@ btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd,
return dtd; return dtd;
} }
/* Recursively add type DTD and any types it references to the used set.
Return a type that should be used for references to DTD - usually DTD itself,
but may be NULL if DTD corresponds to a type which will not be emitted. */
static ctf_dtdef_ref
btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
{
return btf_add_used_type_1 (ctfc, dtd, false, false, true);
}
/* Initial entry point of BTF generation, called at early_finish () after /* Initial entry point of BTF generation, called at early_finish () after
CTF information has possibly been output. Translate all CTF information CTF information has possibly been output. Translate all CTF information
to BTF, and do any processing that must be done early, such as creating to BTF, and do any processing that must be done early, such as creating
@ -1402,7 +1462,7 @@ btf_add_vars (ctf_container_ref ctfc)
ctf_dmdef_t *dmd; ctf_dmdef_t *dmd;
for (dmd = dtd->dtd_u.dtu_members; for (dmd = dtd->dtd_u.dtu_members;
dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd)) dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
btf_add_used_type (ctfc, dmd->dmd_type, false, false, true); btf_add_used_type (ctfc, dmd->dmd_type);
} }
} }
} }
@ -1488,6 +1548,39 @@ btf_assign_var_ids (ctf_container_ref ctfc)
} }
} }
/* Assign BTF IDs for type and decl tags and account for their size. */
static void
btf_assign_tag_ids (ctf_container_ref ctfc)
{
size_t num_tags = vec_safe_length (ctfc->ctfc_tags);
if (num_tags == 0)
return;
unsigned int i;
ctf_dtdef_ref dtd;
FOR_EACH_VEC_ELT (*ctfc->ctfc_tags, i, dtd)
{
/* Assign BTF id. */
ctf_id_t id = ctfc->ctfc_nextid++;
gcc_assert (id <= BTF_MAX_TYPE);
dtd->dtd_type = id;
/* Tags on functions will have a ref_type pointing to the
FUNC_PROTO, we want them to point the FUNC record instead. */
ctf_dtdef_ref *pdtd = NULL;
if (dtd->ref_type && (pdtd = func_map->get (dtd->ref_type)) != NULL)
dtd->ref_type = *pdtd;
/* Strings for tags are stored in the auxiliary strtab, which is
concatenated after the regular strtab. ctti_name only accounts
for offset in the auxiliary strtab until this point. */
dtd->dtd_data.ctti_name += ctfc_get_strtab_len (ctfc, CTF_STRTAB);
ctfc->ctfc_num_types++;
ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (dtd);
}
}
/* Assign BTF IDs for datasec records and account for their size. */ /* Assign BTF IDs for datasec records and account for their size. */
static void static void
@ -1522,7 +1615,7 @@ btf_mark_type_used (tree t)
if (!dtd) if (!dtd)
return; return;
btf_add_used_type (ctfc, dtd, false, false, true); btf_add_used_type (ctfc, dtd);
} }
/* Callback used for assembling the only-used-types list. Note that this is /* Callback used for assembling the only-used-types list. Note that this is
@ -1549,7 +1642,7 @@ btf_collect_pruned_types (ctf_container_ref ctfc)
size_t i; size_t i;
FOR_EACH_VEC_ELT (*funcs, i, dtd) FOR_EACH_VEC_ELT (*funcs, i, dtd)
{ {
btf_add_used_type (ctfc, dtd->ref_type, false, false, true); btf_add_used_type (ctfc, dtd->ref_type);
ctf_add_string (ctfc, dtd->dtd_name, &(dtd->dtd_data.ctti_name), ctf_add_string (ctfc, dtd->dtd_name, &(dtd->dtd_data.ctti_name),
CTF_STRTAB); CTF_STRTAB);
} }
@ -1558,10 +1651,33 @@ btf_collect_pruned_types (ctf_container_ref ctfc)
for (i = 0; i < ctfc->ctfc_vars_list_count; i++) for (i = 0; i < ctfc->ctfc_vars_list_count; i++)
{ {
ctf_dvdef_ref dvd = ctfc->ctfc_vars_list[i]; ctf_dvdef_ref dvd = ctfc->ctfc_vars_list[i];
btf_add_used_type (ctfc, dvd->dvd_type, false, false, true); btf_add_used_type (ctfc, dvd->dvd_type);
ctf_add_string (ctfc, dvd->dvd_name, &(dvd->dvd_name_offset), CTF_STRTAB); ctf_add_string (ctfc, dvd->dvd_name, &(dvd->dvd_name_offset), CTF_STRTAB);
} }
/* Used type tags will be added by recursive btf_add_used_type calls above.
For decl tags, scan the list and only add those decl tags whose referent
types are marked as used. We may have pruned a struct type with members
annotated by a decl tag. */
FOR_EACH_VEC_ELT (*ctfc->ctfc_tags, i, dtd)
{
/* Only add decl tags whose referent types have not been pruned.
Variables are never pruned, so decl tags on variables are always
used. */
if (btf_dtd_kind (dtd) == BTF_KIND_DECL_TAG
&& ((dtd->ref_type && btf_used_types->contains (dtd->ref_type))
|| (dtd->dtd_u.dtu_tag.ref_var)))
btf_add_used_type (ctfc, dtd);
/* Tags on functions or function args will have a ref_type pointing to the
FUNC_PROTO, we want them to point the FUNC record instead. */
ctf_dtdef_ref *pdtd = NULL;
if (dtd->ref_type
&& btf_used_types->contains (dtd->ref_type)
&& (pdtd = func_map->get (dtd->ref_type)) != NULL)
dtd->ref_type = *pdtd;
}
/* Process fixups. If the base type was never added, create a forward for it /* 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 and adjust the reference to point to that. If it was added, then nothing
needs to change. */ needs to change. */
@ -1634,6 +1750,13 @@ btf_finish (void)
btf_assign_var_ids (tu_ctfc); btf_assign_var_ids (tu_ctfc);
btf_assign_func_ids (tu_ctfc); btf_assign_func_ids (tu_ctfc);
/* Both decl and type tags may be pruned if the types/decls to which they
refer are pruned. This is handled in btf_collect_pruned_types, and
through that process they have also been assigned ids already. */
if (!debug_prune_btf)
btf_assign_tag_ids (tu_ctfc);
btf_assign_datasec_ids (tu_ctfc); btf_assign_datasec_ids (tu_ctfc);
/* Finally, write out the complete .BTF section. */ /* Finally, write out the complete .BTF section. */

View File

@ -0,0 +1,14 @@
/* Test simple BTF decl tag generation for variables. */
/* { dg-do compile } */
/* { dg-options "-O0 -gbtf -dA" } */
#define __tag1 __attribute__((btf_decl_tag ("decl1")))
#define __tag2 __attribute__((btf_decl_tag ("decl2")))
#define __tag3 __attribute__((btf_decl_tag ("decl3")))
int x __tag1 __tag2;
int y __tag1;
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_VAR 'x'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_VAR 'x'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_VAR 'y'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */

View File

@ -0,0 +1,22 @@
/* Test BTF decl tag generation for structs. */
/* { dg-do compile } */
/* { dg-options "-O0 -gbtf -dA" } */
#define __tag1 __attribute__((btf_decl_tag ("decl1")))
#define __tag2 __attribute__((btf_decl_tag ("decl2")))
#define __tag3 __attribute__((btf_decl_tag ("decl3")))
struct Foo {
int a;
int b __tag3 __tag2;
char *z __tag1;
};
struct Foo f __tag1 __tag2;
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_STRUCT 'Foo'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=1" 1} } */
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl3'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_STRUCT 'Foo'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=1" 1} } */
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_STRUCT 'Foo'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=2" 1} } */
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_VAR 'f'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_VAR 'f'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */

View File

@ -0,0 +1,22 @@
/* Test BTF decl tag generation for functions and function args. */
/* { dg-do compile } */
/* { dg-options "-O0 -gbtf -dA" } */
#define __tag1 __attribute__((btf_decl_tag ("decl1")))
#define __tag2 __attribute__((btf_decl_tag ("decl2")))
#define __tag3 __attribute__((btf_decl_tag ("decl3")))
int __tag1 __tag2 func (int arg_a __tag3 __tag1, int arg_b __tag2)
{
return arg_a * arg_b;
}
int foo (int x) {
return func (x, x + 1);
}
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_FUNC 'func'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_FUNC 'func'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_FUNC 'func'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=0" 1} } */
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl3'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_FUNC 'func'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=0" 1} } */
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_FUNC 'func'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=1" 1} } */

View File

@ -0,0 +1,34 @@
/* Test BTF decl tag generation with BTF pruning. */
/* { dg-do compile } */
/* { dg-options "-O0 -gbtf -gprune-btf -dA" } */
#define __decl1 __attribute__((btf_decl_tag ("decl1")))
#define __decl2 __attribute__((btf_decl_tag ("decl2")))
#define __decl3 __attribute__((btf_decl_tag ("decl3")))
struct S {
/* This tag on S.v shall not be emitted, because struct S is pruned and
replaced with a FWD, which does not hold any member info. */
int v __decl3;
int w;
};
struct T {
int a;
struct S *s __decl1;
int c __decl2;
};
struct T t __decl1;
int __decl1 func (struct T *t __decl2)
{
return t->a + t->c;
}
/* { dg-final { scan-assembler-not " BTF_KIND_DECL_TAG 'decl3'" } } */
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_STRUCT 'T'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=1" 1} } */
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_STRUCT 'T'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=2" 1} } */
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_VAR 't'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_FUNC 'func'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=-1" 1} } */
/* { dg-final { scan-assembler-times " BTF_KIND_DECL_TAG 'decl2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_FUNC 'func'\\)\[\\r\\n\]+\[^\\r\\n\]*component_idx=0" 1} } */

View File

@ -0,0 +1,26 @@
/* Test simple generation of BTF type tags. */
/* { dg-do compile } */
/* { dg-options "-O0 -gbtf -dA" } */
#define __tag1 __attribute__((btf_type_tag("1")))
#define __tag2 __attribute__((btf_type_tag("2")))
/* var("kp") -> ptr -> type_tag("1") -> int */
int * __tag1 kp;
struct Foo {
char a;
int b;
};
/* var("f") -> ptr -> type_tag("2") -> type_tag("1") -> struct("Foo") */
struct Foo * __tag1 __tag2 f;
/* { dg-final { scan-assembler-times " BTF_KIND_VAR 'kp'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_PTR" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_VAR 'f'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_PTR" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG '1'\\)" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_STRUCT" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_PTR ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG" 2 } } */

View File

@ -0,0 +1,13 @@
/* Test generation of BTF type tags with cv-quals. */
/* { dg-do compile } */
/* { dg-options "-O0 -gbtf -dA" } */
#define __tag __attribute__((btf_type_tag("1")))
/* var("pci") -> const -> ptr -> type_tag("1") -> int */
int __tag *const pci;
/* { dg-final { scan-assembler-times " BTF_KIND_VAR 'pci'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_CONST" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_CONST ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_PTR" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_PTR ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT" 1 } } */

View File

@ -0,0 +1,28 @@
/* Test generation of BTF type tags with typedefs. */
/* { dg-do compile } */
/* { dg-options "-O0 -gbtf -dA" } */
#define __tag1 __attribute__((btf_type_tag("1")))
#define __tag2 __attribute__((btf_type_tag("2")))
typedef int *const cp;
typedef int __tag1 *tp;
/* var("x") -> ptr -> type_tag("2") -> typedef("cp") -> const -> ptr -> int */
cp __tag2 * x;
/* var("y") -> const -> typedef("tp") -> ptr -> type_tag("1") -> int */
const tp y;
/* { dg-final { scan-assembler-times " BTF_KIND_VAR 'x'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_PTR" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_PTR ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG '2'\\)" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPEDEF 'cp'\\)" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_TYPEDEF 'cp'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_CONST" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_CONST ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_PTR" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_PTR ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_VAR 'y'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_CONST" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_CONST ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPEDEF 'tp'\\)" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_TYPEDEF 'tp'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_PTR" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_PTR ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG '1'\\)" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT" 1 } } */

View File

@ -0,0 +1,24 @@
/* Test generation of BTF type tag when applied to function prototypes. */
/* { dg-do compile } */
/* { dg-options "-O0 -gbtf -dA" } */
#define __tag1 __attribute__((btf_type_tag("1")))
#define __tag2 __attribute__((btf_type_tag("2")))
int * __tag1
dothing (void __tag2 *ptr, int __tag2 __tag1 *xi)
{
if (xi)
{
int *tmp = (int *) ptr;
return tmp + (*xi);
}
return (int *) ptr;
}
/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '2'(\[\\r\\n\]+\[^\\r\\n\]*){2} btt_type: void" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG '2'" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_PTR ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG '1'" 2 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_PTR ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG '2'" 1 } } */

View File

@ -0,0 +1,22 @@
/* Test BTF type tag generation using C2x standard attribute syntax.
C2x attribute syntax does not allow attributes to "slide around". */
/* { dg-do compile } */
/* { dg-options "-O0 -gbtf -dA -std=c23" } */
#define __tag1 [[gnu::btf_type_tag ("1")]]
#define __tag2 [[gnu::btf_type_tag ("2")]]
#define __tag3 [[gnu::btf_type_tag ("3")]]
/* Note that the BTF format still only allows to represent type tags on
pointer types, so we do not get any type_tag("1") from the below, as
it applies to the 'volatile int' type and cannot be represented. */
/* var(z) -> const -> ptr -> type_tag(2) -> type_tag(3) -> ptr -> type_tag(2) -> volatile -> int */
volatile int __tag1 * __tag2 * __tag3 __tag2 const z;
/* { dg-final { scan-assembler-times " BTF_KIND_VAR 'z'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_CONST" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_CONST ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_PTR" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_PTR ''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG '2'\\)" 2 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG '3'\\)" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '3'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_PTR" 1 } } */
/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG '2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_VOLATILE" 1 } } */

View File

@ -114,6 +114,8 @@ struct btf_type
#define BTF_KIND_VAR 14 /* Variable. */ #define BTF_KIND_VAR 14 /* Variable. */
#define BTF_KIND_DATASEC 15 /* Section such as .bss or .data. */ #define BTF_KIND_DATASEC 15 /* Section such as .bss or .data. */
#define BTF_KIND_FLOAT 16 /* Floating point. */ #define BTF_KIND_FLOAT 16 /* Floating point. */
#define BTF_KIND_DECL_TAG 17 /* Declaration tag. */
#define BTF_KIND_TYPE_TAG 18 /* Type tag. */
#define BTF_KIND_ENUM64 19 /* Enumeration up to 64 bits. */ #define BTF_KIND_ENUM64 19 /* Enumeration up to 64 bits. */
#define BTF_KIND_MAX BTF_KIND_ENUM64 #define BTF_KIND_MAX BTF_KIND_ENUM64
#define NR_BTF_KINDS (BTF_KIND_MAX + 1) #define NR_BTF_KINDS (BTF_KIND_MAX + 1)
@ -227,6 +229,18 @@ struct btf_enum64
uint32_t val_hi32; /* high 32-bit value for a 64-bit value Enumerator */ uint32_t val_hi32; /* high 32-bit value for a 64-bit value Enumerator */
}; };
/* BTF_KIND_DECL_TAG is followed by a single struct btf_decl_tag, which
describes the item to which the tag applies:
- If component_idx == (uint32_t) -1, then the tag applies to item referred
to by the type_id.
- Otherwise, the tag applies to the struct or union member, or function
argument of the type referred to by type_id with the 0-based index
given by component_idx. */
struct btf_decl_tag
{
uint32_t component_idx;
};
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif