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

Reply via email to