This is Emil's actual request: a btf_type_tag could not be attached to a
typedef of an already-defined struct, union or enum, e.g.
struct S { ... };
typedef struct S __attribute__((btf_type_tag ("t"))) S_t;
This is super useful for creating specialized tagged version of existing
Linux kernel structs.
This is also needed to address the gazillion kernel build warnings
"ignoring attributes applied to 'struct S' after definition"
Note that a typedef that *defines* the aggregate inline,
e.g. "typedef struct S { ... } __tag S_t;", already worked, because the
type is modified in place during its definition.
The drop happens because, for a completed aggregate, decl_attributes ->
build_type_attribute_qual_variant deliberately refuses to build an
attribute variant of a tagged type. btf_type_tag, however, does not
affect type identity, layout or the type's fields, so it is safe to carry
on a variant.
Handle this in the btf_type_tag attribute handler: when the attribute
applies to a completed RECORD/UNION/ENUM type, attach it directly to the
freshly created variant (built by build_variant_type_copy) and set
*no_add_attrs, so decl_attributes does not subsequently try (and fail) to
add it. The existing typedef BTF handling in gen_ctf_typedef then emits
BTF_KIND_TYPEDEF 'S_t' -> BTF_KIND_TYPE_TAG 't' -> BTF_KIND_STRUCT 'S'
The change is confined to the btf_type_tag handler, so only that
attribute is affected.
PR debug/125888
gcc/c-family/ChangeLog:
* c-attribs.cc (handle_btf_type_tag_attribute): Attach the tag to a
variant of a completed struct/union/enum type instead of letting it
be dropped, so it can be carried by a typedef of an existing tagged
type.
gcc/testsuite/ChangeLog:
* gcc.dg/debug/dwarf2/dwarf-btf-type-tag-6.c: Update; btf_type_tag on
an aggregate after definition is now accepted and a
DW_TAG_GNU_annotation DIE is generated.
* gcc.dg/debug/btf/btf-type-tag-8.c: New test (typedef of existing
struct/union/enum).
Signed-off-by: Vineet Gupta <[email protected]>
---
gcc/c-family/c-attribs.cc | 22 +++++++++++-
.../gcc.dg/debug/btf/btf-type-tag-8.c | 34 +++++++++++++++++++
.../debug/dwarf2/dwarf-btf-type-tag-6.c | 23 +++++++++----
3 files changed, 71 insertions(+), 8 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-8.c
diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index 88293c37e4ee..c26cd2843584 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -5286,7 +5286,27 @@ handle_btf_type_tag_attribute (tree *node, tree name,
tree args,
TREE_TYPE (decl) = *type;
}
else
- *type = build_variant_type_copy (*type);
+ {
+ *type = build_variant_type_copy (*type);
+
+ /* For an already-defined tagged type (struct/union/enum),
+ decl_attributes would drop the attribute, because
+ build_type_attribute_qual_variant refuses to attach attributes to a
+ completed aggregate. btf_type_tag does not affect type identity,
+ layout or fields, so attach it directly to the freshly created
+ variant and prevent the caller from trying (and failing) to add it.
+ This is needed for a typedef of an existing tagged type, e.g.
+ struct S { ... };
+ typedef struct S __attribute__((btf_type_tag ("t"))) S_t;
+ (PR125888). */
+ if (RECORD_OR_UNION_TYPE_P (*type)
+ || TREE_CODE (*type) == ENUMERAL_TYPE)
+ {
+ TYPE_ATTRIBUTES (*type) = tree_cons (name, args,
+ TYPE_ATTRIBUTES (*type));
+ *no_add_attrs = true;
+ }
+ }
}
return NULL_TREE;
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-8.c
b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-8.c
new file mode 100644
index 000000000000..e15506a202d9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-8.c
@@ -0,0 +1,34 @@
+/* Test btf_type_tag on a typedef of an already-defined struct, union or enum.
+ PR/125888.
+
+ Unlike a typedef that defines the aggregate inline (btf-type-tag-6.c), here
+ the aggregate is defined first and a separate typedef adds the tag. The tag
+ is recorded as a BTF_KIND_TYPE_TAG inserted between the typedef and the
+ aggregate:
+ S_t -> type_tag("st") -> struct S
+ U_t -> type_tag("un") -> union U
+ E_t -> type_tag("en") -> enum E */
+
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+#define __t(x) __attribute__((btf_type_tag (x)))
+
+struct S { int x; };
+union U { int a; long b; };
+enum E { E0, E1 };
+
+typedef struct S __t ("st") S_t;
+typedef union U __t ("un") U_t;
+typedef enum E __t ("en") E_t;
+
+S_t s;
+U_t u;
+E_t e;
+
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPEDEF
'S_t'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'st'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG
'st'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_STRUCT 'S'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPEDEF
'U_t'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'un'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG
'un'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_UNION 'U'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPEDEF
'E_t'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'en'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG
'en'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_ENUM 'E'\\)" 1 } } */
diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-6.c
b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-6.c
index 11fc9036b8a2..24bcd9836b7a 100644
--- a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-6.c
+++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-btf-type-tag-6.c
@@ -1,6 +1,9 @@
/* Test generation for btf_type_tag attribute when applied to struct/union
- types after definition. Attributes applied after definition will be
- ignored, so DW_TAG_GNU_annotations shall be generated. */
+ types after definition. PR/125888.
+
+ btf_type_tag does not affect type identity, layout or fields, so it is now
+ accepted on already-defined aggregate types (and on typedefs of them) and a
+ DW_TAG_GNU_annotation DIE is generated for each. */
/* { dg-do compile } */
/* { dg-options "-gdwarf -dA" } */
@@ -10,9 +13,9 @@ struct foo
char c;
};
-struct foo __attribute__((btf_type_tag ("tag1"))) x; /* { dg-warning
"ignoring attribute" } */
+struct foo __attribute__((btf_type_tag ("tag1"))) x; /* { dg-bogus "ignoring
attribute" } */
typedef const struct foo c_foo;
-c_foo __attribute__((btf_type_tag ("tag2"))) y; /* { dg-warning "ignoring
attribute" } */
+c_foo __attribute__((btf_type_tag ("tag2"))) y; /* { dg-bogus "ignoring
attribute" } */
union bar
{
@@ -20,8 +23,14 @@ union bar
unsigned int u;
};
-typedef union bar __attribute__((btf_type_tag("tag3"))) tag_bar; /* {
dg-warning "ignoring attribute" } */
+typedef union bar __attribute__((btf_type_tag("tag3"))) tag_bar; /* { dg-bogus
"ignoring attribute" } */
const tag_bar z;
-/* { dg-final { scan-assembler-not "DIE \\(\[^\n\]*\\) DW_TAG_GNU_annotation"
} } */
-/* { dg-final { scan-assembler-not " DW_AT_GNU_annotation" } } */
+/* Each tag generates a DW_TAG_GNU_annotation DIE. The DW_AT_const_value
+ patterns accept either the strp form ("DW_AT_const_value: \"tag\"") or the
+ inline-string form (".ascii \"tag\\0\" ... DW_AT_const_value") so the test
+ works on targets that do not pool debug strings (e.g. bpf). */
+/* { dg-final { scan-assembler-times {(?n)DIE \(.*\) DW_TAG_GNU_annotation} 3
} } */
+/* { dg-final { scan-assembler-times {(?n)( DW_AT_const_value:
"tag1"|"tag1..".* DW_AT_const_value)} 1 } } */
+/* { dg-final { scan-assembler-times {(?n)( DW_AT_const_value:
"tag2"|"tag2..".* DW_AT_const_value)} 1 } } */
+/* { dg-final { scan-assembler-times {(?n)( DW_AT_const_value:
"tag3"|"tag3..".* DW_AT_const_value)} 1 } } */
--
2.54.0