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.
---
 gcc/c/c-decl.cc                               | 35 +++++++++++++------
 .../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.target/aarch64/mvc-warning1.c         | 13 +++++++
 16 files changed, 193 insertions(+), 10 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error2.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-and-mvc-error3.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error2.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error3.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error4.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error5.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error6.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error7.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error8.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-error9.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mvc-error1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mvc-error2.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mvc-warning1.c

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index e68f8e0cdeb..d5cd31c40a2 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;
 		}
@@ -3185,7 +3190,8 @@ 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;
 
@@ -3193,12 +3199,18 @@ duplicate_decls (tree newdecl, tree olddecl)
       && !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);
@@ -3385,6 +3397,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;
@@ -3410,7 +3423,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)
@@ -3429,11 +3442,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;
@@ -3455,7 +3470,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 00000000000..6a46a053475
--- /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 00000000000..e34771e029e
--- /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 00000000000..28717a4f89d
--- /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 00000000000..23d1a0d89f7
--- /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 00000000000..989cc56fc7d
--- /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 00000000000..e814bac4ef1
--- /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 00000000000..44d3195590d
--- /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 00000000000..c0b8a9c2b5c
--- /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 00000000000..7eecf787a50
--- /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 00000000000..ccce201cf30
--- /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 00000000000..7599df189e4
--- /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 00000000000..04a2037b98c
--- /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 00000000000..1f9f557a04b
--- /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 00000000000..0c47ea99731
--- /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 00000000000..bc86f024d84
--- /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