Alfie Richards <alfie.richa...@arm.com> writes:
> Add support for a FMV set defined by a combination of target_clones and
> target_version definitions.
>
> Additionally, change is_function_default_version to consider a function
> declaration annotated with target_clones containing default to be a
> default version.
>
> Lastly, add support for the case that a target_clone has all versions filtered
> out and therefore the declaration should be removed. This is relevant as now
> the default could be defined in a target_version, so a target_clones no longer
> necessarily contains the default.
>
> 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.

OK, thanks.

Richard

> ---
>  gcc/attribs.cc                                | 10 ++++-
>  gcc/c-family/c-attribs.cc                     |  9 +---
>  gcc/cgraph.h                                  |  7 ++++
>  gcc/multiple_target.cc                        | 24 +++++++++--
>  .../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, 183 insertions(+), 13 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 06785eaa136..2ca82674f7c 100644
> --- a/gcc/attribs.cc
> +++ b/gcc/attribs.cc
> @@ -1242,7 +1242,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)
> @@ -1259,6 +1260,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 92d046d92ce..b2875e76031 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 d686238368d..1c91a7ba7ca 100644
> --- a/gcc/cgraph.h
> +++ b/gcc/cgraph.h
> @@ -3119,6 +3119,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 bb430bc4fbf..7784478d8e2 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");
> @@ -516,7 +532,7 @@ ipa_target_clone (bool early)
>       The late stage is only used for the expansion and dispatching of the 
> simple
>       case where the FMV set is defined by a single target_clone attribute.  
> */
>  
> -  FOR_EACH_FUNCTION (node)
> +  FOR_EACH_FUNCTION_REMOVABLE (node)
>      {
>        /* In the early stage, we need to expand any target clone that is not
>        the simple case.  */
> 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..24b81f5f5f3
> --- /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("sve2+sme", "sve2+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\._Msve2Msme:\n" 1 } } */
> +/* { dg-final { scan-assembler-times "\n_Z3foov\._Msve2Msme2:\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..5939353d2d2
> --- /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("sve2+sme", "sve2+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\._Msve2Msme:\n" 1 } } */
> +/* { dg-final { scan-assembler-times "\n_Z3foov\._Msve2Msme2:\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..fb1c596e919
> --- /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("sve2+sme", "sve2+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\._Msve2Msme:\n" 1 } } */
> +/* { dg-final { scan-assembler-times "\n_Z3foov\._Msve2Msme2:\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\._Msve2Msme\n" 1 } } */
> +/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, 
> _Z3foov\._Msve2Msme2\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..e198fecc4cc
> --- /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("sve2+sme", "sve2+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\._Msve2Msme:\n" 1 } } */
> +/* { dg-final { scan-assembler-times "\n_Z3foov\._Msve2Msme2:\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 } } */

Reply via email to