TARGET_HAS_FMV_TARGET_ATTRIBUTE should be a separate patch? it seems
not related to TARGET_OPTION_FUNCTION_VERSIONS and
TARGET_COMPARE_VERSION_PRIORITY?


On Thu, Oct 24, 2024 at 3:13 PM Yangyu Chen <c...@cyyself.name> wrote:
>
> This patch implements TARGET_COMPARE_VERSION_PRIORITY and
> TARGET_OPTION_FUNCTION_VERSIONS for RISC-V.
>
> The TARGET_COMPARE_VERSION_PRIORITY is implemented to compare the
> priority of two function versions based on the rules defined in the
> RISC-V C-API Doc PR #85:
>
> https://github.com/riscv-non-isa/riscv-c-api-doc/pull/85/files#diff-79a93ca266139524b8b642e582ac20999357542001f1f4666fbb62b6fb7a5824R721
>
> If multiple versions have equal priority, we select the function with
> the most number of feature bits generated by
> riscv_minimal_hwprobe_feature_bits. When it comes to the same number of
> feature bits, we diff two versions and select the one with the least
> significant bit set. Since a feature appears earlier in the feature_bits
> might be more important to performance.
>
> The TARGET_OPTION_FUNCTION_VERSIONS is implemented to check whether the
> two function versions are the same. This Implementation reuses the code
> in TARGET_COMPARE_VERSION_PRIORITY and check it returns 0, which means
> the equal priority.
>
> Co-Developed-by: Hank Chang <hank.ch...@sifive.com>
>
> gcc/ChangeLog:
>
>         * config/riscv/riscv-target-attr.cc
>         (riscv_target_attr_parser::update_settings): never free the
>         arch string.
>         * config/riscv/riscv.cc
>         (parse_features_for_version): New function.
>         (compare_fmv_features): New function.
>         (riscv_compare_version_priority): New function.
>         (riscv_common_function_versions): New function.
>         (TARGET_COMPARE_VERSION_PRIORITY): Implement it.
>         (TARGET_OPTION_FUNCTION_VERSIONS): Implement it.
>         * config/riscv/riscv.h (TARGET_HAS_FMV_TARGET_ATTRIBUTE): Define
>         it to 0 to use "target_version" for function versioning.
> ---
>  gcc/config/riscv/riscv-target-attr.cc |   4 -
>  gcc/config/riscv/riscv.cc             | 127 ++++++++++++++++++++++++++
>  gcc/config/riscv/riscv.h              |   2 +
>  3 files changed, 129 insertions(+), 4 deletions(-)
>
> diff --git a/gcc/config/riscv/riscv-target-attr.cc 
> b/gcc/config/riscv/riscv-target-attr.cc
> index 087fbae77b0..4c85ad60b72 100644
> --- a/gcc/config/riscv/riscv-target-attr.cc
> +++ b/gcc/config/riscv/riscv-target-attr.cc
> @@ -239,10 +239,6 @@ riscv_target_attr_parser::update_settings (struct 
> gcc_options *opts) const
>      {
>        std::string local_arch = m_subset_list->to_string (true);
>        const char* local_arch_str = local_arch.c_str ();
> -      struct cl_target_option *default_opts
> -       = TREE_TARGET_OPTION (target_option_default_node);
> -      if (opts->x_riscv_arch_string != default_opts->x_riscv_arch_string)
> -       free (CONST_CAST (void *, (const void *) opts->x_riscv_arch_string));
>        opts->x_riscv_arch_string = xstrdup (local_arch_str);
>
>        riscv_set_arch_by_subset_list (m_subset_list, opts);
> diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
> index 3ac40234345..947864fc3a6 100644
> --- a/gcc/config/riscv/riscv.cc
> +++ b/gcc/config/riscv/riscv.cc
> @@ -12574,6 +12574,127 @@ riscv_c_mode_for_floating_type (enum tree_index ti)
>    return default_mode_for_floating_type (ti);
>  }
>
> +/* This parses the attribute arguments to target_version in DECL and modifies
> +   the feature mask and priority required to select those targets.  */
> +static void
> +parse_features_for_version (tree decl,
> +                           struct riscv_feature_bits &res,
> +                           int &priority)
> +{
> +  tree version_attr = lookup_attribute ("target_version",
> +                                       DECL_ATTRIBUTES (decl));
> +  if (version_attr == NULL_TREE)
> +    {
> +      res.length = 0;
> +      priority = 0;
> +      return;
> +    }
> +
> +  const char *version_string = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE
> +                                                   (version_attr)));
> +  gcc_assert (version_string != NULL);
> +  if (strcmp (version_string, "default") == 0)
> +    {
> +      res.length = 0;
> +      priority = 0;
> +      return;
> +    }
> +  struct cl_target_option cur_target;
> +  cl_target_option_save (&cur_target, &global_options,
> +                        &global_options_set);
> +  /* Always set to default option before parsing "arch=+..."  */
> +  struct cl_target_option *default_opts
> +       = TREE_TARGET_OPTION (target_option_default_node);
> +  cl_target_option_restore (&global_options, &global_options_set,
> +                           default_opts);
> +
> +  riscv_process_target_attr (version_string,
> +                            DECL_SOURCE_LOCATION (decl));
> +
> +  priority = global_options.x_riscv_fmv_priority;
> +  const char *arch_string = global_options.x_riscv_arch_string;
> +  bool parse_res
> +    = riscv_minimal_hwprobe_feature_bits (arch_string, &res,
> +                                         DECL_SOURCE_LOCATION (decl));
> +  gcc_assert (parse_res);
> +
> +  if (arch_string != default_opts->x_riscv_arch_string)
> +    free (CONST_CAST (void *, (const void *) arch_string));
> +
> +  cl_target_option_restore (&global_options, &global_options_set,
> +                           &cur_target);
> +}
> +
> +/* Compare priorities of two feature masks.  Return:
> +     1: mask1 is higher priority
> +    -1: mask2 is higher priority
> +     0: masks are equal.
> +   Since riscv_feature_bits has total 128 bits to be used as mask, when 
> counting
> +   the total 1s in the mask, the 1s in group1 needs to multiply a weight.  */
> +static int
> +compare_fmv_features (const struct riscv_feature_bits &mask1,
> +                     const struct riscv_feature_bits &mask2,
> +                     int prio1, int prio2)
> +{
> +  unsigned length1 = mask1.length, length2 = mask2.length;
> +  /* 1.  Compare length, for length == 0 means default version, which should 
> be
> +        the lowest priority).  */
> +  if (length1 != length2)
> +    return length1 > length2 ? 1 : -1;
> +  /* 2.  Compare the priority.  */
> +  if (prio1 != prio2)
> +    return prio1 > prio2 ? 1 : -1;
> +  /* 3.  Compare the total number of 1s in the mask.  */
> +  unsigned pop1 = 0, pop2 = 0;
> +  for (unsigned i = 0; i < length1; i++)
> +    {
> +      pop1 += __builtin_popcountll (mask1.features[i]);
> +      pop2 += __builtin_popcountll (mask2.features[i]);
> +    }
> +  if (pop1 != pop2)
> +    return pop1 > pop2 ? 1 : -1;
> +  /* 4.  Compare the mask bit by bit order.  */
> +  for (unsigned i = 0; i < length1; i++)
> +    {
> +      unsigned long long xor_mask = mask1.features[i] ^ mask2.features[i];
> +      if (xor_mask == 0)
> +       continue;
> +      return TEST_BIT (mask1.features[i], __builtin_ctzll (xor_mask)) ? 1 : 
> -1;
> +    }
> +  /* 5.  If all bits are equal, return 0.  */
> +  return 0;
> +}
> +
> +/* Compare priorities of two version decls.  Return:
> +     1: mask1 is higher priority
> +    -1: mask2 is higher priority
> +     0: masks are equal.  */
> +int
> +riscv_compare_version_priority (tree decl1, tree decl2)
> +{
> +  struct riscv_feature_bits mask1, mask2;
> +  int prio1, prio2;
> +
> +  parse_features_for_version (decl1, mask1, prio1);
> +  parse_features_for_version (decl2, mask2, prio2);
> +
> +  return compare_fmv_features (mask1, mask2, prio1, prio2);
> +}
> +
> +/* This function returns true if FN1 and FN2 are versions of the same 
> function,
> +   that is, the target_version attributes of the function decls are 
> different.
> +   This assumes that FN1 and FN2 have the same signature.  */
> +
> +bool
> +riscv_common_function_versions (tree fn1, tree fn2)
> +{
> +  if (TREE_CODE (fn1) != FUNCTION_DECL
> +      || TREE_CODE (fn2) != FUNCTION_DECL)
> +    return false;
> +
> +  return riscv_compare_version_priority (fn1, fn2) != 0;
> +}
> +
>  /* On riscv we have an ABI defined safe buffer.  This constant is used to
>     determining the probe offset for alloca.  */
>
> @@ -12948,6 +13069,12 @@ riscv_stack_clash_protection_alloca_probe_range 
> (void)
>  #undef TARGET_C_MODE_FOR_FLOATING_TYPE
>  #define TARGET_C_MODE_FOR_FLOATING_TYPE riscv_c_mode_for_floating_type
>
> +#undef TARGET_COMPARE_VERSION_PRIORITY
> +#define TARGET_COMPARE_VERSION_PRIORITY riscv_compare_version_priority
> +
> +#undef TARGET_OPTION_FUNCTION_VERSIONS
> +#define TARGET_OPTION_FUNCTION_VERSIONS riscv_common_function_versions
> +
>  struct gcc_target targetm = TARGET_INITIALIZER;
>
>  #include "gt-riscv.h"
> diff --git a/gcc/config/riscv/riscv.h b/gcc/config/riscv/riscv.h
> index 2ff9c1024f3..8a8b08b6b51 100644
> --- a/gcc/config/riscv/riscv.h
> +++ b/gcc/config/riscv/riscv.h
> @@ -1303,4 +1303,6 @@ extern void riscv_remove_unneeded_save_restore_calls 
> (void);
>     the target attribute.  */
>  #define TARGET_CLONES_ATTR_SEPARATOR '#'
>
> +#define TARGET_HAS_FMV_TARGET_ATTRIBUTE 0
> +
>  #endif /* ! GCC_RISCV_H */
> --
> 2.45.2
>

Reply via email to