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