This patch adds support for the combination of target_clones and
target_version in the definition of a versioned function.

This patch changes is_function_default_version to consider a function
declaration annotated with target_clones containing default to be a
default version. It also changes the common_function_version hook to
consider two functions annotated with target_clones and/or
target_versions to be common if their specified versions don't overlap.

This takes advantage of refactoring done in previous patches changing
how target_clones are expanded.

gcc/ChangeLog:

        * attribs.cc (is_function_default_version): Add logic for
        target_clones defining the default version.
        * config/aarch64/aarch64.cc (aarch64_common_function_versions): Add
        logic for a target_clones and target_version, or two target_clones
        coexisting in a version set.

gcc/c-family/ChangeLog:

        * c-attribs.cc: Add support for target_version and target_clones
        coexisting.

gcc/testsuite/ChangeLog:

        * g++.target/aarch64/mv-and-mvc1.C: New test.
        * g++.target/aarch64/mv-and-mvc2.C: New test.
        * g++.target/aarch64/mv-and-mvc3.C: New test.
        * g++.target/aarch64/mv-and-mvc4.C: New test.
---
 gcc/attribs.cc                                |  7 +++
 gcc/c-family/c-attribs.cc                     |  2 -
 gcc/config/aarch64/aarch64.cc                 | 46 ++++++++++++++++++-
 .../g++.target/aarch64/mv-and-mvc1.C          | 38 +++++++++++++++
 .../g++.target/aarch64/mv-and-mvc2.C          | 29 ++++++++++++
 .../g++.target/aarch64/mv-and-mvc3.C          | 41 +++++++++++++++++
 .../g++.target/aarch64/mv-and-mvc4.C          | 38 +++++++++++++++
 gcc/testsuite/g++.target/aarch64/mv-error1.C  | 13 ++++++
 gcc/testsuite/g++.target/aarch64/mv-error13.C | 13 ++++++
 gcc/testsuite/g++.target/aarch64/mv-error2.C  | 10 ++++
 gcc/testsuite/g++.target/aarch64/mv-error3.C  | 13 ++++++
 gcc/testsuite/g++.target/aarch64/mv-error7.C  |  9 ++++
 gcc/testsuite/g++.target/aarch64/mv-error8.C  | 21 +++++++++
 gcc/testsuite/g++.target/aarch64/mv-error9.C  | 12 +++++
 14 files changed, 289 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/g++.target/aarch64/mv-and-mvc1.C
 create mode 100644 gcc/testsuite/g++.target/aarch64/mv-and-mvc2.C
 create mode 100644 gcc/testsuite/g++.target/aarch64/mv-and-mvc3.C
 create mode 100644 gcc/testsuite/g++.target/aarch64/mv-and-mvc4.C
 create mode 100644 gcc/testsuite/g++.target/aarch64/mv-error1.C
 create mode 100644 gcc/testsuite/g++.target/aarch64/mv-error13.C
 create mode 100644 gcc/testsuite/g++.target/aarch64/mv-error2.C
 create mode 100644 gcc/testsuite/g++.target/aarch64/mv-error3.C
 create mode 100644 gcc/testsuite/g++.target/aarch64/mv-error7.C
 create mode 100644 gcc/testsuite/g++.target/aarch64/mv-error8.C
 create mode 100644 gcc/testsuite/g++.target/aarch64/mv-error9.C

diff --git a/gcc/attribs.cc b/gcc/attribs.cc
index 687e6d4143a..f877dc4f6e3 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -1327,6 +1327,13 @@ is_function_default_version (const tree decl)
     }
   else
     {
+      if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (decl)))
+	{
+	  int num_def = 0;
+	  auto_vec<string_slice> versions = get_clone_versions (decl, &num_def);
+	  return num_def > 0;
+	}
+
       attr = lookup_attribute ("target_version", DECL_ATTRIBUTES (decl));
       if (!attr)
 	return true;
diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index 642d724f6c6..f2cc43ad641 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -249,13 +249,11 @@ static const struct attribute_spec::exclusions attr_target_clones_exclusions[] =
   ATTR_EXCL ("always_inline", true, true, true),
   ATTR_EXCL ("target", TARGET_HAS_FMV_TARGET_ATTRIBUTE,
 	     TARGET_HAS_FMV_TARGET_ATTRIBUTE, TARGET_HAS_FMV_TARGET_ATTRIBUTE),
-  ATTR_EXCL ("target_version", true, true, true),
   ATTR_EXCL (NULL, false, false, false),
 };
 
 static const struct attribute_spec::exclusions attr_target_version_exclusions[] =
 {
-  ATTR_EXCL ("target_clones", true, true, true),
   ATTR_EXCL (NULL, false, false, false),
 };
 
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 420bbba9be2..f6cb7903d88 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -20671,7 +20671,51 @@ aarch64_common_function_versions (tree fn1, tree fn2)
       || TREE_CODE (fn2) != FUNCTION_DECL)
     return false;
 
-  return (aarch64_compare_version_priority (fn1, fn2) != 0);
+  if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (fn2)))
+    {
+      tree temp = fn1;
+      fn1 = fn2;
+      fn2 = temp;
+    }
+
+  if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (fn1)))
+    {
+      auto_vec<string_slice> fn1_versions = get_clone_versions (fn1);
+      // fn1 is target_clone
+      if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (fn2)))
+	{
+	  auto_vec<string_slice> fn2_versions = get_clone_versions (fn2);
+	  for (string_slice v1 : fn1_versions)
+	    {
+	      aarch64_fmv_feature_mask v1_mask;
+	      aarch64_parse_fmv_features (v1, NULL, &v1_mask, NULL);
+	      for (string_slice v2 : fn2_versions)
+		{
+		  aarch64_fmv_feature_mask v2_mask;
+		  aarch64_parse_fmv_features (v2, NULL, &v2_mask, NULL);
+		  if (v1_mask == v2_mask)
+		    return false;
+		}
+	    }
+	  return true;
+	}
+      else
+	{
+	  aarch64_fmv_feature_mask v2_mask = get_feature_mask_for_version (fn2);
+	  for (string_slice v1 : fn1_versions)
+	    {
+	      aarch64_fmv_feature_mask v1_mask;
+	      aarch64_parse_fmv_features (v1, NULL, &v1_mask, NULL);
+	      if (v1_mask == v2_mask)
+		return false;
+	    }
+	  return true;
+	}
+    }
+  // Can assume both are target_version
+  else
+    // Both are target_version
+    return (aarch64_compare_version_priority (fn1, fn2) != 0);
 }
 
 /* Implement TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P.  Use an opt-out
diff --git a/gcc/testsuite/g++.target/aarch64/mv-and-mvc1.C b/gcc/testsuite/g++.target/aarch64/mv-and-mvc1.C
new file mode 100644
index 00000000000..0e2e746f20e
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/mv-and-mvc1.C
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__((target_version("default")))
+int foo ()
+{
+  return 0;
+}
+
+__attribute__((target_clones("dotprod", "sve+sve2")))
+int foo ()
+{
+  return 1;
+}
+
+__attribute__((target_clones("sme", "sme2")))
+int foo ()
+{
+  return 2;
+}
+
+int bar()
+{
+  return foo ();
+}
+
+
+/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._Msme:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._Msme2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\t_Z3foov\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/g++.target/aarch64/mv-and-mvc2.C b/gcc/testsuite/g++.target/aarch64/mv-and-mvc2.C
new file mode 100644
index 00000000000..6929b153c47
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/mv-and-mvc2.C
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__((target_version("default")))
+int foo ();
+
+__attribute__((target_clones("dotprod", "sve+sve2")))
+int foo ()
+{
+  return 1;
+}
+
+__attribute__((target_clones("sme", "sme2")))
+int foo ()
+{
+  return 2;
+}
+
+/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._Msme:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._Msme2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 0 } } */
diff --git a/gcc/testsuite/g++.target/aarch64/mv-and-mvc3.C b/gcc/testsuite/g++.target/aarch64/mv-and-mvc3.C
new file mode 100644
index 00000000000..b25b6214f4c
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/mv-and-mvc3.C
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__((target_clones("dotprod", "sve+sve2")))
+int foo ();
+
+__attribute__((target_version("default")))
+int foo ()
+{
+  return 0;
+}
+
+__attribute__((target_clones("sme", "sme2")))
+int foo ()
+{
+  return 2;
+}
+
+int bar()
+{
+  return foo ();
+}
+
+
+/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._Mdotprod:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._MsveMsve2:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._Msme:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._Msme2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\t_Z3foov\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 1 } } */
+// { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, _Z3foov\.default\n" 1 } }
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, _Z3foov\._Mdotprod\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, _Z3foov\._MsveMsve2\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, _Z3foov\._Msme\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, _Z3foov\._Msme2\n" 1 } } */
+
diff --git a/gcc/testsuite/g++.target/aarch64/mv-and-mvc4.C b/gcc/testsuite/g++.target/aarch64/mv-and-mvc4.C
new file mode 100644
index 00000000000..c122c9fe3ab
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/mv-and-mvc4.C
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__((target_version("dotprod")))
+int foo ()
+{
+  return 0;
+}
+
+__attribute__((target_clones("default", "sve+sve2")))
+int foo ()
+{
+  return 1;
+}
+
+__attribute__((target_clones("sme", "sme2")))
+int foo ()
+{
+  return 2;
+}
+
+int bar()
+{
+  return foo ();
+}
+
+
+/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._Msme:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\._Msme2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\t_Z3foov\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/g++.target/aarch64/mv-error1.C b/gcc/testsuite/g++.target/aarch64/mv-error1.C
new file mode 100644
index 00000000000..2a659c02dfc
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/mv-error1.C
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__ ((target_version ("default"))) int
+foo ();
+
+__attribute__ ((target_version ("default"))) int
+foo () { return 1; } /* { dg-message "old declaration" } */
+
+__attribute__ ((target_version ("dotprod"))) float
+foo () { return 3; } /* { dg-error "ambiguating new declaration of" } */
diff --git a/gcc/testsuite/g++.target/aarch64/mv-error13.C b/gcc/testsuite/g++.target/aarch64/mv-error13.C
new file mode 100644
index 00000000000..0b6b38ce100
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/mv-error13.C
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ();
+
+int
+bar ()
+{
+  return foo (); /* { dg-error "no default version in scope" } */
+}
diff --git a/gcc/testsuite/g++.target/aarch64/mv-error2.C b/gcc/testsuite/g++.target/aarch64/mv-error2.C
new file mode 100644
index 00000000000..167af4ad380
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/mv-error2.C
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__ ((target_version ("dotprod"))) float
+foo () { return 3; } /* { dg-message "previously defined here" } */
+
+__attribute__ ((target_version ("dotprod"))) float
+foo () { return 3; } /* { dg-error "redefinition of" } */
diff --git a/gcc/testsuite/g++.target/aarch64/mv-error3.C b/gcc/testsuite/g++.target/aarch64/mv-error3.C
new file mode 100644
index 00000000000..cc641488263
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/mv-error3.C
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__ ((target_version ("dotprod"))) float
+foo () { return 3; }
+
+__attribute__ ((target_version ("default"))) float
+foo () { return 3; } /* { dg-message "previously defined here" } */
+
+__attribute__ ((target_version ("default"))) float
+foo () { return 3; } /* { dg-error "redefinition of" } */
diff --git a/gcc/testsuite/g++.target/aarch64/mv-error7.C b/gcc/testsuite/g++.target/aarch64/mv-error7.C
new file mode 100644
index 00000000000..3d2c73446be
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/mv-error7.C
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo();
+
+int bar () { return foo (); } /* { dg-error "no default version in scope" } */
diff --git a/gcc/testsuite/g++.target/aarch64/mv-error8.C b/gcc/testsuite/g++.target/aarch64/mv-error8.C
new file mode 100644
index 00000000000..8ea26750480
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/mv-error8.C
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo () {
+	return 1;
+}
+
+__attribute__ ((target_version ("sve"))) int
+foo () {
+	return 1;
+}
+
+int bar () { return foo (); } /* { dg-error "no matching function for call to" } */
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo2();
+
+int bar2 () { return foo2 (); } /* { dg-error "no default version in scope" } */
diff --git a/gcc/testsuite/g++.target/aarch64/mv-error9.C b/gcc/testsuite/g++.target/aarch64/mv-error9.C
new file mode 100644
index 00000000000..c1b58edca11
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/mv-error9.C
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ();
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ();
+
+int bar () { return foo (); } /* { dg-error "no matching function for call to" } */

Reply via email to