https://gcc.gnu.org/g:2061ee1fbd17cadf7b840e03c6e881d0c508a296

commit 2061ee1fbd17cadf7b840e03c6e881d0c508a296
Author: Alfie Richards <alfie.richa...@arm.com>
Date:   Wed Apr 2 14:24:00 2025 +0000

    c: Improve diagnostics for C FMV declaration conflicts.
    
    Improves diagnostic messages for conflicting function multiversioning (FMV)
    declarations using target_version and/or target_clones attributes.
    
    Conflict errors now include the overlapping version string (if relevant),
    making it easier to identify and resolve declaration mismatches.
    
    gcc/c/ChangeLog:
    
            * c-decl.cc (diagnose_mismatched_decls): Add conflicting_ver 
argument
            and update diagnostics with it.
            (duplicate_decls): Ditto.
            (pushdecl): Add conflicting_version variable update logic to use it.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/aarch64/mv-and-mvc-error1.c: New test.
            * gcc.target/aarch64/mv-and-mvc-error2.c: New test.
            * gcc.target/aarch64/mv-and-mvc-error3.c: New test.
            * gcc.target/aarch64/mv-error1.c: New test.
            * gcc.target/aarch64/mv-error2.c: New test.
            * gcc.target/aarch64/mv-error3.c: New test.
            * gcc.target/aarch64/mv-error4.c: New test.
            * gcc.target/aarch64/mv-error5.c: New test.
            * gcc.target/aarch64/mv-error6.c: New test.
            * gcc.target/aarch64/mv-error7.c: New test.
            * gcc.target/aarch64/mv-error8.c: New test.
            * gcc.target/aarch64/mv-error9.c: New test.
            * gcc.target/aarch64/mvc-error1.c: New test.
            * gcc.target/aarch64/mvc-error2.c: New test.
            * gcc.target/aarch64/mvc-warning1.c: New test.

Diff:
---
 gcc/c/c-decl.cc                                    | 37 ++++++++++++++++------
 .../gcc.target/aarch64/mv-and-mvc-error1.c         |  9 ++++++
 .../gcc.target/aarch64/mv-and-mvc-error2.c         |  9 ++++++
 .../gcc.target/aarch64/mv-and-mvc-error3.c         |  8 +++++
 gcc/testsuite/gcc.target/aarch64/mv-error1.c       | 18 +++++++++++
 gcc/testsuite/gcc.target/aarch64/mv-error2.c       |  9 ++++++
 gcc/testsuite/gcc.target/aarch64/mv-error3.c       | 12 +++++++
 gcc/testsuite/gcc.target/aarch64/mv-error4.c       |  9 ++++++
 gcc/testsuite/gcc.target/aarch64/mv-error5.c       |  8 +++++
 gcc/testsuite/gcc.target/aarch64/mv-error6.c       | 20 ++++++++++++
 gcc/testsuite/gcc.target/aarch64/mv-error7.c       | 11 +++++++
 gcc/testsuite/gcc.target/aarch64/mv-error8.c       | 12 +++++++
 gcc/testsuite/gcc.target/aarch64/mv-error9.c       | 12 +++++++
 gcc/testsuite/gcc.target/aarch64/mvc-error1.c      |  9 ++++++
 gcc/testsuite/gcc.target/aarch64/mvc-error2.c      |  9 ++++++
 gcc/testsuite/gcc.target/aarch64/mvc-warning1.c    | 13 ++++++++
 16 files changed, 195 insertions(+), 10 deletions(-)

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 977e6ef37878..915743eb5ec3 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2119,8 +2119,8 @@ maybe_mark_function_versioned (tree decl)
    TREE_TYPE (NEWDECL, OLDDECL) respectively.  */
 
 static bool
-diagnose_mismatched_decls (tree newdecl, tree olddecl,
-                          tree *newtypep, tree *oldtypep)
+diagnose_mismatched_decls (tree newdecl, tree olddecl, tree *newtypep,
+                          tree *oldtypep, string_slice *conflicting_ver = NULL)
 {
   tree newtype, oldtype;
   bool retval = true;
@@ -2448,7 +2448,12 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
                                                DECL_ATTRIBUTES (newdecl)))))
                {
                  auto_diagnostic_group d;
-                 error ("redefinition of %q+D", newdecl);
+                 if (conflicting_ver && conflicting_ver->is_valid ())
+                   error ("redefinition of %qB version for %q+D",
+                          conflicting_ver,
+                          newdecl);
+                 else
+                   error ("redefinition of %q+D", newdecl);
                  locate_old_decl (olddecl);
                  return false;
                }
@@ -3188,20 +3193,29 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, 
tree oldtype)
    true.  Otherwise, return false.  */
 
 static bool
-duplicate_decls (tree newdecl, tree olddecl)
+duplicate_decls (tree newdecl, tree olddecl,
+                string_slice *conflicting_ver = NULL)
 {
   tree newtype = NULL, oldtype = NULL;
 
   if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
+      && TREE_CODE (olddecl) == FUNCTION_DECL
+      && TREE_CODE (newdecl) == FUNCTION_DECL
       && !mergeable_version_decls (olddecl, newdecl))
     {
       auto_diagnostic_group d;
-      error ("conflicting versioned declarations of %q+D", newdecl);
+      if (conflicting_ver && conflicting_ver->is_valid ())
+       error ("conflicting %qB version declarations of %q+D",
+              conflicting_ver,
+              newdecl);
+      else
+       error ("conflicting versioned declarations of %q+D", newdecl);
       locate_old_decl (olddecl);
       return false;
     }
 
-  if (!diagnose_mismatched_decls (newdecl, olddecl, &newtype, &oldtype))
+  if (!diagnose_mismatched_decls (newdecl, olddecl, &newtype, &oldtype,
+                                 conflicting_ver))
     {
       /* Avoid `unused variable' and other warnings for OLDDECL.  */
       suppress_warning (olddecl, OPT_Wunused);
@@ -3388,6 +3402,7 @@ pushdecl (tree x)
       tree type = TREE_TYPE (x);
       tree visdecl = b->decl;
       tree vistype = TREE_TYPE (visdecl);
+      string_slice conflicting_version = string_slice::invalid ();
       if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE
          && COMPLETE_TYPE_P (TREE_TYPE (x)))
        b->inner_comp = false;
@@ -3413,7 +3428,7 @@ pushdecl (tree x)
       if (b_use && TREE_CODE (b_use->decl) == FUNCTION_DECL
          && TREE_CODE (x) == FUNCTION_DECL && DECL_FILE_SCOPE_P (b_use->decl)
          && DECL_FILE_SCOPE_P (x)
-         && distinct_version_decls (x, b_use->decl)
+         && distinct_version_decls (x, b_use->decl, &conflicting_version)
          && comptypes (vistype, type) != 0)
        {
          if (current_scope->depth != b->depth)
@@ -3432,11 +3447,13 @@ pushdecl (tree x)
             in the set.  */
          cgraph_function_version_info *version = b_v;
          for (; version; version = version->next)
-           if (!distinct_version_decls (version->this_node->decl, x))
+           if (!distinct_version_decls (version->this_node->decl, x,
+                                        &conflicting_version))
              {
                /* The decls define overlapping version, so attempt to merge
                   or diagnose the conflict.  */
-               if (duplicate_decls (x, version->this_node->decl))
+               if (duplicate_decls (x, version->this_node->decl,
+                                    &conflicting_version))
                  return version->this_node->decl;
                else
                  return error_mark_node;
@@ -3458,7 +3475,7 @@ pushdecl (tree x)
          return x;
        }
 
-      if (duplicate_decls (x, b_use->decl))
+      if (duplicate_decls (x, b_use->decl, &conflicting_version))
        {
          if (b_use != b)
            {
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error1.c 
b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error1.c
new file mode 100644
index 000000000000..6a46a053475d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error1.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("dotprod"))) int
+foo () { return 3; } /* { dg-message "previous definition of .foo." } */
+
+__attribute__ ((target_clones ("dotprod", "sve"))) int
+foo () { return 1; } /* { dg-error "conflicting .dotprod. version declarations 
of .foo." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error2.c 
b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error2.c
new file mode 100644
index 000000000000..e34771e029e0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error2.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("default"))) int
+foo () { return 1; } /* { dg-message "previous definition of .foo." } */
+
+__attribute__ ((target_clones ("dotprod", "sve"))) float
+foo () { return 3; } /* { dg-error "conflicting versioned declarations of 
.foo." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error3.c 
b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error3.c
new file mode 100644
index 000000000000..28717a4f89df
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error3.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+float foo () { return 1; } /* { dg-message "previous definition of .foo." } */
+
+__attribute__ ((target_clones ("default", "dotprod", "sve"))) float
+foo () { return 3; } /* { dg-error "redefinition of .default. version for 
.foo." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error1.c 
b/gcc/testsuite/gcc.target/aarch64/mv-error1.c
new file mode 100644
index 000000000000..23d1a0d89f73
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error1.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("default"))) int
+foo ();
+
+__attribute__ ((target_version ("default"))) int
+foo () { return 1; } /* { dg-message "previous definition of .foo." "note"} */
+
+__attribute__ ((target_version ("dotprod"))) float
+foo () { return 3; } /* { dg-error "conflicting versioned declarations of 
.foo." } */
+
+__attribute__ ((target_version ("sve"))) int
+foo2 () { return 1; } /* { dg-message "previous definition of .foo2." "note" } 
*/
+
+__attribute__ ((target_version ("dotprod"))) float
+foo2 () { return 3; } /* { dg-error "conflicting versioned declarations of 
.foo2." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error2.c 
b/gcc/testsuite/gcc.target/aarch64/mv-error2.c
new file mode 100644
index 000000000000..989cc56fc7d0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error2.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("dotprod"))) float
+foo () { return 3; } /* { dg-message "previous definition of .foo." "note" } */
+
+__attribute__ ((target_version ("dotprod"))) float
+foo () { return 3; } /* { dg-error "redefinition of .dotprod. version for 
.foo." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error3.c 
b/gcc/testsuite/gcc.target/aarch64/mv-error3.c
new file mode 100644
index 000000000000..e814bac4ef19
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error3.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("dotprod"))) float
+foo () { return 3; }
+
+__attribute__ ((target_version ("default"))) float
+foo () { return 3; } /* { dg-message "previous definition of .foo." "note" } */
+
+__attribute__ ((target_version ("default"))) float
+foo () { return 3; } /* { dg-error "redefinition of .default. version for 
.foo." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error4.c 
b/gcc/testsuite/gcc.target/aarch64/mv-error4.c
new file mode 100644
index 000000000000..44d3195590d2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error4.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("test"))) float
+foo () { return 3; } /* { dg-error "invalid feature modifier .test. of value 
.test. in .target_version. attribute" } */
+
+__attribute__ ((target_version ("sve+test"))) float
+foo2 () { return 3; } /* { dg-error "invalid feature modifier .test. of value 
.sve.test. in .target_version. attribute" } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error5.c 
b/gcc/testsuite/gcc.target/aarch64/mv-error5.c
new file mode 100644
index 000000000000..c0b8a9c2b5c1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error5.c
@@ -0,0 +1,8 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo(); /* { dg-message "previous declaration of .foo. with type " } */
+
+int bar () { return foo (); } /* { dg-error "implicit declaration of function 
.foo." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error6.c 
b/gcc/testsuite/gcc.target/aarch64/mv-error6.c
new file mode 100644
index 000000000000..7eecf787a50f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error6.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo () { /* { dg-message "previous definition of .foo." "note" } */
+       return 1;
+}
+
+__attribute__ ((target_version ("sve"))) int
+foo () {
+       return 1;
+}
+
+int bar () { return foo (); } /* { dg-error "implicit declaration of function 
.foo." } */
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo2(); /* { dg-message "previous declaration of .foo2." "note" } */
+
+int bar2 () { return foo2 (); } /* { dg-error "implicit declaration of 
function .foo2." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error7.c 
b/gcc/testsuite/gcc.target/aarch64/mv-error7.c
new file mode 100644
index 000000000000..ccce201cf306
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error7.c
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("dotprod"))) int
+foo (); /* { dg-message "previous declaration of .foo." } */
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ();
+
+int bar () { return foo (); } /* { dg-error "implicit declaration of function 
.foo." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error8.c 
b/gcc/testsuite/gcc.target/aarch64/mv-error8.c
new file mode 100644
index 000000000000..7599df189e41
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error8.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("default"))) int
+foo (int a, int (*b)[4]) { return 1; }
+
+int bar(void) {
+  __attribute__ ((target_version ("dotprod"))) int
+  foo (int a, int (*b)[5]) { return 3; } /* { dg-error "versioned definitions 
are only allowed at file scope" } */
+
+  return 1;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-error9.c 
b/gcc/testsuite/gcc.target/aarch64/mv-error9.c
new file mode 100644
index 000000000000..04a2037b98c8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-error9.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("dotprod"))) int
+foo (); /* { dg-message "previous declaration of .foo." } */
+
+int
+bar ()
+{
+  return foo (); /* { dg-error "implicit declaration of function .foo." } */
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-error1.c 
b/gcc/testsuite/gcc.target/aarch64/mvc-error1.c
new file mode 100644
index 000000000000..1f9f557a04be
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mvc-error1.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_clones ("default, dotprod"))) float
+foo (); /* { dg-message "previous declaration of .foo." } */
+
+__attribute__ ((target_clones ("dotprod", "sve"))) float
+foo () { return 3; } /* { dg-error "conflicting .dotprod. version declarations 
of .foo." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-error2.c 
b/gcc/testsuite/gcc.target/aarch64/mvc-error2.c
new file mode 100644
index 000000000000..0c47ea997315
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mvc-error2.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_clones ("default, dotprod"))) float
+foo () { return 3; } /* { dg-message "previous definition of .foo." } */
+
+__attribute__ ((target_clones ("dotprod", "sve"))) float
+foo () { return 3; } /* { dg-error "conflicting .dotprod. version declarations 
of .foo." } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-warning1.c 
b/gcc/testsuite/gcc.target/aarch64/mvc-warning1.c
new file mode 100644
index 000000000000..bc86f024d842
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mvc-warning1.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_clones("default", "dotprod", "sve+sve2")))
+int foo () {
+  return 1;
+}
+
+__attribute__((target_clones("invalid1")))
+int foo () { /* { dg-warning "invalid .target_clones. version .invalid1. 
ignored" } */
+  return 2;
+}

Reply via email to