Alfie Richards <alfie.richa...@arm.com> writes:

> This changes behavior of target_clones and target_version attributes
> to be inline with what is specified in the Arm C Language Extension.
>
> Notably this changes the scope and signature of multiversioned functions
> to that of the default version, and changes the resolver to be
> created at the implementation of the default version.
>
> This is achieved by changing the C++ front end to no longer resolve any
> non-default version decls in lookup, and by moving dipatching
> for default_target sets to reuse the dispatching logic for target_clones
> in multiple_target.cc.
>
> This also fixes https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118313
> for aarch64 and riscv.
>
> This changes the behavior of both the aarch64, and riscv targets.
>
> gcc/ChangeLog:
>
>       * cgraphunit.cc (analyze_functions): Add dependency from default node
>       to non-default versions.
>       * ipa.cc (symbol_table::remove_unreachable_nodes): Ditto.
>       * multiple_target.cc (ipa_target_clone): Change logic to conditionally
>       dispatch target_clones and to dispatch some target_version sets.
>
> gcc/cp/ChangeLog:
>
>       * call.cc (add_candidates): For target_version semantics don't resolve
>       non-default versions.
>       * class.cc (resolve_address_of_overloaded_function): Ditto.
>       * cp-gimplify.cc (cp_genericize_r): For target_version semantics don't
>       redirect calls to versioned functions (done later at
>       multiple_target.cc.)
>       * decl.cc (start_decl): Mangle and mark all non-default function
>       decls.
>       (start_preparsed_function): Ditto.
>       * typeck.cc (cp_build_function_call_vec): Add error if target has no
>       default implementation.
>
> gcc/testsuite/ChangeLog:
>
>       * g++.target/aarch64/mv-1.C: Change for new semantics.
>       * g++.target/aarch64/mv-symbols2.C: Ditto.
>       * g++.target/aarch64/mv-symbols3.C: Ditto.
>       * g++.target/aarch64/mv-symbols4.C: Ditto.
>       * g++.target/aarch64/mv-symbols5.C: Ditto.
>       * g++.target/aarch64/mvc-symbols3.C: Ditto.
>       * g++.target/riscv/mv-symbols2.C: Ditto.
>       * g++.target/riscv/mv-symbols3.C: Ditto.
>       * g++.target/riscv/mv-symbols4.C: Ditto.
>       * g++.target/riscv/mv-symbols5.C: Ditto.
>       * g++.target/riscv/mvc-symbols3.C: Ditto.
>       * g++.target/aarch64/mv-symbols10.C: New test.
>       * g++.target/aarch64/mv-symbols11.C: New test.
>       * g++.target/aarch64/mv-symbols12.C: New test.
>       * g++.target/aarch64/mv-symbols14.C: New test.
>       * g++.target/aarch64/mv-symbols15.C: New test.
>       * g++.target/aarch64/mv-symbols6.C: New test.
>       * g++.target/aarch64/mv-symbols8.C: New test.
>       * g++.target/aarch64/mv-symbols9.C: New test.

Nice!  I agree that the tests test for what the ACLE asks for.
Some comments below.

> ---
>  gcc/cgraphunit.cc                             |  9 ++++
>  gcc/cp/call.cc                                |  8 ++++
>  gcc/cp/class.cc                               | 11 ++++-
>  gcc/cp/cp-gimplify.cc                         |  6 ++-
>  gcc/cp/decl.cc                                | 24 ++++++++++
>  gcc/cp/typeck.cc                              |  8 ++++
>  gcc/ipa.cc                                    | 11 +++++
>  gcc/multiple_target.cc                        | 13 ++++-
>  gcc/testsuite/g++.target/aarch64/mv-1.C       |  4 ++
>  .../g++.target/aarch64/mv-symbols10.C         | 43 +++++++++++++++++
>  .../g++.target/aarch64/mv-symbols11.C         | 27 +++++++++++
>  .../g++.target/aarch64/mv-symbols12.C         | 18 +++++++
>  .../g++.target/aarch64/mv-symbols14.C         | 16 +++++++
>  .../g++.target/aarch64/mv-symbols15.C         | 16 +++++++
>  .../g++.target/aarch64/mv-symbols2.C          | 12 ++---
>  .../g++.target/aarch64/mv-symbols3.C          |  6 +--
>  .../g++.target/aarch64/mv-symbols4.C          |  6 +--
>  .../g++.target/aarch64/mv-symbols5.C          |  6 +--
>  .../g++.target/aarch64/mv-symbols6.C          | 23 +++++++++
>  .../g++.target/aarch64/mv-symbols8.C          | 48 +++++++++++++++++++
>  .../g++.target/aarch64/mv-symbols9.C          | 46 ++++++++++++++++++
>  .../g++.target/aarch64/mvc-symbols3.C         | 12 ++---
>  gcc/testsuite/g++.target/riscv/mv-symbols2.C  | 12 ++---
>  gcc/testsuite/g++.target/riscv/mv-symbols3.C  |  6 +--
>  gcc/testsuite/g++.target/riscv/mv-symbols4.C  |  6 +--
>  gcc/testsuite/g++.target/riscv/mv-symbols5.C  |  6 +--
>  gcc/testsuite/g++.target/riscv/mvc-symbols3.C | 12 ++---
>  27 files changed, 368 insertions(+), 47 deletions(-)
>  create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols10.C
>  create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols11.C
>  create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols12.C
>  create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols14.C
>  create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols15.C
>  create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols6.C
>  create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols8.C
>  create mode 100644 gcc/testsuite/g++.target/aarch64/mv-symbols9.C
>
> diff --git a/gcc/cgraphunit.cc b/gcc/cgraphunit.cc
> index 82f205488e9..f7f8957e618 100644
> --- a/gcc/cgraphunit.cc
> +++ b/gcc/cgraphunit.cc
> @@ -1264,6 +1264,15 @@ analyze_functions (bool first_time)
>             if (!cnode->analyzed)
>               cnode->analyze ();
>  
> +           /* A reference to a default node in a funciton set implies a

function

> +              refrence to all versions in the set.  */

reference

> +           if (cnode->function_version ()
> +               && is_function_default_version (node->decl))
> +             for (cgraph_function_version_info *fvi
> +                  = cnode->function_version ()->next;
> +                  fvi; fvi = fvi->next)
> +               enqueue_node (fvi->this_node);
> +

Here too it seems worth caching cnode->function_version ().

>             for (edge = cnode->callees; edge; edge = edge->next_callee)
>               if (edge->callee->definition
>                   && (!DECL_EXTERNAL (edge->callee->decl)
> [...]
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index 9e1a2e0a9bd..552b1845352 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -6174,6 +6174,12 @@ start_decl (const cp_declarator *declarator,
>  
>    was_public = TREE_PUBLIC (decl);
>  
> +  /* Mark any non-default function as versioned as it needs to be mangled
> +     even when on its own in a TU.  */
> +  if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE && TREE_CODE (decl) == FUNCTION_DECL
> +      && !is_function_default_version (decl))
> +    maybe_mark_function_versioned (decl);
> +

Sorry for the formatting nit, but: usually the convention is to have one
subcondition per line if the whole condition spans multiple lines.

>    /* Set the assembler string for any versioned function.  */
>    if (TREE_CODE (decl) == FUNCTION_DECL
>        && (lookup_attribute (TARGET_HAS_FMV_TARGET_ATTRIBUTE ? "target"
> @@ -6186,6 +6192,12 @@ start_decl (const cp_declarator *declarator,
>       node->insert_new_function_version ();
>        if (!node->function_version ()->assembler_name)
>       node->function_version ()->assembler_name = DECL_ASSEMBLER_NAME (decl);
> +
> +      /* In target_version semantics mangle non-default versions even if no
> +      other versions are present.  */
> +      if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
> +       && !is_function_default_version (decl))
> +     maybe_mark_function_versioned (decl);
>      }
>  
>    if ((DECL_EXTERNAL (decl) || TREE_CODE (decl) == FUNCTION_DECL)

This is probably just because I'm being lazy and not applying the
patches locally to try them out, but: could you explain why you need
to do this twice in the same function?  Same for start_preparsed_function.

> [...]
> diff --git a/gcc/ipa.cc b/gcc/ipa.cc
> index 8a7b067a526..39ab0cc3de5 100644
> --- a/gcc/ipa.cc
> +++ b/gcc/ipa.cc
> @@ -433,6 +433,17 @@ symbol_table::remove_unreachable_nodes (FILE *file)
>                                                      e, &first, &reachable);
>                   }
>               }
> +
> +           /* A refrence to the default node implies use of all the other

reference

> +              versions (they get used in the function resolver made later
> +              in multiple_target.cc)  */
> +           if (cnode->function_version ()
> +               && is_function_default_version (node->decl))
> +             for (cgraph_function_version_info *fvi
> +                  = cnode->function_version ()->next;
> +                  fvi; fvi = fvi->next)
> +               enqueue_node (fvi->this_node, &first, &reachable);
> +

I was going to complain about the cut-&-paste with cgraphunit.cc,
but I see this is actually a different enqueue_node...

>             for (e = cnode->callees; e; e = e->next_callee)
>               {
>                 symtab_node *body = e->callee->function_symbol ();
> [...]
> diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols10.C 
> b/gcc/testsuite/g++.target/aarch64/mv-symbols10.C
> new file mode 100644
> index 00000000000..dc5a8ff6572
> --- /dev/null
> +++ b/gcc/testsuite/g++.target/aarch64/mv-symbols10.C
> @@ -0,0 +1,43 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O0" } */
> +/* { dg-additional-options "-Wno-experimental-fmv-target" } */
> +
> +int
> +foo ();
> +
> +int
> +foo ()
> +{
> +  return 1;
> +}
> +
> +__attribute__ ((target_version ("dotprod"))) int
> +foo ()
> +{
> +  return 3;
> +}
> +__attribute__ ((target_version ("sve+sve2"))) int
> +foo ()
> +{
> +  return 5;
> +}
> +
> +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\.resolver:\n" 1 } } */
> +/* { dg-final { scan-assembler-times "\n\tadrp\tx., _Z3foov\._MsveMsve2\n" 1 
> } } */

It'd be safer to allow x[0-9]+, since the instruction could use x10 or above.
Same for other tests.

> +/* { dg-final { scan-assembler-times "\n\tadrp\tx., _Z3foov\._Mdotprod\n" 1 
> } } */
> +/* { dg-final { scan-assembler-times "\n\tadrp\tx., _Z3foov\.default\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-symbols12.C 
> b/gcc/testsuite/g++.target/aarch64/mv-symbols12.C
> new file mode 100644
> index 00000000000..05d915f19ab
> --- /dev/null
> +++ b/gcc/testsuite/g++.target/aarch64/mv-symbols12.C
> @@ -0,0 +1,18 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O0" } */
> +/* { dg-additional-options "-Wno-experimental-fmv-target" } */
> +
> +__attribute__ ((target_version ("default"))) int
> +foo () { return 1; }
> +
> +__attribute__ ((target_version ("dotprod"))) int
> +foo () { return 3; }
> +
> +int (*test)();
> +
> +int bar ()
> +{
> +  test = foo;
> +
> +  return test();
> +}

What's this testing?  The code seems to compile ok for unpatched compilers
and the test doesn't seem to check anything beyond that.  Same question
for mv-symbols14 and mv-symbols15.

(Very minor, but: it'd be nice to renumber the tests to avoid the gap at 13,
unless it's a superstition thing.)

I don't know how much this will add in practice, but how about having a
test that contains a default version definition, a non-default version
definition, and a non-default version declaration (for a different feature)?
It looks like all the current tests have all non-default versions defined
locally or no non-default versions defined locally.

Thanks,
Richard

> diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols14.C 
> b/gcc/testsuite/g++.target/aarch64/mv-symbols14.C
> new file mode 100644
> index 00000000000..fbee7800ba2
> --- /dev/null
> +++ b/gcc/testsuite/g++.target/aarch64/mv-symbols14.C
> @@ -0,0 +1,16 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O0" } */
> +/* { dg-additional-options "-Wno-experimental-fmv-target" } */
> +
> +int foo () {
> +  return 1;
> +}
> +
> +void
> +bar ()
> +{
> +  foo ();
> +}
> +
> +__attribute__ ((target_version ("dotprod"))) int
> +foo ();
> diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols15.C 
> b/gcc/testsuite/g++.target/aarch64/mv-symbols15.C
> new file mode 100644
> index 00000000000..a10c0bfb990
> --- /dev/null
> +++ b/gcc/testsuite/g++.target/aarch64/mv-symbols15.C
> @@ -0,0 +1,16 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O0" } */
> +/* { dg-additional-options "-Wno-experimental-fmv-target" } */
> +
> +int foo () {
> +  return 1;
> +}
> +
> +void bar ()
> +{
> +  int (*test)() = foo;
> +
> +  test();
> +}
> +
> +__attribute__ ((target_version ("dotprod"))) int foo ();

Reply via email to