https://gcc.gnu.org/g:fd372533f3130f3dd554d5afb05fed9bbe61c8f1
commit fd372533f3130f3dd554d5afb05fed9bbe61c8f1 Author: Alfie Richards <alfie.richa...@arm.com> Date: Mon Mar 24 16:02:07 2025 +0000 Support mixing of target_clones and target_version. 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. This takes advantage of refactoring done in previous patches changing how target_clones are expanded and how conflicting decls are handled. gcc/ChangeLog: * attribs.cc (is_function_default_version): Update to handle target_clones. * cgraph.h (FOR_EACH_FUNCTION_REMOVABLE): New macro. * multiple_target.cc (expand_target_clones): Update logic to delete empty target_clones and modify diagnostic. (ipa_target_clone): Update to use FOR_EACH_FUNCTION_REMOVABLE. 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. Diff: --- gcc/attribs.cc | 10 ++++++- gcc/c-family/c-attribs.cc | 9 +----- gcc/cgraph.h | 7 +++++ gcc/multiple_target.cc | 24 ++++++++++++--- gcc/testsuite/g++.target/aarch64/mv-and-mvc1.C | 38 ++++++++++++++++++++++++ gcc/testsuite/g++.target/aarch64/mv-and-mvc2.C | 29 ++++++++++++++++++ gcc/testsuite/g++.target/aarch64/mv-and-mvc3.C | 41 ++++++++++++++++++++++++++ gcc/testsuite/g++.target/aarch64/mv-and-mvc4.C | 38 ++++++++++++++++++++++++ 8 files changed, 183 insertions(+), 13 deletions(-) diff --git a/gcc/attribs.cc b/gcc/attribs.cc index 04a9e743dbe0..57dd01531a3b 100644 --- a/gcc/attribs.cc +++ b/gcc/attribs.cc @@ -1247,7 +1247,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) @@ -1264,6 +1265,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 b5287f0da06d..a4e657d9ffd7 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/cgraph.h b/gcc/cgraph.h index 8dcc9315a51a..5a8ccb8042b0 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -3090,6 +3090,13 @@ symbol_table::next_function_with_gimple_body (cgraph_node *node) for ((node) = symtab->first_function (); (node); \ (node) = symtab->next_function ((node))) +/* Walk all functions but precompute so a node can be deleted if needed. */ +#define FOR_EACH_FUNCTION_REMOVABLE(node) \ + cgraph_node *next; \ + for ((node) = symtab->first_function (), \ + next = (node) ? symtab->next_function ((node)) : NULL; (node); \ + (node) = next, next = (node) ? symtab->next_function ((node)) : NULL) + /* Return true when callgraph node is a function with Gimple body defined in current unit. Functions can also be define externally or they can be thunks with no Gimple representation. diff --git a/gcc/multiple_target.cc b/gcc/multiple_target.cc index e6de66a60d75..30b06cbc0f59 100644 --- a/gcc/multiple_target.cc +++ b/gcc/multiple_target.cc @@ -269,14 +269,28 @@ expand_target_clones (struct cgraph_node *node, bool definition) auto_vec<string_slice> attr_list = get_clone_versions (node->decl, &num_defaults); + /* If the target clones list is empty after filtering, remove this node. */ + if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE && attr_list.is_empty ()) + { + node->remove (); + return false; + } + /* No need to clone for 1 target attribute. */ - if (attr_list.length () == 1) + if (attr_list.length () == 1 && TARGET_HAS_FMV_TARGET_ATTRIBUTE) { warning_at (DECL_SOURCE_LOCATION (node->decl), 0, "single %<target_clones%> attribute is ignored"); return false; } + /* For target_version semantics, a target clone with just a default version + is the same as an unannotated decl, so can ignore. */ + if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE + && attr_list.length () == 1 + && num_defaults == 1) + return false; + if (node->definition && (node->alias || !tree_versionable_function_p (node->decl))) { @@ -304,8 +318,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_defaults == 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_defaults == 0) { error_at (DECL_SOURCE_LOCATION (node->decl), "%<default%> target was not set"); @@ -473,7 +489,7 @@ ipa_target_clone (void) struct cgraph_node *node; auto_vec<cgraph_node *> to_dispatch; - FOR_EACH_FUNCTION (node) + FOR_EACH_FUNCTION_REMOVABLE (node) /* Expand all target versions. */ if (expand_target_clones (node, node->definition) && TARGET_HAS_FMV_TARGET_ATTRIBUTE) 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 000000000000..0e2e746f20ee --- /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 000000000000..6929b153c474 --- /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 000000000000..b25b6214f4ca --- /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 000000000000..c122c9fe3ab4 --- /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 } } */