This patch introduces the TARGET_REJECT_FUNCTION_CLONE_VERSION hook which is used to determine if a target_clones version string parses.
If true is returned, a warning is emitted and from then on the version is ignored. This is as specified in the Arm C Language Extension. The purpose of this is to allow some portability of code using target_clones attributes. Currently this is only properly implemented for the Aarch64 backend. For riscv which is the only other backend which uses target_version semantics a partial implementation is present, where this hook is used to check parsing, in which errors will be emitted on a failed parse rather than warnings. A refactor of the riscv parsing logic would be required to enable this functionality fully. This fixes PR 118339 where parse failures could cause ICE in Aarch64. gcc/ChangeLog: PR target/118339 * target.def: Add reject_target_clone_version hook. * tree.cc (get_clone_attr_versions): Add filter and location argument. (get_clone_versions): Update call to get_clone_attr_versions. * tree.h (get_clone_attr_versions): Add filter and location argument. * config/aarch64/aarch64.cc (aarch64_reject_target_clone_version): New function (TARGET_REJECT_FUNCTION_CLONE_VERSION): New define. * config/riscv/riscv.cc (riscv_reject_target_clone_version): New function. (TARGET_REJECT_FUNCTION_CLONE_VERSION): New define. * doc/tm.texi: Regenerated. * doc/tm.texi.in: Add documentation for new hook. * hooks.h (hook_stringslice_locationt_false): New function. * hooks.cc (hook_stringslice_locationt_false): New function. gcc/c-family/ChangeLog: * c-attribs.cc (handle_target_clones_attribute): Update to emit warnings for rejected versions. --- gcc/c-family/c-attribs.cc | 26 +++++++++++++++++++++----- gcc/config/aarch64/aarch64.cc | 20 ++++++++++++++++++++ gcc/config/riscv/riscv.cc | 18 ++++++++++++++++++ gcc/doc/tm.texi | 5 +++++ gcc/doc/tm.texi.in | 2 ++ gcc/hooks.cc | 6 ++++++ gcc/hooks.h | 3 +++ gcc/target.def | 8 ++++++++ gcc/tree.cc | 12 ++++++++++-- gcc/tree.h | 8 ++++++-- 10 files changed, 99 insertions(+), 9 deletions(-) diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc index 5dff489fcca..b5287f0da06 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -6132,12 +6132,28 @@ handle_target_clones_attribute (tree *node, tree name, tree ARG_UNUSED (args), } } - auto_vec<string_slice> versions= get_clone_attr_versions (args, NULL); - - if (versions.length () == 1) - { + int num_defaults = 0; + auto_vec<string_slice> versions= get_clone_attr_versions (args, + &num_defaults, + DECL_SOURCE_LOCATION (*node), + false); + + for (auto v : versions) + if (targetm.reject_function_clone_version + (v, DECL_SOURCE_LOCATION (*node))) warning (OPT_Wattributes, - "single %<target_clones%> attribute is ignored"); + "invalid %<target_clones%> version %qB ignored", + &v); + + /* Lone target_clones version is always ignored for target attr semantics. + Only ignore under target_version semantics if it is a default + version. */ + if (versions.length () == 1 && (TARGET_HAS_FMV_TARGET_ATTRIBUTE + || num_defaults == 1)) + { + if (TARGET_HAS_FMV_TARGET_ATTRIBUTE) + warning (OPT_Wattributes, + "single %<target_clones%> attribute is ignored"); *no_add_attrs = true; } else diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc index 99e351fb65b..43ac50c7734 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -31229,6 +31229,23 @@ aarch64_expand_reversed_crc_using_pmull (scalar_mode crc_mode, } } +bool +aarch64_reject_target_clone_version (string_slice str, + location_t loc ATTRIBUTE_UNUSED) +{ + str = str.strip (); + + if (str == "default") + return false; + + enum aarch_parse_opt_result parse_res; + auto isa_flags = aarch64_asm_isa_flags; + parse_res = aarch64_parse_fmv_features (str, &isa_flags, NULL, NULL); + + /* Reject any version which does not parse. */ + return parse_res != AARCH_PARSE_OK; +} + /* Target-specific selftests. */ #if CHECKING_P @@ -32052,6 +32069,9 @@ aarch64_libgcc_floating_mode_supported_p #undef TARGET_OPTION_FUNCTION_VERSIONS #define TARGET_OPTION_FUNCTION_VERSIONS aarch64_common_function_versions +#undef TARGET_REJECT_FUNCTION_CLONE_VERSION +#define TARGET_REJECT_FUNCTION_CLONE_VERSION aarch64_reject_target_clone_version + #undef TARGET_COMPARE_VERSION_PRIORITY #define TARGET_COMPARE_VERSION_PRIORITY aarch64_compare_version_priority diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index ddeb321cb44..5f10cb9accf 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -13211,6 +13211,21 @@ riscv_common_function_versions (tree fn1, tree fn2) return riscv_compare_version_priority (fn1, fn2) != 0; } +bool +riscv_reject_target_clone_version (string_slice str, location_t loc) +{ + struct riscv_feature_bits mask; + int prio; + + /* Currently it is not possible to parse without emitting errors on failure + so do not reject on a failed parse, as this would then emit two + diagnostics. Instead let errors be emitted which will halt + compilation. */ + parse_features_for_version (str, loc, mask, prio); + + return false; +} + /* Implement TARGET_MANGLE_DECL_ASSEMBLER_NAME, to add function multiversioning suffixes. */ @@ -14349,6 +14364,9 @@ bool need_shadow_stack_push_pop_p () #undef TARGET_COMPARE_VERSION_PRIORITY #define TARGET_COMPARE_VERSION_PRIORITY riscv_compare_version_priority +#undef TARGET_REJECT_FUNCTION_CLONE_VERSION +#define TARGET_REJECT_FUNCTION_CLONE_VERSION riscv_reject_target_clone_version + #undef TARGET_OPTION_FUNCTION_VERSIONS #define TARGET_OPTION_FUNCTION_VERSIONS riscv_common_function_versions diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index a96700c0d38..1f0fc92858e 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -12272,6 +12272,11 @@ function version at run-time for a given set of function versions. body must be generated. @end deftypefn +@deftypefn {Target Hook} bool TARGET_REJECT_FUNCTION_CLONE_VERSION (string_slice @var{str}, location_t @var{loc}) +This hook is used to ignore versions specified in a target_clones +annotation. @var{str} is the version to be considered. +@end deftypefn + @deftypefn {Target Hook} bool TARGET_PREDICT_DOLOOP_P (class loop *@var{loop}) Return true if we can predict it is possible to use a low-overhead loop for a particular loop. The parameter @var{loop} is a pointer to the loop. diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in index eccc4d88493..5db7917e214 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -7885,6 +7885,8 @@ to by @var{ce_info}. @hook TARGET_GENERATE_VERSION_DISPATCHER_BODY +@hook TARGET_REJECT_FUNCTION_CLONE_VERSION + @hook TARGET_PREDICT_DOLOOP_P @hook TARGET_HAVE_COUNT_REG_DECR_P diff --git a/gcc/hooks.cc b/gcc/hooks.cc index 951825d4cf6..0decbf14afc 100644 --- a/gcc/hooks.cc +++ b/gcc/hooks.cc @@ -578,3 +578,9 @@ hook_optmode_mode_uhwi_none (machine_mode, unsigned HOST_WIDE_INT) { return opt_machine_mode (); } + +bool +hook_stringslice_locationt_false (string_slice, location_t) +{ + return false; +} diff --git a/gcc/hooks.h b/gcc/hooks.h index c0663bf4455..0de7c53675b 100644 --- a/gcc/hooks.h +++ b/gcc/hooks.h @@ -136,4 +136,7 @@ extern const char *hook_constcharptr_int_const_tree_const_tree_null (int, const_ extern opt_machine_mode hook_optmode_mode_uhwi_none (machine_mode, unsigned HOST_WIDE_INT); + +extern bool hook_stringslice_locationt_false (string_slice, location_t); + #endif diff --git a/gcc/target.def b/gcc/target.def index 6c7cdc8126b..206c7a6395e 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -2588,6 +2588,14 @@ version at run-time. @var{decl} is one version from a set of semantically\n\ identical versions.", tree, (void *decl), NULL) +/* Target hook is used to ignore certain versions specified in a target_clones + annoration. STR is the version to be considered. */ +DEFHOOK +(reject_function_clone_version , + "This hook is used to ignore versions specified in a target_clones\n\ +annotation. @var{str} is the version to be considered.", + bool, (string_slice str, location_t loc), hook_stringslice_locationt_false) + /* Returns a code for a target-specific builtin that implements reciprocal of a target-specific function, or NULL_TREE if not available. */ DEFHOOK diff --git a/gcc/tree.cc b/gcc/tree.cc index 77005bd0e03..705f0ae663a 100644 --- a/gcc/tree.cc +++ b/gcc/tree.cc @@ -15350,7 +15350,10 @@ get_attr_nonstring_decl (tree expr, tree *ref) ARGLIST. DEFAULT_COUNT is incremented for each default version found. */ auto_vec<string_slice> -get_clone_attr_versions (const tree arglist, int *default_count) +get_clone_attr_versions (const tree arglist, + int *default_count, + location_t loc, + bool filter) { gcc_assert (TREE_CODE (arglist) == TREE_LIST); auto_vec<string_slice> versions; @@ -15366,6 +15369,9 @@ get_clone_attr_versions (const tree arglist, int *default_count) string_slice attr = string_slice::tokenize (&str, separators); attr = attr.strip (); + if (filter && targetm.reject_function_clone_version (attr, loc)) + continue; + if (attr == "default" && default_count) (*default_count)++; versions.safe_push (attr); @@ -15384,7 +15390,9 @@ get_clone_versions (const tree decl, int *default_count) if (!attr) return auto_vec<string_slice> (); tree arglist = TREE_VALUE (attr); - return get_clone_attr_versions (arglist, default_count); + return get_clone_attr_versions (arglist, + default_count, + DECL_SOURCE_LOCATION (decl)); } /* If DECL has a target_version attribute, returns a string_slice containing the diff --git a/gcc/tree.h b/gcc/tree.h index ea9d2e119f9..76fea1253fd 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -7058,7 +7058,11 @@ extern string_slice get_target_version (const tree); a decl. Can also record the number of default versions found. */ extern auto_vec<string_slice> get_clone_versions (const tree, int * = NULL); /* Returns a vector of the version strings from a target_clones attribute - directly. */ -extern auto_vec<string_slice> get_clone_attr_versions (const tree, int *); + directly. Additionally takes a location for potential diagnostics to be + emmitted for and a bool to control whether or not the results should + be filtered with TARGET_REJECT_FUNCTION_CLONE_VERSION. */ +extern auto_vec<string_slice> get_clone_attr_versions (const tree, int *, + location_t loc, + bool = true); #endif /* GCC_TREE_H */ -- 2.34.1