]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
fmv: c++: Add check_target_clone hook for filtering target_clone versions.
authorAlfie Richards <alfie.richards@arm.com>
Mon, 24 Mar 2025 15:04:38 +0000 (15:04 +0000)
committerAlfie Richards <alfie.richards@arm.com>
Tue, 23 Sep 2025 09:47:43 +0000 (09:47 +0000)
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
gcc/config/aarch64/aarch64.cc
gcc/config/riscv/riscv.cc
gcc/doc/tm.texi
gcc/doc/tm.texi.in
gcc/hooks.cc
gcc/hooks.h
gcc/target.def
gcc/tree.cc
gcc/tree.h

index 1e3a94ed94939e8f1d9e020595bb8ad4f6b3fdee..6e11f74de2289b9917455f606ab7e2812a1f0ae3 100644 (file)
@@ -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
index c056fec293a1197c379412ea6a307a991982db61..30808a670860434cfa66c2e450701618c03c4ba1 100644 (file)
@@ -31945,6 +31945,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
@@ -32802,6 +32852,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
 
index 5c1753a6f96a555cb43330386cec893329453803..1f1b92e82fe603f4ec2836b3796e8ee8477cab22 100644 (file)
@@ -14118,6 +14118,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.  */
 
@@ -15967,6 +15986,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
 
index b80e6846dd7c9be8166f231b326ec4c767624093..d2b1e712628b9028524b104b5169701798bdc04b 100644 (file)
@@ -12290,6 +12290,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.
index c3ed9a9fd7c28179a2a9e79b1193d5a3d2dcf4ae..30a9400f0f9e5ac404f21d0dec930e3ced6c72a0 100644 (file)
@@ -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
index 76cb593103dd404a60a1f4f273725372929102c3..865820d80b1d63f485357a5fbaf1cd90c851f65e 100644 (file)
@@ -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;
+}
index e95bd11aca86ab090d5b191777e5f96b2689fa0a..08542d7a24eba63fefdf5e60772fd860f0ae2e0f 100644 (file)
@@ -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
index d4fd3b46a7d3890cf17767045389b9b65495c00d..02172c52d20b38bb0572f2260b85ac773ec77703 100644 (file)
@@ -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
index 56b2b86d8bb1f805ab6a8a9fdad98a0f9bf7eeb0..2e114d01110ba50b68ca5f6bf882b868524bca30 100644 (file)
@@ -15425,10 +15425,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;
@@ -15444,6 +15447,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);
@@ -15454,15 +15460,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
index 6d28f2dddd84909db365099cae6108e727370d3c..b22dce5214a2c73c6d88c6a741a63b5227ffe673 100644 (file)
@@ -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  */