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

Reply via email to