https://gcc.gnu.org/g:558918d6b2ac3d726d74543e1a2ccc200bf0fa35
commit r16-6540-g558918d6b2ac3d726d74543e1a2ccc200bf0fa35 Author: Alfie Richards <[email protected]> Date: Tue Oct 7 14:16:16 2025 +0000 aarch64: Add support for fmv priority syntax. 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.target/aarch64/fmv_priority_error1.c: New test. * gcc.target/aarch64/fmv_priority_error2.c: New test. Diff: --- gcc/config/aarch64/aarch64.cc | 146 +++++++++++++++++---- gcc/testsuite/gcc.target/aarch64/fmv_priority3.c | 26 ++++ .../gcc.target/aarch64/fmv_priority_error1.c | 11 ++ .../gcc.target/aarch64/fmv_priority_error2.c | 8 ++ 4 files changed, 166 insertions(+), 25 deletions(-) diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc index 3ba2e686ebfc..293afa52b3b3 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -20916,25 +20916,29 @@ 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, ";"); + while (arch_string.is_valid ()) { string_slice ext; - ext = string_slice::tokenize (&str, "+"); - - gcc_assert (ext.is_valid ()); + ext = string_slice::tokenize (&arch_string, "+"); + ext = ext.strip (); - if (!ext.is_valid () || ext.empty ()) + if (ext.empty ()) return AARCH_PARSE_MISSING_ARG; int num_features = ARRAY_SIZE (aarch64_fmv_feature_data); @@ -20971,6 +20975,60 @@ aarch64_parse_fmv_features (string_slice str, aarch64_feature_flags *isa_flags, } } + /* Parse any extra arguments. */ + unsigned int priority_res = 0; + while (str.is_valid ()) + { + string_slice argument = string_slice::tokenize (&str, ";").strip (); + /* Save the whole argument for diagnostics. */ + string_slice arg = argument; + string_slice name = string_slice::tokenize (&argument, "=").strip (); + argument = argument.strip (); + if (!argument.is_valid () || argument.empty ()) + { + *invalid_extension = std::string (arg.begin (), arg.size ()); + return AARCH_PARSE_INVALID_FEATURE; + } + + /* priority=N argument (only one is allowed). */ + if (name == "priority" && priority_res == 0) + { + /* Priority values can only be between 1 and 255, so any greater than + 3 digits long are invalid. */ + if (argument.size () > 3) + { + *invalid_extension = std::string (arg.begin (), arg.size ()); + return AARCH_PARSE_INVALID_FEATURE; + } + + /* Parse the string value. */ + for (char c : argument) + { + if (!ISDIGIT (c)) + { + priority_res = 0; + break; + } + priority_res = 10 * priority_res + c - '0'; + } + + /* Check the entire string parsed, and that the value is in range. */ + 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; } @@ -21002,7 +21060,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) @@ -21091,7 +21149,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)); @@ -21105,7 +21163,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); @@ -21141,9 +21199,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); } @@ -21160,9 +21222,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)) @@ -21240,7 +21302,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"); @@ -21543,7 +21605,7 @@ dispatch_function_versions (tree dispatch_decl, FOR_EACH_VEC_ELT_REVERSE ((*fndecls), i, version_decl) { aarch64_fmv_feature_mask feature_mask - = get_feature_mask_for_version (version_decl); + = get_feature_mask_for_version (version_decl, NULL); *empty_bb = add_condition_to_bb (dispatch_decl, version_decl, feature_mask, mask_var, *empty_bb); } @@ -21666,26 +21728,46 @@ aarch64_get_function_versions_dispatcher (void *decl) return dispatch_decl; } -/* This function returns true if STR1 and STR2 are version strings for the same - function. */ +/* This function returns true if OLD_STR and NEW_STR are version strings for the + same function. + Emits a diagnostic if the new version should not be merged with the old + version due to a prioriy value mismatch. */ bool -aarch64_same_function_versions (string_slice old_str, const_tree, - string_slice new_str, const_tree) +aarch64_same_function_versions (string_slice old_str, const_tree old_decl, + string_slice new_str, const_tree new_decl) { enum aarch_parse_opt_result parse_res; aarch64_fmv_feature_mask old_feature_mask; aarch64_fmv_feature_mask new_feature_mask; + unsigned int old_priority; + unsigned int new_priority; parse_res = aarch64_parse_fmv_features (old_str, NULL, &old_feature_mask, - NULL); + &old_priority, NULL); gcc_assert (parse_res == AARCH_PARSE_OK); parse_res = aarch64_parse_fmv_features (new_str, NULL, &new_feature_mask, - NULL); + &new_priority, NULL); gcc_assert (parse_res == AARCH_PARSE_OK); - return old_feature_mask == new_feature_mask; + if (old_feature_mask != new_feature_mask) + return false; + + /* Accept the case where the old version had a defined priority value but the + new version does not, as we infer the new version inherits the same + priority. */ + if (old_decl && new_decl && old_priority != new_priority + && (new_priority != 0)) + { + error ("%q+D has an inconsistent function multi-version priority value", + new_decl); + inform (DECL_SOURCE_LOCATION (old_decl), + "%q+D was previously declared here with priority value of %d", + old_decl, old_priority); + } + + return true; } /* Implement TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P. Use an opt-out @@ -30927,6 +31009,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); } @@ -32942,8 +33038,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 000000000000..535e99ab6a08 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/fmv_priority3.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc "" } */ +/* { dg-options "-O0 -fdump-ipa-targetclone1-details" } */ + +int foo [[gnu::target_version("dotprod;priority=1")]] (void); +int foo [[gnu::target_version("default")]] (void) { return 1; } +int foo [[gnu::target_version("dotprod;priority=1")]] (void) { return 2; } +int foo [[gnu::target_version("sve")]] (void) { 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("dotprod;priority=2")]] (void); +int bar [[gnu::target_version("default")]] (void) { return 1; } +int bar [[gnu::target_version("dotprod")]] (void) { return 2; } +int bar [[gnu::target_version("sve;priority=1")]] (void) { 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")]] (void) { return 1; } +int baz [[gnu::target_version("dotprod;priority=1")]] (void) { return 2; } +int baz [[gnu::target_version("sve;priority=1")]] (void) { 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" } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/fmv_priority_error1.c b/gcc/testsuite/gcc.target/aarch64/fmv_priority_error1.c new file mode 100644 index 000000000000..7b7665367c1f --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/fmv_priority_error1.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc } */ +/* { dg-options "-std=c23 -pedantic-errors" } */ + +// Value was 2, now is 1 +int foo [[gnu::target_version("dotprod;priority=2")]] (void); /* { dg-message ".foo \\\[\\\[target_version\\\(.dotprod;priority=2.\\\)\\\]\\\]. was previously declared here with priority value of 2" } */ +int foo [[gnu::target_version("dotprod;priority=1")]] (void) { return 2; } /* { dg-error ".foo \\\[\\\[target_version\\\(.dotprod;priority=1.\\\)\\\]\\\]. has an inconsistent function multi-version priority value" } */ + +// Value was 0, now is 2 +int bar [[gnu::target_version("dotprod")]] (void); /* { dg-message ".bar \\\[\\\[target_version\\\(.dotprod.\\\)\\\]\\\]. was previously declared here with priority value of 0" } */ +int bar [[gnu::target_version("dotprod;priority=2")]] (void) { return 2; } /* { dg-error ".bar \\\[\\\[target_version\\\(.dotprod;priority=2.\\\)\\\]\\\]. has an inconsistent function multi-version priority value" } */ diff --git a/gcc/testsuite/gcc.target/aarch64/fmv_priority_error2.c b/gcc/testsuite/gcc.target/aarch64/fmv_priority_error2.c new file mode 100644 index 000000000000..0f212ff6aae3 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/fmv_priority_error2.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-require-ifunc } */ +/* { dg-options "-std=c23 -pedantic-errors" } */ + +int foo [[gnu::target_version("dotprod;priority=0")]] (void); /* { dg-error "invalid feature modifier .priority=0. of value .dotprod;priority=0. in .target_version. attribute" } */ +int foo [[gnu::target_version("dotprod;priority=-1")]] (void); /* { dg-error "invalid feature modifier .priority=-1. of value .dotprod;priority=-1. in .target_version. attribute" } */ +int foo [[gnu::target_version("dotprod;priority=256")]] (void); /* { dg-error "invalid feature modifier .priority=256. of value .dotprod;priority=256. in .target_version. attribute" } */ +int foo [[gnu::target_version("priority=4;dotprod")]] (void); /* { dg-error "invalid feature modifier .priority=4. of value .priority=4;dotprod. in .target_version. attribute" } */
