This commit introduces support for the target_version attribute in the c
frontend, following the behavior defined in the Arm C Language Extension.

Key changes include:

- During pushdecl, the compiler now checks whether the current symbol is
  part of a multiversioned set.
  - New versions are added to the function multiversioning (FMV) set, and the
    symbol binding is updated to include the default version (if present).
    This means the binding for a multiversioned symbol will always reference
    the default version (if present), as it defines the scope and signature
    for the entire set.
  - Pre-existing versions are merged with their previous version (or diagnosed).
- Lookup logic is adjusted to prevent resolving non-default versions.
- start_decl and start_function are updated to handle marking and mangling of
  versioned functions.
- c_parse_final_cleanups now includes a call to process_same_body_aliases.
  This has no functional impact other than setting cpp_implicit_aliases_done
  on all nodes, which is necessary for certain shared FMV logic.

gcc/c/ChangeLog:

        * c-decl.cc (maybe_mark_function_versioned): New function.
        (merge_decls): Preserve DECL_FUNCTION_VERSIONED in merging.
        (duplicate_decls): Add check and diagnostic for unmergable version 
decls.
        (pushdecl): Add FMV target_version logic.
        (lookup_name): Don't resolve non-default versions.
        (start_decl): Mark and mangle versioned functions.
        (start_function): Mark and mangle versioned functions.
        (c_parse_final_cleanups): Add call to process_same_body_aliases.

gcc/testsuite/ChangeLog:

        * gcc.target/aarch64/mv-1.c: New test.
        * gcc.target/aarch64/mv-and-mvc1.c: New test.
        * gcc.target/aarch64/mv-and-mvc2.c: New test.
        * gcc.target/aarch64/mv-and-mvc3.c: New test.
        * gcc.target/aarch64/mv-and-mvc4.c: New test.
        * gcc.target/aarch64/mv-symbols1.c: New test.
        * gcc.target/aarch64/mv-symbols10.c: New test.
        * gcc.target/aarch64/mv-symbols11.c: New test.
        * gcc.target/aarch64/mv-symbols12.c: New test.
        * gcc.target/aarch64/mv-symbols13.c: New test.
        * gcc.target/aarch64/mv-symbols2.c: New test.
        * gcc.target/aarch64/mv-symbols3.c: New test.
        * gcc.target/aarch64/mv-symbols4.c: New test.
        * gcc.target/aarch64/mv-symbols5.c: New test.
        * gcc.target/aarch64/mv-symbols6.c: New test.
        * gcc.target/aarch64/mv-symbols7.c: New test.
        * gcc.target/aarch64/mv-symbols8.c: New test.
        * gcc.target/aarch64/mv-symbols9.c: New test.
        * gcc.target/aarch64/mvc-symbols1.c: New test.
        * gcc.target/aarch64/mvc-symbols2.c: New test.
        * gcc.target/aarch64/mvc-symbols3.c: New test.
        * gcc.target/aarch64/mvc-symbols4.c: New test.
---
 gcc/c/c-decl.cc                               | 113 ++++++++++++++++++
 gcc/testsuite/gcc.target/aarch64/mv-1.c       |  43 +++++++
 .../gcc.target/aarch64/mv-and-mvc1.c          |  37 ++++++
 .../gcc.target/aarch64/mv-and-mvc2.c          |  28 +++++
 .../gcc.target/aarch64/mv-and-mvc3.c          |  40 +++++++
 .../gcc.target/aarch64/mv-and-mvc4.c          |  37 ++++++
 .../gcc.target/aarch64/mv-symbols1.c          |  38 ++++++
 .../gcc.target/aarch64/mv-symbols10.c         |  42 +++++++
 .../gcc.target/aarch64/mv-symbols11.c         |  16 +++
 .../gcc.target/aarch64/mv-symbols12.c         |  27 +++++
 .../gcc.target/aarch64/mv-symbols13.c         |  28 +++++
 .../gcc.target/aarch64/mv-symbols2.c          |  28 +++++
 .../gcc.target/aarch64/mv-symbols3.c          |  27 +++++
 .../gcc.target/aarch64/mv-symbols4.c          |  31 +++++
 .../gcc.target/aarch64/mv-symbols5.c          |  36 ++++++
 .../gcc.target/aarch64/mv-symbols6.c          |  20 ++++
 .../gcc.target/aarch64/mv-symbols7.c          |  47 ++++++++
 .../gcc.target/aarch64/mv-symbols8.c          |  47 ++++++++
 .../gcc.target/aarch64/mv-symbols9.c          |  44 +++++++
 .../gcc.target/aarch64/mvc-symbols1.c         |  25 ++++
 .../gcc.target/aarch64/mvc-symbols2.c         |  15 +++
 .../gcc.target/aarch64/mvc-symbols3.c         |  19 +++
 .../gcc.target/aarch64/mvc-symbols4.c         |  12 ++
 23 files changed, 800 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-and-mvc1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-and-mvc2.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-and-mvc3.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-and-mvc4.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols10.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols11.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols12.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols13.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols2.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols3.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols4.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols5.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols6.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols7.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols8.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mv-symbols9.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mvc-symbols1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mvc-symbols2.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mvc-symbols3.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/mvc-symbols4.c

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 33d0ec1b88f..c39c61a9ef2 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2088,6 +2088,29 @@ previous_tag (tree type)
   return NULL_TREE;
 }
 
+/* Subroutine to mark functions as versioned when using the attribute
+   'target_version'.  */
+
+static void
+maybe_mark_function_versioned (tree decl)
+{
+  if (!DECL_FUNCTION_VERSIONED (decl))
+    {
+      /* We need to insert function version now to make sure the correct
+        pre-mangled assembler name is recorded.  */
+      cgraph_node *node = cgraph_node::get_create (decl);
+
+      if (!node->function_version ())
+       node->insert_new_function_version ();
+
+      DECL_FUNCTION_VERSIONED (decl) = 1;
+
+      tree mangled_name
+       = targetm.mangle_decl_assembler_name (decl, DECL_NAME (decl));
+      SET_DECL_ASSEMBLER_NAME (decl, mangled_name);
+    }
+ }
+
 /* Subroutine of duplicate_decls.  Compare NEWDECL to OLDDECL.
    Returns true if the caller should proceed to merge the two, false
    if OLDDECL should simply be discarded.  As a side effect, issues
@@ -2507,6 +2530,10 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl,
                        "but not here");
            }
        }
+      /* Check if these are unmergable overlapping FMV declarations.  */
+      if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
+         && !diagnose_versioned_decls (olddecl, newdecl))
+       return false;
     }
   else if (VAR_P (newdecl))
     {
@@ -2974,6 +3001,10 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, 
tree oldtype)
 
   if (TREE_CODE (newdecl) == FUNCTION_DECL)
     {
+      DECL_FUNCTION_VERSIONED (olddecl) = DECL_FUNCTION_VERSIONED (olddecl)
+                                         || DECL_FUNCTION_VERSIONED (newdecl);
+      DECL_FUNCTION_VERSIONED (newdecl) = DECL_FUNCTION_VERSIONED (olddecl)
+                                         || DECL_FUNCTION_VERSIONED (newdecl);
       /* If we're redefining a function previously defined as extern
         inline, make sure we emit debug info for the inline before we
         throw it away, in case it was inlined into a function that
@@ -3373,6 +3404,56 @@ pushdecl (tree x)
                TREE_TYPE (b_use->decl) = b_use->u.type;
            }
        }
+
+      /* Check if x is part of a FMV set with b_use.  */
+      if (b_use && TREE_CODE (b_use->decl) == FUNCTION_DECL
+         && TREE_CODE (x) == FUNCTION_DECL && DECL_FILE_SCOPE_P (b_use->decl)
+         && DECL_FILE_SCOPE_P (x)
+         && distinct_version_decls (x, b_use->decl)
+         && comptypes (vistype, type) != 0)
+       {
+         if (current_scope->depth != b->depth)
+           error ("FMV versions must all be declared at the same scope level");
+
+         maybe_mark_function_versioned (b_use->decl);
+         maybe_mark_function_versioned (b->decl);
+         maybe_mark_function_versioned (x);
+
+         cgraph_node *b_node = cgraph_node::get_create (b_use->decl);
+         cgraph_function_version_info *b_v = b_node->function_version ();
+         if (!b_v)
+           b_v = b_node->insert_new_function_version ();
+
+         /* Check if this new node conflicts with any previous functions
+            in the set.  */
+         cgraph_function_version_info *version = b_v;
+         for (; version; version = version->next)
+           if (!distinct_version_decls (version->this_node->decl, x))
+             {
+               /* The decls define overlapping version, so attempt to merge
+                  or diagnose the conflict.  */
+               if (duplicate_decls (x, version->this_node->decl))
+                 return version->this_node->decl;
+               else
+                 return error_mark_node;
+             }
+
+         /* This is a new version to be added to FMV structure.  */
+         cgraph_node::add_function_version (b_v, x);
+
+         /* Get the first node from the structure.  */
+         cgraph_function_version_info *default_v = b_v;
+         while (default_v->prev)
+           default_v = default_v->prev;
+         /* Always use the default node for the bindings.  */
+         b_use->decl = default_v->this_node->decl;
+         b->decl = default_v->this_node->decl;
+
+         /* Node is not a duplicate, so no need to do the rest of the
+            checks.  */
+         return x;
+       }
+
       if (duplicate_decls (x, b_use->decl))
        {
          if (b_use != b)
@@ -4497,6 +4578,12 @@ tree
 lookup_name (tree name)
 {
   struct c_binding *b = I_SYMBOL_BINDING (name);
+  /* Do not resolve non-default function versions.  */
+  if (b
+      && TREE_CODE (b->decl) == FUNCTION_DECL
+      && DECL_FUNCTION_VERSIONED (b->decl)
+      && !is_function_default_version (b->decl))
+    return NULL_TREE;
   if (b && !b->invisible)
     {
       maybe_record_typedef_use (b->decl);
@@ -5748,6 +5835,17 @@ start_decl (struct c_declarator *declarator, struct 
c_declspecs *declspecs,
       && VAR_OR_FUNCTION_DECL_P (decl))
       objc_check_global_decl (decl);
 
+  /* To enable versions to be created across TU's we mark and mangle all
+     non-default versioned functions.  */
+  if (TREE_CODE (decl) == FUNCTION_DECL
+      && !TARGET_HAS_FMV_TARGET_ATTRIBUTE
+      && get_target_version (decl).is_valid ())
+    {
+      maybe_mark_function_versioned (decl);
+      if (current_scope != file_scope)
+       error ("versioned declarations are only allowed at file scope");
+    }
+
   /* Add this decl to the current scope.
      TEM may equal DECL or it may be a previous decl of the same name.  */
   if (do_push)
@@ -10847,6 +10945,17 @@ start_function (struct c_declspecs *declspecs, struct 
c_declarator *declarator,
       warn_parm_array_mismatch (origloc, old_decl, parms);
     }
 
+  /* To enable versions to be created across TU's we mark and mangle all
+     non-default versioned functions.  */
+  if (TREE_CODE (decl1) == FUNCTION_DECL
+      && !TARGET_HAS_FMV_TARGET_ATTRIBUTE
+      && get_target_version (decl1).is_valid ())
+    {
+      maybe_mark_function_versioned (decl1);
+      if (current_scope != file_scope)
+       error ("versioned definitions are only allowed at file scope");
+    }
+
   /* Record the decl so that the function name is defined.
      If we already have a decl for this name, and it is a FUNCTION_DECL,
      use the old decl.  */
@@ -13693,6 +13802,10 @@ c_parse_final_cleanups (void)
     c_write_global_declarations_1 (BLOCK_VARS (DECL_INITIAL (t)));
   c_write_global_declarations_1 (BLOCK_VARS (ext_block));
 
+  /* Call this to set cpp_implicit_aliases_done on all nodes.  This is
+     important for function multiversioning aliases to get resolved.  */
+  symtab->process_same_body_aliases ();
+
   if (!in_lto_p)
     free_attr_access_data ();
 
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-1.c 
b/gcc/testsuite/gcc.target/aarch64/mv-1.c
new file mode 100644
index 00000000000..6f095ecd7a7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-1.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("default"))) int
+foo ()
+{
+  return 1;
+}
+
+__attribute__ ((target_version ("rng"))) int
+foo ()
+{
+  return 2;
+}
+
+__attribute__ ((target_version ("flagm"))) int
+foo ()
+{
+  return 3;
+}
+
+__attribute__ ((target_version ("rng+flagm"))) int
+foo ()
+{
+  return 4;
+}
+
+int
+bar ()
+{
+  return foo ();
+}
+
+/* Check usage of the first two FMV features, in case of off-by-one errors.  */
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mrng:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MrngMflagm:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mflagm:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc1.c 
b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc1.c
new file mode 100644
index 00000000000..39ed306eec2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc1.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_version("default")))
+int foo ()
+{
+  return 0;
+}
+
+__attribute__((target_clones("dotprod", "sve+sve2")))
+int foo ()
+{
+  return 1;
+}
+
+__attribute__((target_clones("sve", "sve2")))
+int foo ()
+{
+  return 2;
+}
+
+int bar()
+{
+  return foo ();
+}
+
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msve:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc2.c 
b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc2.c
new file mode 100644
index 00000000000..17c7cbdd02d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc2.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_version("default")))
+int foo ();
+
+__attribute__((target_clones("dotprod", "sve+sve2")))
+int foo ()
+{
+  return 1;
+}
+
+__attribute__((target_clones("sve", "sve2")))
+int foo ()
+{
+  return 2;
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msve:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 0 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc3.c 
b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc3.c
new file mode 100644
index 00000000000..8325c8e0699
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc3.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_clones("dotprod", "sve+sve2")))
+int foo ();
+
+__attribute__((target_version("default")))
+int foo ()
+{
+  return 0;
+}
+
+__attribute__((target_clones("sve", "sve2")))
+int foo ()
+{
+  return 2;
+}
+
+int bar()
+{
+  return foo ();
+}
+
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msve:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
+// { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, foo\.default\n" 1 } 
}
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, foo\._Mdotprod\n" 1 
} } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, foo\._MsveMsve2\n" 
1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, foo\._Msve\n" 1 } } 
*/
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, foo\._Msve2\n" 1 } 
} */
+
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-and-mvc4.c 
b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc4.c
new file mode 100644
index 00000000000..951c9500d74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-and-mvc4.c
@@ -0,0 +1,37 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0" } */
+
+__attribute__((target_version("dotprod")))
+int foo ()
+{
+  return 0;
+}
+
+__attribute__((target_clones("default", "sve+sve2")))
+int foo ()
+{
+  return 1;
+}
+
+__attribute__((target_clones("sve", "sve2")))
+int foo ()
+{
+  return 2;
+}
+
+int bar()
+{
+  return foo ();
+}
+
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msve:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols1.c 
b/gcc/testsuite/gcc.target/aarch64/mv-symbols1.c
new file mode 100644
index 00000000000..798227826e5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols1.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// Basic case of fmv correctness with all functions and use in one TU.
+
+__attribute__ ((target_version ("default"))) 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 ();
+}
+
+/* When updating any of the symbol names in these tests, make sure to also
+   update any tests for their absence in mv-symbolsN.C */
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols10.c 
b/gcc/testsuite/gcc.target/aarch64/mv-symbols10.c
new file mode 100644
index 00000000000..d5256389d7b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols10.c
@@ -0,0 +1,42 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+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 "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._MsveMsve2\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._Mdotprod\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\.default\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols11.c 
b/gcc/testsuite/gcc.target/aarch64/mv-symbols11.c
new file mode 100644
index 00000000000..fd3dc345a59
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols11.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// Check that types can be combined
+
+__attribute__ ((target_version ("default"))) int
+foo (int a, int (*b)[4]) { return 1; }
+
+__attribute__ ((target_version ("dotprod"))) int
+foo (int a, int (*b)[]) { return 3; }
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols12.c 
b/gcc/testsuite/gcc.target/aarch64/mv-symbols12.c
new file mode 100644
index 00000000000..1a0b667eb5f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols12.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("default"))) int
+foo () { return 1; }
+
+__attribute__ ((target_version ("dotprod"))) int
+foo () { return 3; }
+
+int bar ()
+{
+  int (*test)() = foo;
+
+  test();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\], foo\._Mdotprod\n" 1 
} } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\], foo\.default\n" 1 } 
} */
+
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, foo\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols13.c 
b/gcc/testsuite/gcc.target/aarch64/mv-symbols13.c
new file mode 100644
index 00000000000..308dace64a7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols13.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("default"))) int
+foo ();
+
+int bar ()
+{
+  int (*test)() = foo;
+
+  test();
+}
+
+__attribute__ ((target_version ("dotprod"))) int
+foo () { return 3; }
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\], foo\._Mdotprod\n" 0 
} } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\], foo\.default\n" 0 } 
} */
+
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, foo\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 0 } } */
+
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols2.c 
b/gcc/testsuite/gcc.target/aarch64/mv-symbols2.c
new file mode 100644
index 00000000000..a8732caf214
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols2.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// FMV correctness with definitions but no call
+
+__attribute__ ((target_version ("default"))) int
+foo ()
+{
+  return 1;
+}
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ()
+{
+  return 3;
+}
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ()
+{
+  return 5;
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols3.c 
b/gcc/testsuite/gcc.target/aarch64/mv-symbols3.c
new file mode 100644
index 00000000000..962bae93577
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols3.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// FMV correctness with declarations but no implementation
+
+__attribute__ ((target_version ("default"))) int
+foo ();
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ();
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ();
+
+int
+bar ()
+{
+  return foo ();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 0 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols4.c 
b/gcc/testsuite/gcc.target/aarch64/mv-symbols4.c
new file mode 100644
index 00000000000..a476800b2c5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols4.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// FMV correctness with a default implementation and declarations of other
+// versions
+
+__attribute__ ((target_version ("default"))) int
+foo ()
+{
+  return 1;
+}
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ();
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ();
+
+int
+bar ()
+{
+  return foo ();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols5.c 
b/gcc/testsuite/gcc.target/aarch64/mv-symbols5.c
new file mode 100644
index 00000000000..4df20009f79
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols5.c
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+// FMV correctness with default declaration, and implementations of other
+// versions.
+
+__attribute__ ((target_version ("default"))) int
+foo ();
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ()
+{
+  return 3;
+}
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ()
+{
+  return 5;
+}
+
+int
+bar ()
+{
+  return foo ();
+}
+
+/* When updating any of the symbol names in these tests, make sure to also
+   update any tests for their absence in mvc-symbolsN.C */
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 0 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols6.c 
b/gcc/testsuite/gcc.target/aarch64/mv-symbols6.c
new file mode 100644
index 00000000000..cbf8bcaf8e1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols6.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("default"))) int
+foo ()
+{
+  return 1;
+}
+
+int bar()
+{
+  return foo();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "bl\tfoo.default\n" 1 } } */
+/* { dg-final { scan-assembler-times ".global\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times ".set\tfoo,foo.default\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols7.c 
b/gcc/testsuite/gcc.target/aarch64/mv-symbols7.c
new file mode 100644
index 00000000000..2ea4d2ebf0f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols7.c
@@ -0,0 +1,47 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_version ("dotprod"))) int
+foo ();
+
+__attribute__ ((target_version ("sve+sve2"))) int
+foo ();
+
+__attribute__ ((target_version ("default"))) int
+foo ();
+
+int
+bar ()
+{
+  return 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;
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._MsveMsve2\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._Mdotprod\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\.default\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols8.c 
b/gcc/testsuite/gcc.target/aarch64/mv-symbols8.c
new file mode 100644
index 00000000000..3e3eaf21aa9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols8.c
@@ -0,0 +1,47 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__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 "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._MsveMsve2\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._Mdotprod\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\.default\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mv-symbols9.c 
b/gcc/testsuite/gcc.target/aarch64/mv-symbols9.c
new file mode 100644
index 00000000000..8e0864f1663
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mv-symbols9.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__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 "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._MsveMsve2\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\._Mdotprod\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tadrp\tx., foo\.default\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-symbols1.c 
b/gcc/testsuite/gcc.target/aarch64/mvc-symbols1.c
new file mode 100644
index 00000000000..3ad15e5bb73
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mvc-symbols1.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_clones ("default", "dotprod", "sve+sve2"))) int
+foo ()
+{
+  return 1;
+}
+
+int
+bar ()
+{
+  return foo ();
+}
+
+/* When updating any of the symbol names in these tests, make sure to also
+   update any tests for their absence in mvc-symbolsN.C */
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-symbols2.c 
b/gcc/testsuite/gcc.target/aarch64/mvc-symbols2.c
new file mode 100644
index 00000000000..78385ed904b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mvc-symbols2.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_clones ("default", "dotprod", "sve+sve2"))) int
+foo ()
+{
+  return 1;
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-symbols3.c 
b/gcc/testsuite/gcc.target/aarch64/mvc-symbols3.c
new file mode 100644
index 00000000000..1cbe3fd0850
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mvc-symbols3.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_clones ("default", "dotprod", "sve+sve2"))) int
+foo ();
+
+int
+bar ()
+{
+  return foo ();
+}
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\tbl\tfoo\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 0 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/mvc-symbols4.c 
b/gcc/testsuite/gcc.target/aarch64/mvc-symbols4.c
new file mode 100644
index 00000000000..abaf60f91c3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/mvc-symbols4.c
@@ -0,0 +1,12 @@
+/* { dg-do compile } */
+/* { dg-options "-O0" } */
+
+__attribute__ ((target_clones ("default", "dotprod", "sve+sve2"))) int
+foo ();
+
+/* { dg-final { scan-assembler-times "\nfoo\.default:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Mdotprod:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._MsveMsve2:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\nfoo\.resolver:\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\tfoo, 
%gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\tfoo,foo\.resolver\n" 0 } } */
-- 
2.34.1

Reply via email to