<alfie.richa...@arm.com> writes:
> From: Alfie Richards <alfie.richa...@arm.com>
>
> 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));

No need for the line break here.

> +
> +      /* 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.  */

This is summarising the documentation for the hook, rather than
describing something specific to aarch64.  It might be better using
the usual boilerplate:

/* Implement TARGET_CHECK_TARGET_CLONE_VERSION.  */

so that there is only one place that needs updating if we want to
improve the documentation later.

> +
> +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;

What I meant in the previous review was that this might be better as:

  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
> @@ -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);

FWIW, the patch I posted should allow loc_p to be passed directly.

> +
> +  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.

"invalid errors" doesn't sound right.  Maybe "emit diagnostics for
invalid vversions".  Probably worth adding ", otherwise emit no
diagnostics" to the end.

OK with those changes, thanks.

Richard

> +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  */

Reply via email to