https://gcc.gnu.org/g:2a3abe41cbd4b8d6b509b72f086ff1b58fb285de

commit 2a3abe41cbd4b8d6b509b72f086ff1b58fb285de
Author: Alfie Richards <alfie.richa...@arm.com>
Date:   Wed Apr 2 13:37:02 2025 +0000

    c: Add target_version attribute support.
    
    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): Dont 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.

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

diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index c778c7febfa0..977e6ef37878 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -2087,6 +2087,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
@@ -2973,6 +2996,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
@@ -3165,6 +3192,15 @@ duplicate_decls (tree newdecl, tree olddecl)
 {
   tree newtype = NULL, oldtype = NULL;
 
+  if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
+      && !mergeable_version_decls (olddecl, newdecl))
+    {
+      auto_diagnostic_group d;
+      error ("conflicting versioned declarations of %q+D", newdecl);
+      locate_old_decl (olddecl);
+      return false;
+    }
+
   if (!diagnose_mismatched_decls (newdecl, olddecl, &newtype, &oldtype))
     {
       /* Avoid `unused variable' and other warnings for OLDDECL.  */
@@ -3372,6 +3408,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)
@@ -4496,6 +4582,11 @@ tree
 lookup_name (tree name)
 {
   struct c_binding *b = I_SYMBOL_BINDING (name);
+  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);
@@ -5765,6 +5856,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)
@@ -10860,6 +10962,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.  */
@@ -13726,6 +13839,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 000000000000..6f095ecd7a73
--- /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 000000000000..c8ffbb53ee59
--- /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("sme", "sme2")))
+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\._Msme:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msme2:\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 000000000000..74673f39d89a
--- /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("sme", "sme2")))
+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\._Msme:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msme2:\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 000000000000..5f5b661d1f91
--- /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("sme", "sme2")))
+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\._Msme:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msme2:\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\._Msme\n" 1 } } 
*/
+/* { dg-final { scan-assembler-times "\n\tadrp\tx\[0-9\]+, foo\._Msme2\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 000000000000..c8c08ffffd9e
--- /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("sme", "sme2")))
+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\._Msme:\n" 1 } } */
+/* { dg-final { scan-assembler-times "\nfoo\._Msme2:\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 000000000000..798227826e50
--- /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 000000000000..d5256389d7bb
--- /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 000000000000..fd3dc345a599
--- /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 000000000000..1a0b667eb5f1
--- /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 000000000000..308dace64a78
--- /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 000000000000..a8732caf2140
--- /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 000000000000..962bae935773
--- /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 000000000000..a476800b2c53
--- /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 000000000000..4df20009f795
--- /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 000000000000..cbf8bcaf8e16
--- /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 000000000000..2ea4d2ebf0fb
--- /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 000000000000..3e3eaf21aa9e
--- /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 000000000000..8e0864f1663f
--- /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 000000000000..3ad15e5bb73d
--- /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 000000000000..78385ed904bb
--- /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 000000000000..1cbe3fd08509
--- /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 000000000000..abaf60f91c3e
--- /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 } } */

Reply via email to