---
gcc/c-family/c-attribs.cc | 23 +++++++++++----
gcc/config/aarch64/aarch64.cc | 53 +++++++++++++++++++++++++++++++++++
gcc/config/riscv/riscv.cc | 22 +++++++++++++++
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, 149 insertions(+), 14 deletions(-)
diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
index 1e3a94ed949..6e11f74de22 100644
--- a/gcc/c-family/c-attribs.cc
+++ b/gcc/c-family/c-attribs.cc
@@ -6282,12 +6282,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 4c4ec2d25f3..df3f5b2ea18 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -31946,6 +31946,56 @@ aarch64_expand_fp_spaceship (rtx dest, rtx op0, rtx
op1, rtx hint)
}
}
+/* Implement TARGET_CHECK_TARGET_CLONE_VERSION. */
+
+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");
+ return false;
+ 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);
+ return false;
+ 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);
+ return false;
+ 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);
+ return false;
+ }
+ gcc_unreachable ();
+}
+
/* Target-specific selftests. */
#if CHECKING_P
@@ -32803,6 +32853,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 c5574453cf8..185c66c6b2c 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -14082,6 +14082,25 @@ 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;
+
+ /* 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_p, mask, prio);
+
+ return true;
+}
+
/* Implement TARGET_MANGLE_DECL_ASSEMBLER_NAME, to add function
multiversioning
suffixes. */
@@ -15845,6 +15864,9 @@ riscv_prefetch_offset_address_p (rtx x, machine_mode mode)
#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 37642680f42..4650e805846 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 versions at
+that location. Otherwise emit no diagnostics.
+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 c3ed9a9fd7c..30a9400f0f9 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 8e491d83864..adb8edc7353 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 versions at\n\
+that location. Otherwise emit no diagnostics.\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 d351e9e253c..05fd3d097e0 100644
--- a/gcc/tree.cc
+++ b/gcc/tree.cc
@@ -15421,10 +15421,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;
@@ -15440,6 +15443,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);
@@ -15450,15 +15456,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 6d28f2dddd8..b22dce5214a 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -7105,10 +7105,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 */