This patch introduces the TARGET_CHECK_TARGET_CLONE_VERSION hook which is used to determine if a target_clones version string parses.
The hook has a flag to enable emitting diagnostics. This is as specified in the Arm C Language Extension. The purpose of this is to be able to ignore invalid versions 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 check_target_clone_version hook. * tree.cc (get_clone_attr_versions): Add filter argument. (get_clone_versions): Add filter argument. * tree.h (get_clone_attr_versions): Add filter. (get_clone_versions): Add filter argument. * config/aarch64/aarch64.cc (aarch64_check_target_clone_version): New function (TARGET_CHECK_TARGET_CLONE_VERSION): New define. * config/riscv/riscv.cc (riscv_check_target_clone_version): New function. (TARGET_CHECK_TARGET_CLONE_VERSION): New define. * doc/tm.texi: Regenerated. * doc/tm.texi.in: Add documentation for new hook. * hooks.h (hook_stringslice_locationtptr_true): New function. * hooks.cc (hook_stringslice_locationtptr_true): New function. gcc/c-family/ChangeLog: * c-attribs.cc (handle_target_clones_attribute): Update to use new hook. --- gcc/c-family/c-attribs.cc | 23 +++++++++++--- gcc/config/aarch64/aarch64.cc | 57 +++++++++++++++++++++++++++++++++++ gcc/config/riscv/riscv.cc | 25 +++++++++++++++ gcc/doc/tm.texi | 9 ++++++ gcc/doc/tm.texi.in | 2 ++ gcc/hooks.cc | 8 +++++ gcc/hooks.h | 3 ++ gcc/target.def | 13 ++++++++ gcc/tree.cc | 17 ++++++++--- gcc/tree.h | 13 +++++--- 10 files changed, 156 insertions(+), 14 deletions(-) diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc index 041a00479bd..86d2b60c83a 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -6249,12 +6249,25 @@ 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, + false); + + for (auto v : versions) + targetm.check_target_clone_version + (v, &DECL_SOURCE_LOCATION (*node)); + + /* 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)) { - warning (OPT_Wattributes, - "single %<target_clones%> attribute is ignored"); + 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 78db3a436a7..5415af2a73b 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -31886,6 +31886,60 @@ aarch64_expand_fp_spaceship (rtx dest, rtx op0, rtx op1, rtx hint) } } +/* Checks if the function version string STR parses correctly. + If it is an invalid string, returns true to reject the string, otherwise + returns false. + If loc is not NULL and the string is invalid, emits a warning. */ + +bool +aarch64_check_target_clone_version (string_slice str, location_t *loc) +{ + str = str.strip (); + + if (str == "default") + return true; + + enum aarch_parse_opt_result parse_res; + auto isa_flags = aarch64_asm_isa_flags; + std::string invalid_extension; + + parse_res + = aarch64_parse_fmv_features (str, &isa_flags, NULL, &invalid_extension); + + if (loc == NULL) + return parse_res == AARCH_PARSE_OK; + + switch (parse_res) + { + case AARCH_PARSE_OK: + return true; + case AARCH_PARSE_MISSING_ARG: + warning_at (*loc, OPT_Wattributes, + "empty string not valid for a %<target_clones%> version"); + break; + case AARCH_PARSE_INVALID_FEATURE: + warning_at (*loc, OPT_Wattributes, + "invalid feature modifier %qs in version %qB for " + "%<target_clones%> attribute", + invalid_extension.c_str (), &str); + break; + case AARCH_PARSE_DUPLICATE_FEATURE: + warning_at (*loc, OPT_Wattributes, + "duplicate feature modifier %qs in version %qB for " + "%<target_clones%> attribute", + invalid_extension.c_str (), &str); + break; + case AARCH_PARSE_INVALID_ARG: + warning_at (*loc, OPT_Wattributes, + "invalid feature %qs in version %qB for " + "%<target_clones%> attribute", + invalid_extension.c_str (), &str); + break; + gcc_unreachable (); + } + return false; +} + /* Target-specific selftests. */ #if CHECKING_P @@ -32743,6 +32797,9 @@ aarch64_libgcc_floating_mode_supported_p #undef TARGET_OPTION_FUNCTION_VERSIONS #define TARGET_OPTION_FUNCTION_VERSIONS aarch64_common_function_versions +#undef TARGET_CHECK_TARGET_CLONE_VERSION +#define TARGET_CHECK_TARGET_CLONE_VERSION aarch64_check_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 6bbbe65b32f..419eec7ed16 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -19,6 +19,7 @@ You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ +#include "input.h" #define IN_TARGET_CODE 1 #define INCLUDE_STRING @@ -14065,6 +14066,27 @@ riscv_common_function_versions (tree fn1, tree fn2) return riscv_compare_version_priority (fn1, fn2) != 0; } +/* Checks if the function version specifying string STR parses correctly. + If it is an invalid string, currently emits a diagnostic at LOC. + Always returns true. */ + +bool +riscv_check_target_clone_version (string_slice str, location_t *loc_p) +{ + struct riscv_feature_bits mask; + int prio; + + location_t loc = loc_p ? *loc_p : UNKNOWN_LOCATION; + + /* 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 true; +} + /* Implement TARGET_MANGLE_DECL_ASSEMBLER_NAME, to add function multiversioning suffixes. */ @@ -15791,6 +15813,9 @@ synthesize_add (rtx operands[3]) #undef TARGET_COMPARE_VERSION_PRIORITY #define TARGET_COMPARE_VERSION_PRIORITY riscv_compare_version_priority +#undef TARGET_CHECK_TARGET_CLONE_VERSION +#define TARGET_CHECK_TARGET_CLONE_VERSION riscv_check_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 4c338c382ad..b37708d9be9 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -12289,6 +12289,15 @@ function version at run-time for a given set of function versions. body must be generated. @end deftypefn +@deftypefn {Target Hook} bool TARGET_CHECK_TARGET_CLONE_VERSION (string_slice @var{str}, location_t *@var{loc}) +This hook is used to check if a version specified in a @code{target_clones} +annotation is valid. @var{str} is the version to be considered. +If @var{loc} is not @code{NULL} then emit warnings for invalid errors at +that location. +Returns @code{true} if @var{str} is a valid version string, and @code{false} +otherwise +@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 12b8ed660a0..c2e7921192c 100644 --- a/gcc/doc/tm.texi.in +++ b/gcc/doc/tm.texi.in @@ -7897,6 +7897,8 @@ to by @var{ce_info}. @hook TARGET_GENERATE_VERSION_DISPATCHER_BODY +@hook TARGET_CHECK_TARGET_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 76cb593103d..865820d80b1 100644 --- a/gcc/hooks.cc +++ b/gcc/hooks.cc @@ -585,3 +585,11 @@ hook_optmode_mode_uhwi_none (machine_mode, unsigned HOST_WIDE_INT) { return opt_machine_mode (); } + +/* Generic hook that takes a string_slice and a locations and returns false. */ + +bool +hook_stringslice_locationtptr_true (string_slice, location_t *) +{ + return true; +} diff --git a/gcc/hooks.h b/gcc/hooks.h index e95bd11aca8..08542d7a24e 100644 --- a/gcc/hooks.h +++ b/gcc/hooks.h @@ -137,4 +137,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_locationtptr_true (string_slice, location_t *); + #endif diff --git a/gcc/target.def b/gcc/target.def index 5dd8f253ef6..c4912622f67 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -2606,6 +2606,19 @@ 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 +(check_target_clone_version , + "This hook is used to check if a version specified in a @code{target_clones}\n\ +annotation is valid. @var{str} is the version to be considered.\n\ +If @var{loc} is not @code{NULL} then emit warnings for invalid errors at\n\ +that location.\n\ +Returns @code{true} if @var{str} is a valid version string, and @code{false}\n\ +otherwise", + bool, (string_slice str, location_t *loc), + hook_stringslice_locationtptr_true) + /* 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 ad381091820..4e1d522827e 100644 --- a/gcc/tree.cc +++ b/gcc/tree.cc @@ -15398,10 +15398,13 @@ get_attr_nonstring_decl (tree expr, tree *ref) } /* Returns an auto_vec of string_slices containing the version strings from - ARGLIST. DEFAULT_COUNT is incremented for each default version found. */ + ARGLIST. DEFAULT_COUNT is incremented for each default version found. + If FILTER is true then any invalid versions strings are not included. */ auto_vec<string_slice> -get_clone_attr_versions (const tree arglist, int *default_count) +get_clone_attr_versions (const tree arglist, + int *default_count, + bool filter) { gcc_assert (TREE_CODE (arglist) == TREE_LIST); auto_vec<string_slice> versions; @@ -15417,6 +15420,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.check_target_clone_version (attr, NULL)) + continue; + if (attr == "default" && default_count) (*default_count)++; versions.safe_push (attr); @@ -15427,15 +15433,16 @@ get_clone_attr_versions (const tree arglist, int *default_count) /* Returns an auto_vec of string_slices containing the version strings from the target_clone attribute from DECL. DEFAULT_COUNT is incremented for each - default version found. */ + default version found. If FILTER is true then any invalid versions strings + are not included. */ auto_vec<string_slice> -get_clone_versions (const tree decl, int *default_count) +get_clone_versions (const tree decl, int *default_count, bool filter) { tree attr = lookup_attribute ("target_clones", DECL_ATTRIBUTES (decl)); 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, filter); } /* If DECL has a target_version attribute, returns a string_slice containing the diff --git a/gcc/tree.h b/gcc/tree.h index b67331aef74..f773ea1fef1 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -7101,10 +7101,15 @@ extern tree get_attr_nonstring_decl (tree, tree * = NULL); Returns an invalid string_slice if no attribute is present. */ extern string_slice get_target_version (const tree); /* Returns a vector of the version strings from a target_clones attribute on - a decl. Can also record the number of default versions found. */ -extern auto_vec<string_slice> get_clone_versions (const tree, int * = NULL); + a decl. Can also record the number of default versions found. + Use bool to control whether or not the results should + be filtered with TARGET_CHECK_TARGET_CLONE_VERSION. */ +extern auto_vec<string_slice> get_clone_versions + (const tree,int * = NULL, bool = true); /* 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 bool to control whether or not the results + should be filtered with TARGET_CHECK_TARGET_CLONE_VERSION. */ +extern auto_vec<string_slice> get_clone_attr_versions + (const tree, int *, bool = true); #endif /* GCC_TREE_H */ -- 2.34.1 -- Alfie Richards