A btf_type_tag for function return value of pointer, e.g.
int __attribute__((btf_type_tag ("user"))) * f (void);
was rejected with a "does not apply to functions" warning and dropped, so
no type tag was recorded for the return type in DWARF or BTF.
This is not ideal for following reasons:
- btf_type_tags are allowed on regular pointers (in fact the only thing
they are allowed on) so function returned pointers should be treated
similarly.
- Associating this to function itself seems wrong syntactically.
- Clang supports this construct.
- Linux kernel has extensive use of this construct for functions
returning percpu pointers - so not having this support causes tons of
build spew when enabling support for gcc generated btf_{decl/type}_tag.
The workaround was -Wno-attribute which looses an otherwise good
diagnostic.
So handle this in the C-family attribute handler: when the attribute
lands on a FUNCTION_TYPE whose return type is a POINTER_TYPE, rebuild
the function type with a tagged variant of that pointer return type.
A non-pointer return type (and METHOD_TYPE) is left rejected with the
existing warning, matching clang's "pointer types only" model.
No change is needed in the BTF backend: the tagged pointer return type is
emitted by the existing gen_ctf_pointer_type handling, producing
f: FUNC_PROTO -> ptr -> type_tag("user") -> ...
Type tags reaching a function's return or arguments through a typedef are
handled when generating the typedef (see "btf: emit BTF type tags for
typedefs").
PR debug/125991
gcc/c-family/ChangeLog:
* c-attribs.cc (handle_btf_type_tag_attribute): When the attribute
applies to a FUNCTION_TYPE whose return type is a pointer, apply it
to that pointer return type by rebuilding the function type. Keep
warning for a non-pointer return type and for METHOD_TYPE.
gcc/ChangeLog:
* doc/extend.texi (btf_type_tag): Document that on a function the
attribute tags a pointer return type.
gcc/testsuite/ChangeLog:
* gcc.dg/attr-btf-type-tag-3.c: Update; btf_type_tag on a function
with a pointer return type is now accepted, a non-pointer return
type still warns.
* gcc.dg/debug/btf/btf-type-tag-9.c: New test.
* gcc.dg/debug/btf/btf-type-tag-10.c: New test.
* gcc.dg/debug/btf/btf-type-tag-11.c: New test.
* gcc.dg/debug/btf/btf-type-tag-12.c: New test.
* gcc.dg/debug/btf/btf-type-tag-13.c: New test.
* gcc.dg/debug/btf/btf-type-tag-14.c: New test.
* gcc.dg/debug/btf/btf-type-tag-c2x-2.c: New test.
Signed-off-by: Vineet Gupta <[email protected]>
---
gcc/c-family/c-attribs.cc | 23 +++++++++++++
gcc/doc/extend.texi | 4 +++
gcc/testsuite/gcc.dg/attr-btf-type-tag-3.c | 12 +++++--
.../gcc.dg/debug/btf/btf-type-tag-10.c | 14 ++++++++
.../gcc.dg/debug/btf/btf-type-tag-11.c | 16 +++++++++
.../gcc.dg/debug/btf/btf-type-tag-12.c | 19 +++++++++++
.../gcc.dg/debug/btf/btf-type-tag-13.c | 12 +++++++
.../gcc.dg/debug/btf/btf-type-tag-14.c | 34 +++++++++++++++++++
.../gcc.dg/debug/btf/btf-type-tag-9.c | 16 +++++++++
.../gcc.dg/debug/btf/btf-type-tag-c2x-2.c | 22 ++++++++++++
10 files changed, 169 insertions(+), 3 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-10.c
create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-11.c
create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-12.c
create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-13.c
create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-14.c
create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-9.c
create mode 100644 gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-c2x-2.c
diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index c26cd2843584..b2fcc811291d 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -5244,6 +5244,29 @@ handle_btf_type_tag_attribute (tree *node, tree name,
tree args,
return NULL_TREE;
}
+ /* A btf_type_tag is a type tag; following clang, it is only meaningful on a
+ pointer type. On a function it applies to the return type, but only when
+ that return type is a pointer: rebuild the function type with a tagged
+ variant of the pointer return type. A non-pointer return type (or a
+ METHOD_TYPE) does not carry the tag, which is dropped with a warning. */
+ if (TREE_CODE (*node) == FUNCTION_TYPE
+ && TREE_CODE (TREE_TYPE (*node)) == POINTER_TYPE)
+ {
+ tree fntype = *node;
+ tree ret = TREE_TYPE (fntype);
+ tree newret
+ = build_type_attribute_variant (ret,
+ tree_cons (name, args,
+ TYPE_ATTRIBUTES (ret)));
+ tree newfn = build_function_type (newret, TYPE_ARG_TYPES (fntype));
+ /* Preserve any attributes already on the function type itself. */
+ if (TYPE_ATTRIBUTES (fntype))
+ newfn = build_type_attribute_variant (newfn, TYPE_ATTRIBUTES (fntype));
+ *node = newfn;
+ *no_add_attrs = true;
+ return NULL_TREE;
+ }
+
if (TREE_CODE (*node) == FUNCTION_TYPE || TREE_CODE (*node) == METHOD_TYPE)
{
warning (OPT_Wattributes,
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index dec09f599ced..b013938abe88 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2325,6 +2325,10 @@ information is generated, the attribute has no effect.
The argument is treated as a null-terminated sequence of zero or more
non-null bytes. Wide character strings are not supported.
+When applied to a function whose return type is a pointer, the attribute
+tags that pointer return type. On a function with a non-pointer return
+type the attribute has no effect.
+
The attribute may be supplied multiple times for a single type, in
which case each distinct argument string will be recorded in a
separate DIE or BTF record, each associated to the type. For a single
diff --git a/gcc/testsuite/gcc.dg/attr-btf-type-tag-3.c
b/gcc/testsuite/gcc.dg/attr-btf-type-tag-3.c
index afb14b12a22f..ad8c623f45fc 100644
--- a/gcc/testsuite/gcc.dg/attr-btf-type-tag-3.c
+++ b/gcc/testsuite/gcc.dg/attr-btf-type-tag-3.c
@@ -1,8 +1,14 @@
-/* Test btf_type_tag attribute warnings. */
+/* Test btf_type_tag attribute on function return types.
+
+ Following clang, btf_type_tag is only meaningful on a pointer type. On a
+ function it applies to the return type only when that return type is a
+ pointer; a non-pointer return type still warns and the tag is dropped. */
/* { dg-do compile } */
+/* Non-pointer return: tag does not apply, warning expected. */
int __attribute__((btf_type_tag ("A"))) a (int x); /* { dg-warning "does not
apply to functions" } */
-__attribute__((btf_type_tag ("B"))) int *b (int y); /* { dg-warning "does not
apply to functions" } */
+/* Pointer return: tag applies to the return type, accepted. */
+__attribute__((btf_type_tag ("B"))) int *b (int y); /* { dg-bogus "does not
apply to functions" } */
-int *c (int z) __attribute__((btf_type_tag ("C"))); /* { dg-warning "does not
apply to functions" } */
+int *c (int z) __attribute__((btf_type_tag ("C"))); /* { dg-bogus "does not
apply to functions" } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-10.c
b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-10.c
new file mode 100644
index 000000000000..b3a65c6d21f3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-10.c
@@ -0,0 +1,14 @@
+/* Test btf_type_tag on a function's pointer return type with the
+ __attribute__ placed immediately after the '*' (i.e. on the pointer
+ itself). The tag applies to the pointer return type. PR/125991.
+
+ f: FUNC_PROTO -> ptr -> type_tag("A") -> int */
+
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+int * __attribute__((btf_type_tag ("A"))) f (int x) { return 0; }
+
+/* { dg-final { scan-assembler-times " BTF_KIND_FUNC_PROTO
''(\[\\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 'A'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG
'A'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT 'int'\\)" 1 } } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-11.c
b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-11.c
new file mode 100644
index 000000000000..3f29abe61cbb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-11.c
@@ -0,0 +1,16 @@
+/* Test btf_type_tag on a function return type, attribute in the prefix
+ position (before all declaration specifiers). PR/125991.
+
+ __attribute__((btf_type_tag("B"))) int *b (int y);
+
+ The tag applies to the (pointer) return type:
+ b: FUNC_PROTO -> ptr -> type_tag("B") -> int */
+
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+__attribute__((btf_type_tag ("B"))) int *b (int y) { return 0; }
+
+/* { dg-final { scan-assembler-times " BTF_KIND_FUNC_PROTO
''(\[\\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 'B'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG
'B'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT 'int'\\)" 1 } } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-12.c
b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-12.c
new file mode 100644
index 000000000000..cfe098f3f0b2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-12.c
@@ -0,0 +1,19 @@
+/* Test btf_type_tag on a function return type, attribute in the postfix
+ position (after the declarator). PR/125991.
+
+ int *c (int z) __attribute__((btf_type_tag("C")));
+
+ A postfix attribute is not permitted on a function definition, so 'c' is
+ declared and referenced via a caller to force BTF emission. The tag applies
+ to the (pointer) return type:
+ c: FUNC_PROTO -> ptr -> type_tag("C") -> int */
+
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+int *c (int z) __attribute__((btf_type_tag ("C")));
+
+int *caller (int z) { return c (z); }
+
+/* { dg-final { scan-assembler-times " BTF_KIND_PTR
''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'C'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG
'C'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT 'int'\\)" 1 } } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-13.c
b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-13.c
new file mode 100644
index 000000000000..d18d405b3d8e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-13.c
@@ -0,0 +1,12 @@
+/* Test that btf_type_tag on a non-pointer function return type is dropped:
+ it is only meaningful on a pointer type, so no BTF_KIND_TYPE_TAG record is
+ emitted and a warning is issued. PR/125991. */
+
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+int __attribute__((btf_type_tag ("dropped"))) f (int x) { return x; } /* {
dg-warning "does not apply to functions" } */
+
+/* The function is emitted, but with no type tag on its return type. */
+/* { dg-final { scan-assembler "BTF_KIND_FUNC 'f'" } } */
+/* { dg-final { scan-assembler-not "BTF_KIND_TYPE_TAG" } } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-14.c
b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-14.c
new file mode 100644
index 000000000000..335d3efa1614
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-14.c
@@ -0,0 +1,34 @@
+/* Test generation of BTF type tags on a function's pointer return type and on
+ its arguments. PR/125991.
+
+ btf_type_tag is only meaningful on a pointer type, so the directly-tagged
+ return must be a pointer; here it carries two tags. The arguments are
+ tagged via typedefs. Each tagged type results in a BTF_KIND_TYPE_TAG record
+ inserted into the appropriate chain of the BTF_KIND_FUNC_PROTO:
+
+ func: FUNC_PROTO -> ret ptr -> type_tag("type2") -> type_tag("type1") ->
int
+ -> arg0 typedef("TYP1") -> type_tag("type1") -> int
+ -> arg1 typedef("TYP2") -> type_tag("type2") -> int */
+
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+#define __tag1 __attribute__((btf_type_tag ("type1")))
+#define __tag2 __attribute__((btf_type_tag ("type2")))
+
+typedef int __tag1 TYP1;
+typedef int __tag2 TYP2;
+
+int __tag1 __tag2 * func (TYP1 arg_a, TYP2 arg_b)
+{
+ return 0;
+}
+
+/* Return is a pointer carrying type2 -> type1. */
+/* { dg-final { scan-assembler-times " BTF_KIND_FUNC_PROTO
''(\[\\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_TYPE_TAG 'type1'\\)" 1 } } */
+
+/* Argument chains via typedefs. */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPEDEF
'TYP1'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'type1'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPEDEF
'TYP2'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'type2'\\)" 1 } } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-9.c
b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-9.c
new file mode 100644
index 000000000000..80f5e26c5315
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-9.c
@@ -0,0 +1,16 @@
+/* Test btf_type_tag on a function pointer return type, attribute in the
+ return-type declaration-specifier position (before the '*'). PR/125991.
+
+ int __attribute__((btf_type_tag("A"))) * a (int x);
+
+ The tag applies to the pointer return type:
+ a: FUNC_PROTO -> ptr -> type_tag("A") -> int */
+
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA" } */
+
+int __attribute__((btf_type_tag ("A"))) * a (int x) { return 0; }
+
+/* { dg-final { scan-assembler-times " BTF_KIND_FUNC_PROTO
''(\[\\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 'A'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG
'A'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT 'int'\\)" 1 } } */
diff --git a/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-c2x-2.c
b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-c2x-2.c
new file mode 100644
index 000000000000..7e5db8f718df
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/debug/btf/btf-type-tag-c2x-2.c
@@ -0,0 +1,22 @@
+/* Test btf_type_tag on a function's pointer return type using C23 standard
+ attribute syntax. C23 attributes do not "slide" (unlike __attribute__), so
+ the tag applies to the pointer return type only when it appears after the
+ '*'. Before the '*' it applies to the pointee, which is a non-pointer and
+ is dropped. PR/125991. */
+
+/* { dg-do compile } */
+/* { dg-options "-O0 -gbtf -dA -std=c23" } */
+
+#define __tag_after [[gnu::btf_type_tag ("after")]]
+#define __tag_before [[gnu::btf_type_tag ("before")]]
+
+/* After '*': tags the pointer return type.
+ f: FUNC_PROTO -> ptr -> type_tag("after") -> int */
+int * __tag_after f (int x) { return 0; }
+
+/* Before '*': tags the pointee 'int' (non-pointer) -> dropped, no tag. */
+int __tag_before * g (int x) { return 0; }
+
+/* { dg-final { scan-assembler-times " BTF_KIND_PTR
''(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_TYPE_TAG 'after'\\)" 1 } } */
+/* { dg-final { scan-assembler-times " BTF_KIND_TYPE_TAG
'after'(\[\\r\\n\]+\[^\\r\\n\]*){2}\\(BTF_KIND_INT 'int'\\)" 1 } } */
+/* { dg-final { scan-assembler-not "BTF_KIND_TYPE_TAG 'before'" } } */
--
2.54.0