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.

Additionally, after refactoring the riscv logic, the location argument
to the hook would be unnecessary.

This also fixes PR 118339 where parse failures could cause ICE in Aarch64.

gcc/ChangeLog:

        PR target/118339
        * attribs.cc (reject_target_clone_version): New function.
        * 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/i386/i386.cc (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.
        * config/rs6000/rs6000.cc (TARGET_REJECT_FUNCTION_CLONE_VERSION):
        New define.
        * doc/tm.texi: Regenerated.
        * doc/tm.texi.in: Add documentation for new hook.

gcc/c-family/ChangeLog:

        * c-attribs.cc (handle_target_clones_attribute): Update to emit warnings
        for rejected versions.
---
 gcc/attribs.cc                |  7 +++++++
 gcc/c-family/c-attribs.cc     | 24 ++++++++++++++++++++----
 gcc/config/aarch64/aarch64.cc | 20 ++++++++++++++++++++
 gcc/config/i386/i386.cc       |  3 +++
 gcc/config/riscv/riscv.cc     | 18 ++++++++++++++++++
 gcc/config/rs6000/rs6000.cc   |  3 +++
 gcc/doc/tm.texi               |  5 +++++
 gcc/doc/tm.texi.in            |  2 ++
 gcc/target.def                |  8 ++++++++
 gcc/tree.cc                   | 12 ++++++++++--
 gcc/tree.h                    |  8 ++++++--
 11 files changed, 102 insertions(+), 8 deletions(-)

diff --git a/gcc/attribs.cc b/gcc/attribs.cc
index 09c4db96531..80833388ff2 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -1242,6 +1242,13 @@ common_function_versions (tree fn1, tree fn2)
   return result;
 }
 
+bool
+reject_target_clone_version (string_slice str ATTRIBUTE_UNUSED,
+			     location_t loc ATTRIBUTE_UNUSED)
+{
+  return false;
+}
+
 /* Make a dispatcher declaration for the multi-versioned function DECL.
    Calls to DECL function will be replaced with calls to the dispatcher
    by the front-end.  Return the decl created.  */
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);
+      int num_defaults = 0;
+      auto_vec<string_slice> versions= get_clone_attr_versions (args,
+				      &num_defaults,
+				      DECL_SOURCE_LOCATION (*node),
+				      false);
 
-      if (versions.length () == 1)
-	{
+      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 fe46e569afe..1b9d91d268a 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -31224,6 +31224,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
@@ -32047,6 +32064,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/i386/i386.cc b/gcc/config/i386/i386.cc
index 18127bcada8..864788e07eb 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -27149,6 +27149,9 @@ ix86_libgcc_floating_mode_supported_p
 #undef TARGET_OPTION_FUNCTION_VERSIONS
 #define TARGET_OPTION_FUNCTION_VERSIONS common_function_versions
 
+#undef TARGET_REJECT_FUNCTION_CLONE_VERSION
+#define TARGET_REJECT_FUNCTION_CLONE_VERSION reject_target_clone_version
+
 #undef TARGET_CAN_INLINE_P
 #define TARGET_CAN_INLINE_P ix86_can_inline_p
 
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index 723f8c1ebce..b7588ec297a 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.  */
 
@@ -14358,6 +14373,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/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc
index 0cf4bdfe603..17326ed68b5 100644
--- a/gcc/config/rs6000/rs6000.cc
+++ b/gcc/config/rs6000/rs6000.cc
@@ -1734,6 +1734,9 @@ static const scoped_attribute_specs *const rs6000_attribute_table[] =
 #undef TARGET_OPTION_FUNCTION_VERSIONS
 #define TARGET_OPTION_FUNCTION_VERSIONS common_function_versions
 
+#undef TARGET_REJECT_FUNCTION_CLONE_VERSION
+#define TARGET_REJECT_FUNCTION_CLONE_VERSION reject_target_clone_version
+
 #undef TARGET_HARD_REGNO_NREGS
 #define TARGET_HARD_REGNO_NREGS rs6000_hard_regno_nregs_hook
 #undef TARGET_HARD_REGNO_MODE_OK
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index a96700c0d38..2746c2505aa 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/target.def b/gcc/target.def
index 6c7cdc8126b..e01eb2a2413 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), NULL)
+
 /* 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 45b7e001ece..932e161da14 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));
 }
 
 /* Only works for target_version due to target attributes allowing multiple
diff --git a/gcc/tree.h b/gcc/tree.h
index 34ad5885310..796e858cf26 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -7047,7 +7047,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  */

Reply via email to