From: Alfie Richards <[email protected]>

Adds support for the AArch64 fmv priority syntax.

This allows users to override the default function ordering.

For example:

```c
int bar [[gnu::target_version("default")]] (int){
  return 1;
}

int bar [[gnu::target_version("dotprod;priority=2")]] (int) {
  return 2;
}

int bar [[gnu::target_version("sve;priority=1")]] (int) {
  return 3;
}
```

gcc/ChangeLog:

        * config/aarch64/aarch64.cc (aarch64_parse_fmv_features): Add parsing
        for priority arguments.
        (aarch64_process_target_version_attr): Update call to
        aarch64_parse_fmv_features.
        (get_feature_mask_for_version): Update call to
        aarch64_parse_fmv_features.
        (aarch64_compare_version_priority): Add logic to order by priority if 
present.
        (aarch64_functions_b_resolvable_from_a): Update call to
        aarch64_parse_fmv_features.
        (aarch64_mangle_decl_assembler_name): Update call to
        aarch64_parse_fmv_features.
        (dispatch_function_versions): Add logic to sort by priority.
        (aarch64_same_function_versions): Add diagnostic if invalid use of
        priority syntax.
        (aarch64_merge_decl_attributes): Add logic to make suer priority
        arguments are preserved.
        (aarch64_check_target_clone_version): Update call to
        aarch64_parse_fmv_features.

gcc/testsuite/ChangeLog:

        * gcc.target/aarch64/fmv_priority3.c: New test.
---
 gcc/config/aarch64/aarch64.cc                 | 126 +++++++++++++++---
 .../gcc.target/aarch64/fmv_priority3.c        |  24 ++++
 2 files changed, 130 insertions(+), 20 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/aarch64/fmv_priority3.c

diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 3f341f33f0a..6cfd269c07b 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -20403,21 +20403,27 @@ static aarch64_fmv_feature_datum 
aarch64_fmv_feature_data[] = {
 static enum aarch_parse_opt_result
 aarch64_parse_fmv_features (string_slice str, aarch64_feature_flags *isa_flags,
                            aarch64_fmv_feature_mask *feature_mask,
+                           unsigned int *priority,
                            std::string *invalid_extension)
 {
   if (feature_mask)
     *feature_mask = 0ULL;
+  if (priority)
+    *priority = 0;
+
+  str = str.strip ();
 
   if (str == "default")
     return AARCH_PARSE_OK;
 
-  gcc_assert (str.is_valid ());
-
-  while (str.is_valid ())
+  /* Parse the architecture part of the string.  */
+  string_slice arch_string = string_slice::tokenize (&str, ";");
+  gcc_assert (arch_string.is_valid ());
+  while (arch_string.is_valid ())
     {
       string_slice ext;
 
-      ext = string_slice::tokenize (&str, "+");
+      ext = string_slice::tokenize (&arch_string, "+");
 
       gcc_assert (ext.is_valid ());
 
@@ -20458,6 +20464,60 @@ aarch64_parse_fmv_features (string_slice str, 
aarch64_feature_flags *isa_flags,
        }
     }
 
+  /* Parse any extra arguments.  */
+  while (str.is_valid ())
+    {
+      string_slice argument = string_slice::tokenize (&str, ";");
+      string_slice arg = argument;
+      char n[4];
+      argument = argument.strip ();
+      string_slice name = string_slice::tokenize (&argument, "=").strip ();
+      unsigned int priority_res = 0;
+
+      /* priority=N argument (only one is allowed).  */
+      if (name == "priority" && priority_res == 0)
+       {
+         if (!argument.is_valid ())
+           {
+             *invalid_extension = std::string (arg.begin (), arg.size ());
+             return AARCH_PARSE_INVALID_FEATURE;
+           }
+         argument = argument.strip ();
+         if (!argument.is_valid ())
+           {
+             *invalid_extension = std::string (arg.begin (), arg.size ());
+             return AARCH_PARSE_INVALID_FEATURE;
+           }
+         if (argument.size () > 3)
+           {
+             *invalid_extension = std::string (arg.begin (), arg.size ());
+             return AARCH_PARSE_INVALID_FEATURE;
+           }
+         memcpy (n, argument.begin (),
+                 std::min (argument.size (), (unsigned int) 3));
+
+         int res = sscanf (n, "%ud", &priority_res);
+         if (res != 1)
+           {
+             *invalid_extension = std::string (arg.begin (), arg.size ());
+             return AARCH_PARSE_INVALID_FEATURE;
+           }
+         if (priority_res < 1 || priority_res > 255)
+           {
+             *invalid_extension = std::string (arg.begin (), arg.size ());
+             return AARCH_PARSE_INVALID_FEATURE;
+           }
+         if (priority)
+           *priority = priority_res;
+       }
+      else
+       {
+         /* Unrecognised argument.  */
+         *invalid_extension = std::string (arg.begin (), arg.size ());
+         return AARCH_PARSE_INVALID_FEATURE;
+       }
+    }
+
   return AARCH_PARSE_OK;
 }
 
@@ -20489,7 +20549,7 @@ aarch64_process_target_version_attr (tree args)
   auto isa_flags = aarch64_asm_isa_flags;
 
   std::string invalid_extension;
-  parse_res = aarch64_parse_fmv_features (str, &isa_flags, NULL,
+  parse_res = aarch64_parse_fmv_features (str, &isa_flags, NULL, NULL,
                                          &invalid_extension);
 
   if (parse_res == AARCH_PARSE_OK)
@@ -20578,7 +20638,7 @@ aarch64_option_valid_version_attribute_p (tree fndecl, 
tree, tree args, int)
    add or remove redundant feature requirements.  */
 
 static aarch64_fmv_feature_mask
-get_feature_mask_for_version (tree decl)
+get_feature_mask_for_version (tree decl, unsigned int *priority)
 {
   tree version_attr = lookup_attribute ("target_version",
                                        DECL_ATTRIBUTES (decl));
@@ -20592,7 +20652,7 @@ get_feature_mask_for_version (tree decl)
   aarch64_fmv_feature_mask feature_mask;
 
   parse_res = aarch64_parse_fmv_features (version_string, NULL,
-                                         &feature_mask, NULL);
+                                         &feature_mask, priority, NULL);
 
   /* We should have detected any errors before getting here.  */
   gcc_assert (parse_res == AARCH_PARSE_OK);
@@ -20628,9 +20688,13 @@ compare_feature_masks (aarch64_fmv_feature_mask mask1,
 int
 aarch64_compare_version_priority (tree decl1, tree decl2)
 {
-  auto mask1 = get_feature_mask_for_version (decl1);
-  auto mask2 = get_feature_mask_for_version (decl2);
+  unsigned int priority_1 = 0;
+  unsigned int priority_2 = 0;
+  auto mask1 = get_feature_mask_for_version (decl1, &priority_1);
+  auto mask2 = get_feature_mask_for_version (decl2, &priority_2);
 
+  if (priority_1 != priority_2)
+    return priority_1 > priority_2 ? 1 : -1;
   return compare_feature_masks (mask1, mask2);
 }
 
@@ -20647,9 +20711,9 @@ aarch64_functions_b_resolvable_from_a (tree decl_a, 
tree decl_b, tree baseline)
   auto a_version = get_target_version (decl_a);
   auto b_version = get_target_version (decl_b);
   if (a_version.is_valid ())
-    aarch64_parse_fmv_features (a_version, &isa_a, NULL, NULL);
+    aarch64_parse_fmv_features (a_version, &isa_a, NULL, NULL, NULL);
   if (b_version.is_valid ())
-    aarch64_parse_fmv_features (b_version, &isa_b, NULL, NULL);
+    aarch64_parse_fmv_features (b_version, &isa_b, NULL, NULL, NULL);
 
   /* Are there any bits of b that arent in a.  */
   if (isa_b & (~isa_a))
@@ -20727,7 +20791,7 @@ aarch64_mangle_decl_assembler_name (tree decl, tree id)
       else if (DECL_FUNCTION_VERSIONED (decl))
        {
          aarch64_fmv_feature_mask feature_mask
-           = get_feature_mask_for_version (decl);
+           = get_feature_mask_for_version (decl, NULL);
 
          if (feature_mask == 0ULL)
            return clone_identifier (id, "default");
@@ -21157,19 +21221,27 @@ aarch64_get_function_versions_dispatcher (void *decl)
    function.  */
 
 bool
-aarch64_same_function_versions (string_slice str1, string_slice str2)
+aarch64_same_function_versions (string_slice old_str, string_slice new_str)
 {
   enum aarch_parse_opt_result parse_res;
   aarch64_fmv_feature_mask feature_mask1;
   aarch64_fmv_feature_mask feature_mask2;
-  parse_res = aarch64_parse_fmv_features (str1, NULL,
-                                         &feature_mask1, NULL);
+  unsigned int priority1;
+  unsigned int priority2;
+  parse_res = aarch64_parse_fmv_features (old_str, NULL,
+                                         &feature_mask1, &priority1, NULL);
   gcc_assert (parse_res == AARCH_PARSE_OK);
-  parse_res = aarch64_parse_fmv_features (str2, NULL,
-                                         &feature_mask2, NULL);
+  parse_res = aarch64_parse_fmv_features (new_str, NULL,
+                                         &feature_mask2, &priority2, NULL);
   gcc_assert (parse_res == AARCH_PARSE_OK);
 
-  return feature_mask1 == feature_mask2;
+  if (feature_mask1 != feature_mask2)
+    return false;
+
+  if (priority1 != priority2 && priority1 != 0)
+    error ("inconsistent priority values");
+
+  return true;
 }
 
 /* Implement TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P.  Use an opt-out
@@ -30276,6 +30348,20 @@ aarch64_merge_decl_attributes (tree olddecl, tree 
newdecl)
        aarch64_check_arm_new_against_type (TREE_VALUE (new_new), newdecl);
     }
 
+  /* For target_version and target_clones, make sure we take the old version
+     as priority syntax cannot be added addetively.  */
+  tree old_target_version = lookup_attribute ("target_version", old_attrs);
+  tree new_target_version = lookup_attribute ("target_version", new_attrs);
+
+  if (old_target_version && new_target_version)
+    TREE_VALUE (new_target_version) = TREE_VALUE (old_target_version);
+
+  tree old_target_clones = lookup_attribute ("target_clones", old_attrs);
+  tree new_target_clones = lookup_attribute ("target_clones", new_attrs);
+
+  if (old_target_clones && new_target_clones)
+    TREE_VALUE (new_target_clones) = TREE_VALUE (old_target_clones);
+
   return merge_attributes (old_attrs, new_attrs);
 }
 
@@ -31944,8 +32030,8 @@ aarch64_check_target_clone_version (string_slice str, 
location_t *loc)
   auto isa_flags = aarch64_asm_isa_flags;
   std::string invalid_extension;
 
-  parse_res
-    = aarch64_parse_fmv_features (str, &isa_flags, NULL, &invalid_extension);
+  parse_res = aarch64_parse_fmv_features (str, &isa_flags, NULL, NULL,
+                                         &invalid_extension);
 
   if (loc == NULL)
     return parse_res == AARCH_PARSE_OK;
diff --git a/gcc/testsuite/gcc.target/aarch64/fmv_priority3.c 
b/gcc/testsuite/gcc.target/aarch64/fmv_priority3.c
new file mode 100644
index 00000000000..0f24bf7c798
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/fmv_priority3.c
@@ -0,0 +1,24 @@
+/* { dg-do compile }  */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-O0 -fdump-ipa-targetclone1-details" } */
+
+int foo [[gnu::target_version("default")]] (int) { return 1; }
+int foo [[gnu::target_version("dotprod;priority=1")]] (int) { return 2; }
+int foo [[gnu::target_version("sve")]] (int) { return 3; }
+
+// Priority1 dotprod > sve
+/* { dg-final { scan-ipa-dump-times "Version order for 
foo/\[0-9\]+:\\nfoo\.default/\[0-9\]+\\nfoo\._Msve/\[0-9\]+\\nfoo\._Mdotprod/\[0-9\]+\\n"
 1 "targetclone1" } } */
+
+int bar [[gnu::target_version("default")]] (int) { return 1; }
+int bar [[gnu::target_version("dotprod;priority=2")]] (int) { return 2; }
+int bar [[gnu::target_version("sve;priority=1")]] (int) { return 3; }
+
+// Priority2 dotprod > Priority1 sve
+/* { dg-final { scan-ipa-dump-times "Version order for 
bar/\[0-9\]+:\\nbar\.default/\[0-9\]+\\nbar\._Msve/\[0-9\]+\\nbar\._Mdotprod/\[0-9\]+\\n"
 1 "targetclone1" } } */
+
+int baz [[gnu::target_version("default")]] (int) { return 1; }
+int baz [[gnu::target_version("dotprod;priority=1")]] (int) { return 2; }
+int baz [[gnu::target_version("sve;priority=1")]] (int) { return 3; }
+
+// Priority1 sve > Priority1 dotprod
+/* { dg-final { scan-ipa-dump-times "Version order for 
baz/\[0-9\]+:\\nbaz\.default/\[0-9\]+\\nbaz\._Mdotprod/\[0-9\]+\\nbaz\._Msve/\[0-9\]+\\n"
 1 "targetclone1" } } */
-- 
2.34.1

Reply via email to