[v1,14/16] Change target_version semantics to follow ACLE specification.

Message ID 20250203130421.2192732-16-alfie.richards@arm.com
State New
Headers
Series FMV refactor and ACLE compliance. |

Checks

Context Check Description
rivoscibot/toolchain-ci-rivos-lint success Lint passed
rivoscibot/toolchain-ci-rivos-apply-patch success Patch applied
rivoscibot/toolchain-ci-rivos-build--linux-rv64gcv-lp64d-multilib fail Build failed
rivoscibot/toolchain-ci-rivos-build--linux-rv64gc_zba_zbb_zbc_zbs-lp64d-multilib fail Build failed
rivoscibot/toolchain-ci-rivos-build--newlib-rv64gcv-lp64d-multilib fail Build failed
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Test passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 fail Build failed

Commit Message

Alfie Richards Feb. 3, 2025, 1:04 p.m. UTC
  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.
---
 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
  

Comments

Richard Sandiford Feb. 4, 2025, 11:03 a.m. UTC | #1
Alfie Richards <alfie.richards@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 ();
  

Patch

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
+		 refrence to all versions in the set.  */
+	      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);
+
 	      for (edge = cnode->callees; edge; edge = edge->next_callee)
 		if (edge->callee->definition
 		    && (!DECL_EXTERNAL (edge->callee->decl)
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 3a56a82632d..ae39c9b09a4 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -6785,6 +6785,14 @@  add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args,
 	    continue;
 	}
 
+      /* Do not resolve any non-default function.  Only the default version
+	 is resolvable (for the target_version attribute semantics.)  */
+      if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE && !is_function_default_version (fn))
+	{
+	  add_ignored_candidate (candidates, fn);
+	  continue;
+	}
+
       if (TREE_CODE (fn) == TEMPLATE_DECL)
 	add_template_candidate (candidates,
 				fn,
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index a9a80d1b4be..a926dc00945 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -8789,6 +8789,11 @@  resolve_address_of_overloaded_function (tree target_type,
 	if (!constraints_satisfied_p (fn))
 	  continue;
 
+	/* Never resolve a non-default version.  */
+	if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
+	    && !is_function_default_version (fn))
+	  continue;
+
 	if (undeduced_auto_decl (fn))
 	  {
 	    /* Force instantiation to do return type deduction.  */
@@ -9014,8 +9019,10 @@  resolve_address_of_overloaded_function (tree target_type,
   /* If a pointer to a function that is multi-versioned is requested, the
      pointer to the dispatcher function is returned instead.  This works
      well because indirectly calling the function will dispatch the right
-     function version at run-time.  */
-  if (DECL_FUNCTION_VERSIONED (fn))
+     function version at run-time.
+     This is done at multiple_target.cc for target_version semantics.  */
+
+  if (DECL_FUNCTION_VERSIONED (fn) && TARGET_HAS_FMV_TARGET_ATTRIBUTE)
     {
       fn = get_function_version_dispatcher (fn);
       if (fn == NULL)
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 4ec3de13008..3ef5bec5d54 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -2130,10 +2130,12 @@  cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
 	 returns the function with the highest target priority, that is,
 	 the version that will checked for dispatching first.  If this
 	 version is inlinable, a direct call to this version can be made
-	 otherwise the call should go through the dispatcher.  */
+	 otherwise the call should go through the dispatcher.
+	 This is done at multiple_target.cc for target_version semantics.  */
       {
 	tree fn = cp_get_callee_fndecl_nofold (stmt);
-	if (fn && DECL_FUNCTION_VERSIONED (fn)
+	if (fn && TARGET_HAS_FMV_TARGET_ATTRIBUTE
+	    && DECL_FUNCTION_VERSIONED (fn)
 	    && (current_function_decl == NULL
 		|| !targetm.target_option.can_inline_p (current_function_decl,
 							fn)))
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);
+
   /* 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)
@@ -18776,6 +18788,12 @@  start_preparsed_function (tree decl1, tree attrs, int flags)
   if (!DECL_OMP_DECLARE_REDUCTION_P (decl1))
     start_lambda_scope (decl1);
 
+  /* 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 (decl1) == FUNCTION_DECL
+      && !is_function_default_version (decl1))
+    maybe_mark_function_versioned (decl1);
+
   /* Set the assembler string for any versioned function.  */
   if (TREE_CODE (decl1) == FUNCTION_DECL
       && (lookup_attribute (TARGET_HAS_FMV_TARGET_ATTRIBUTE ? "target"
@@ -18788,6 +18806,12 @@  start_preparsed_function (tree decl1, tree attrs, int flags)
 	node->insert_new_function_version ();
       if (!node->function_version ()->assembler_name)
 	node->function_version ()->assembler_name = DECL_ASSEMBLER_NAME (decl1);
+
+      /* 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 (decl1))
+	maybe_mark_function_versioned (decl1);
     }
 
   return true;
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index a9c32ff930d..ccb17df1feb 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -4439,6 +4439,14 @@  cp_build_function_call_vec (tree function, vec<tree, va_gc> **params,
 	return error_mark_node;
       fndecl = function;
 
+      if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
+	   && !is_function_default_version (fndecl))
+	 {
+	   if (complain & tf_error)
+	    error ("no default version in scope");
+	   return error_mark_node;
+	 }
+
       /* Convert anything with function type to a pointer-to-function.  */
       if (DECL_MAIN_P (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
+		 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);
+
 	      for (e = cnode->callees; e; e = e->next_callee)
 		{
 	          symtab_node *body = e->callee->function_symbol ();
diff --git a/gcc/multiple_target.cc b/gcc/multiple_target.cc
index 4f748a81f9b..07a894087f8 100644
--- a/gcc/multiple_target.cc
+++ b/gcc/multiple_target.cc
@@ -424,9 +424,20 @@  ipa_target_clone (void)
   auto_vec<cgraph_node *> to_dispatch;
 
   FOR_EACH_FUNCTION (node)
-    if (expand_target_clones (node, node->definition))
+    /* Expand all target versions.  */
+    if (expand_target_clones (node, node->definition)
+	&& TARGET_HAS_FMV_TARGET_ATTRIBUTE)
+      /* In non target_version semantics, dispatch all target clone sets.  */
       to_dispatch.safe_push (node);
 
+  /* In target_version semantics dispatch all FMV function sets with a default
+     implementation.  */
+  if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE)
+    FOR_EACH_FUNCTION (node)
+      if (is_function_default_version (node->decl)
+	  && DECL_FUNCTION_VERSIONED (node->decl) && TREE_STATIC (node->decl))
+	to_dispatch.safe_push (node);
+
   for (unsigned i = 0; i < to_dispatch.length (); i++)
     create_dispatcher_calls (to_dispatch[i]);
 
diff --git a/gcc/testsuite/g++.target/aarch64/mv-1.C b/gcc/testsuite/g++.target/aarch64/mv-1.C
index b10037f1b9b..93b8a136587 100644
--- a/gcc/testsuite/g++.target/aarch64/mv-1.C
+++ b/gcc/testsuite/g++.target/aarch64/mv-1.C
@@ -37,3 +37,7 @@  int bar()
 /* { dg-final { scan-assembler-times "\n_Z3foov\._Mrng:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\._MrngMflagm:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\._Mflagm:\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-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 } } */
+/* { 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-symbols11.C b/gcc/testsuite/g++.target/aarch64/mv-symbols11.C
new file mode 100644
index 00000000000..92d4ab617d8
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/mv-symbols11.C
@@ -0,0 +1,27 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__ ((target_version ("default"))) void
+foo (int a = 3);
+
+__attribute__ ((target_version ("sve"))) void
+foo (int a = 4);
+
+void bar() {
+  foo();
+}
+
+__attribute__ ((target_version ("sve"))) void
+foo2 (int a = 6);
+
+__attribute__ ((target_version ("default"))) void
+foo2 (int a = 5);
+
+void bar2() {
+  foo2();
+}
+
+
+/* { dg-final { scan-assembler-times "\n\tmov\tw\[0-9\]\+, 3\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tmov\tw\[0-9\]\+, 5\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();
+}
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 ();
diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols2.C b/gcc/testsuite/g++.target/aarch64/mv-symbols2.C
index 6da88ddfb48..55f2d48f5e4 100644
--- a/gcc/testsuite/g++.target/aarch64/mv-symbols2.C
+++ b/gcc/testsuite/g++.target/aarch64/mv-symbols2.C
@@ -41,13 +41,13 @@  int foo (int)
 /* { 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" 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 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\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_Z3fooi\.default:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\._Mdotprod:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\._MsveMsve2:\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, %gnu_indirect_function\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols3.C b/gcc/testsuite/g++.target/aarch64/mv-symbols3.C
index 5dd7b49be2a..6ba02a2aae9 100644
--- a/gcc/testsuite/g++.target/aarch64/mv-symbols3.C
+++ b/gcc/testsuite/g++.target/aarch64/mv-symbols3.C
@@ -29,10 +29,10 @@  int bar()
 /* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 0 } } */
 /* { 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\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
 /* { 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\t\.type\t_Z3foov, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 0 } } */
 
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\._Mdotprod:\n" 0 } } */
diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols4.C b/gcc/testsuite/g++.target/aarch64/mv-symbols4.C
index 4b25d17cc15..cc013c47848 100644
--- a/gcc/testsuite/g++.target/aarch64/mv-symbols4.C
+++ b/gcc/testsuite/g++.target/aarch64/mv-symbols4.C
@@ -44,6 +44,6 @@  int bar()
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\._Mdotprod:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\._MsveMsve2:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, %gnu_indirect_function\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, %gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols5.C b/gcc/testsuite/g++.target/aarch64/mv-symbols5.C
index fac00b20313..1396ca379e4 100644
--- a/gcc/testsuite/g++.target/aarch64/mv-symbols5.C
+++ b/gcc/testsuite/g++.target/aarch64/mv-symbols5.C
@@ -44,10 +44,10 @@  int bar()
 /* { dg-final { scan-assembler-times "\n_Z3foov\.default:\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\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
 /* { 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\t\.type\t_Z3foov, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 0 } } */
 
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\._Mdotprod:\n" 1 } } */
diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols6.C b/gcc/testsuite/g++.target/aarch64/mv-symbols6.C
new file mode 100644
index 00000000000..bdfdcdbdaed
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/mv-symbols6.C
@@ -0,0 +1,23 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__ ((target_version ("default"))) int
+foo ()
+{
+  return 1;
+}
+
+int bar()
+{
+  return foo();
+}
+
+/* It is not overly clear what the correct behaviour is in this case.
+   This test serves more as a test of consistency for this case rather
+   than a test of correctness.  */
+
+/* { dg-final { scan-assembler-times "\n_Z3foov:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "bl\t_Z3foov\n" 1 } } */
diff --git a/gcc/testsuite/g++.target/aarch64/mv-symbols8.C b/gcc/testsuite/g++.target/aarch64/mv-symbols8.C
new file mode 100644
index 00000000000..6c526227b31
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/mv-symbols8.C
@@ -0,0 +1,48 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+/* { dg-additional-options "-Wno-experimental-fmv-target" } */
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ();
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ();
+
+__attribute__ ((target_version ("default"))) int
+foo ();
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ()
+{
+  return 5;
+}
+__attribute__ ((target_version ("dotprod"))) int
+foo ()
+{
+  return 3;
+}
+__attribute__ ((target_version ("default"))) int
+foo ()
+{
+  return 1;
+}
+
+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 } } */
+/* { 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-symbols9.C b/gcc/testsuite/g++.target/aarch64/mv-symbols9.C
new file mode 100644
index 00000000000..937a223fcb2
--- /dev/null
+++ b/gcc/testsuite/g++.target/aarch64/mv-symbols9.C
@@ -0,0 +1,46 @@ 
+/* { 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
+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 } } */
+/* { 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/mvc-symbols3.C b/gcc/testsuite/g++.target/aarch64/mvc-symbols3.C
index 350a5586643..2a315d2db5c 100644
--- a/gcc/testsuite/g++.target/aarch64/mvc-symbols3.C
+++ b/gcc/testsuite/g++.target/aarch64/mvc-symbols3.C
@@ -22,15 +22,15 @@  int bar(int x)
 /* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 0 } } */
 /* { 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\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
 /* { 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\t\.type\t_Z3foov, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 0 } } */
 
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\._Mdotprod:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\._MsveMsve2:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n\tbl\t_Z3fooi\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, %gnu_indirect_function\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, %gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 0 } } */
diff --git a/gcc/testsuite/g++.target/riscv/mv-symbols2.C b/gcc/testsuite/g++.target/riscv/mv-symbols2.C
index 43fa1502b7d..8a5c5a0bc9f 100644
--- a/gcc/testsuite/g++.target/riscv/mv-symbols2.C
+++ b/gcc/testsuite/g++.target/riscv/mv-symbols2.C
@@ -47,15 +47,15 @@  int foo (int)
 /* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\.arch__v:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\.arch__zba__zbb:\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n\tcall\t_Z3foov\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 } } */
+/* { 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_Z3fooi\.default:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.arch__v:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.arch__zba__zbb:\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n\tcall\t_Z3fooi\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, @gnu_indirect_function\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, @gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/g++.target/riscv/mv-symbols3.C b/gcc/testsuite/g++.target/riscv/mv-symbols3.C
index 4dc81cf7395..fa2d3371e87 100644
--- a/gcc/testsuite/g++.target/riscv/mv-symbols3.C
+++ b/gcc/testsuite/g++.target/riscv/mv-symbols3.C
@@ -36,10 +36,10 @@  int bar()
 /* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\.arch__v:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\.arch__zba__zbb:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n\tcall\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\t\.type\t_Z3foov, @gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 0 } } */
 
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.arch__v:\n" 0 } } */
diff --git a/gcc/testsuite/g++.target/riscv/mv-symbols4.C b/gcc/testsuite/g++.target/riscv/mv-symbols4.C
index b0ed16a5eda..fa84e25900c 100644
--- a/gcc/testsuite/g++.target/riscv/mv-symbols4.C
+++ b/gcc/testsuite/g++.target/riscv/mv-symbols4.C
@@ -50,7 +50,7 @@  int bar()
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.arch__v:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.arch__zba__zbb:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n\tcall\t_Z3fooi\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, @gnu_indirect_function\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, @gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/g++.target/riscv/mv-symbols5.C b/gcc/testsuite/g++.target/riscv/mv-symbols5.C
index f4c6b294e0f..5551f5da554 100644
--- a/gcc/testsuite/g++.target/riscv/mv-symbols5.C
+++ b/gcc/testsuite/g++.target/riscv/mv-symbols5.C
@@ -48,10 +48,10 @@  int bar()
 /* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\.arch__v:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\.arch__zba__zbb:\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n\tcall\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\t\.type\t_Z3foov, @gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 0 } } */
 
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.arch__v:\n" 1 } } */
diff --git a/gcc/testsuite/g++.target/riscv/mvc-symbols3.C b/gcc/testsuite/g++.target/riscv/mvc-symbols3.C
index b36c3fa7a95..256fec7d368 100644
--- a/gcc/testsuite/g++.target/riscv/mvc-symbols3.C
+++ b/gcc/testsuite/g++.target/riscv/mvc-symbols3.C
@@ -28,15 +28,15 @@  int bar(int x)
 /* { dg-final { scan-assembler-times "\n_Z3foov\.default:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\.arch__v:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\.arch__zba__zbb:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n\tcall\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\t\.type\t_Z3foov, @gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov,_Z3foov\.resolver\n" 0 } } */
 
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.default:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.arch__v:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.arch__zba__zbb:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n\tcall\t_Z3fooi\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, @gnu_indirect_function\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi, @gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi,_Z3fooi\.resolver\n" 0 } } */