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.
This can be enabled for riscv by modifying
riscv_common_function_versions. Currently target_clone and target_version
declarations will always be considered duplicate preventing mixing
currently.
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
support for target_version and target_clone mixing.
* multiple_target.cc (expand_target_clones): Add support for the
default version not being part of a target_clones attribute.
gcc/c-family/ChangeLog:
* c-attribs.cc: Add support for target_version and target_clone mixing.
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 | 10 ++++-
gcc/c-family/c-attribs.cc | 9 +---
gcc/config/aarch64/aarch64.cc | 45 ++++++++++++++++++-
gcc/multiple_target.cc | 6 ++-
.../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 ++++++++++++++++
8 files changed, 204 insertions(+), 12 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
diff --git a/gcc/attribs.cc b/gcc/attribs.cc
index d0f37d77098..2c402fb994d 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -1317,7 +1317,8 @@ make_dispatcher_decl (const tree decl)
With the target attribute semantics, returns true if the function is marked
as default with the target version.
With the target_version attribute semantics, returns true if the function
- is either not annotated, or annotated as default. */
+ is either not annotated, annotated as default, or is a target_clone
+ containing the default declaration. */
bool
is_function_default_version (const tree decl)
@@ -1334,6 +1335,13 @@ is_function_default_version (const tree decl)
}
else
{
+ if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (decl)))
+ {
+ int num_defaults = 0;
+ get_clone_versions (decl, &num_defaults);
+ return num_defaults > 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..f793418d3f4 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -249,13 +249,6 @@ 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),
};
@@ -543,7 +536,7 @@ const struct attribute_spec c_common_gnu_attributes[] =
attr_target_exclusions },
{ "target_version", 1, 1, true, false, false, false,
handle_target_version_attribute,
- attr_target_version_exclusions },
+ NULL },
{ "target_clones", 1, -1, true, false, false, false,
handle_target_clones_attribute,
attr_target_clones_exclusions },
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 6b2247be7e7..cbba250da59 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -20682,7 +20682,50 @@ aarch64_common_function_versions (tree fn1, tree fn2)
|| TREE_CODE (fn2) != FUNCTION_DECL)
return false;
- return (aarch64_compare_version_priority (fn1, fn2) != 0);
+ /* As this is symmetric, can remove the case where fn2 is target clone and
+ fn1 is target version by swapping here. */
+ if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (fn2)))
+ std::swap (fn1, fn2);
+
+ 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)))
+ {
+ /* Both are target_clone. */
+ 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
+ {
+ /* Only fn1 is target clone. */
+ 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;
+ }
+ }
+ 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/multiple_target.cc b/gcc/multiple_target.cc
index 839c6a4906a..8c9caa4cd96 100644
--- a/gcc/multiple_target.cc
+++ b/gcc/multiple_target.cc
@@ -299,8 +299,10 @@ expand_target_clones (struct cgraph_node *node, bool definition)
"multiple %<default%> targets were set");
return false;
}
- /* Disallow target clones with no defaults. */
- if (num_def == 0)
+
+ /* For target FMV semantics, where target and target_clone mixing
+ is not supported, disallow target clones with no defaults. */
+ if (TARGET_HAS_FMV_TARGET_ATTRIBUTE && num_def == 0)
{
error_at (DECL_SOURCE_LOCATION (node->decl),
"%<default%> target was not set");
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 } } */