> On 27 Mar 2025, at 16:45, Alfie Richards <alfie.richa...@arm.com> wrote:
> 
> 
> This change refactors FMV handling in the frontend to allows greater
> reasoning about versions in shared code.
> 
> This is needed for target_version semantics and allowing target_clones
> and target_versions to both be used for the declaration there are now
> two questions that need to be answered for the front end.
> 
> 1. Are these two declarations completely distinct FMV declarations
> (ie. the versions they define have no overlap). If so, they don't match.
> 2. Are these two declarations matching and therefore mergeable.
> (ie. two target_clone decls that define the same set of versions, or
> an un-annotated declaration, and a target_clones definition containing the
> default version). If so, the existing merging logic should be used to
> try to merge these and diagnose if it's not possible. If not, then this
> needs to be diagnosed.
> 
> To do this the common_function_versions function has been renamed
> distinct_function_versions (meaning, are the versions defined by these
> two functions completely distinct from eachother).
> 
> The common function version hook was changed to instead take two
> string_slice's and determine if they define the same version.
> 
> There is a new function, called mergeable_version_decls which checks
> if two decls (which define overlapping versions) can be merged.
> For example, if they are two target_clone decls which define the exact
> same set of versions.
> 
> This change also records the conflicting version so that it can be
> included in diagnostics.
> 
> gcc/ChangeLog:
> 
> * attribs.cc (attr_strcmp): Moved to target specific code.
> (sorted_attr_string): Moved to target specific code.
> (common_function_versions): New function.
> * attribs.h (sorted_attr_string): Removed.
> (common_function_versions): New function.
> * config/aarch64/aarch64.cc (aarch64_common_function_versions):
> New function.
> * config/riscv/riscv.cc (riscv_common_function_versions): New function.
> * doc/tm.texi: Regenerated.
> * target.def: Change common_function_versions hook.
> * tree.cc (distinct_version_decls): New function.
> (mergeable_version_decls): Ditto.
> * tree.h (distinct_version_decls): New function.
> (mergeable_version_decls): Ditto.
> 
> gcc/cp/ChangeLog:
> 
> * class.cc (resolve_address_of_overloaded_function): Updated to use
> distinct_version_decls instead of common_function_version hook.
> * cp-tree.h (decls_match): Updated to use
> distinct_version_decls instead of common_function_version hook.
> * decl.cc (decls_match): Refacture to use distinct_version_decls and
> to pass through conflicting_version argument.
> (maybe_version_functions): Updated to use
> distinct_version_decls instead of common_function_version hook.
> (duplicate_decls): Add logic to handle conflicting unmergable decls
> and improve diagnostics for conflicting versions.
> * decl2.cc (check_classfn): Updated to use
> distinct_version_decls instead of common_function_version hook.
> ---
> gcc/attribs.cc                |  75 ++-----------
> gcc/attribs.h                 |   3 +-
> gcc/config/aarch64/aarch64.cc |  16 ++-
> gcc/config/riscv/riscv.cc     |  32 +++---
> gcc/cp/class.cc               |   4 +-
> gcc/cp/cp-tree.h              |   2 +-
> gcc/cp/decl.cc                |  43 +++++--
> gcc/cp/decl2.cc               |   2 +-
> gcc/doc/tm.texi               |   4 +-
> gcc/target.def                |   6 +-
> gcc/tree.cc                   | 204 ++++++++++++++++++++++++++++++++++
> gcc/tree.h                    |   6 +
> 12 files changed, 293 insertions(+), 104 deletions(-)
> ---
> diff --git a/gcc/attribs.cc b/gcc/attribs.cc
> index 80833388ff2..13ddee3376b 100644
> --- a/gcc/attribs.cc
> +++ b/gcc/attribs.cc
> @@ -1086,7 +1086,14 @@ make_attribute (string_slice name, string_slice 
> arg_name, tree chain)
>   return attr;
> }
> 
> -/* Common functions used for target clone support.  */
> +/* Used for targets with target_version semantics.  */
> +
> +bool
> +common_function_versions (string_slice fn1 ATTRIBUTE_UNUSED,
> +  string_slice fn2 ATTRIBUTE_UNUSED)
> +{
> +  gcc_unreachable();
> +}
> 
> /* Comparator function to be used in qsort routine to sort attribute
>    specification strings to "target".  */
> @@ -1176,72 +1183,6 @@ sorted_attr_string (tree arglist)
>   XDELETEVEC (attr_str);
>   return ret_str;
> }
> -
> -
> -/* This function returns true if FN1 and FN2 are versions of the same 
> function,
> -   that is, the target strings of the function decls are different.  This 
> assumes
> -   that FN1 and FN2 have the same signature.  */
> -
> -bool
> -common_function_versions (tree fn1, tree fn2)
> -{
> -  tree attr1, attr2;
> -  char *target1, *target2;
> -  bool result;
> -
> -  if (TREE_CODE (fn1) != FUNCTION_DECL
> -      || TREE_CODE (fn2) != FUNCTION_DECL)
> -    return false;
> -
> -  attr1 = lookup_attribute ("target", DECL_ATTRIBUTES (fn1));
> -  attr2 = lookup_attribute ("target", DECL_ATTRIBUTES (fn2));
> -
> -  /* At least one function decl should have the target attribute specified.  
> */
> -  if (attr1 == NULL_TREE && attr2 == NULL_TREE)
> -    return false;
> -
> -  /* Diagnose missing target attribute if one of the decls is already
> -     multi-versioned.  */
> -  if (attr1 == NULL_TREE || attr2 == NULL_TREE)
> -    {
> -      if (DECL_FUNCTION_VERSIONED (fn1) || DECL_FUNCTION_VERSIONED (fn2))
> - {
> -  if (attr2 != NULL_TREE)
> -    {
> -      std::swap (fn1, fn2);
> -      attr1 = attr2;
> -    }
> -  auto_diagnostic_group d;
> -  error_at (DECL_SOURCE_LOCATION (fn2),
> -    "missing %<target%> attribute for multi-versioned %qD",
> -    fn2);
> -  inform (DECL_SOURCE_LOCATION (fn1),
> -  "previous declaration of %qD", fn1);
> -  /* Prevent diagnosing of the same error multiple times.  */
> -  DECL_ATTRIBUTES (fn2)
> -    = tree_cons (get_identifier ("target"),
> - copy_node (TREE_VALUE (attr1)),
> - DECL_ATTRIBUTES (fn2));
> - }
> -      return false;
> -    }
> -
> -  target1 = sorted_attr_string (TREE_VALUE (attr1));
> -  target2 = sorted_attr_string (TREE_VALUE (attr2));
> -
> -  /* The sorted target strings must be different for fn1 and fn2
> -     to be versions.  */
> -  if (strcmp (target1, target2) == 0)
> -    result = false;
> -  else
> -    result = true;
> -
> -  XDELETEVEC (target1);
> -  XDELETEVEC (target2);
> -
> -  return result;
> -}
> -
> bool
> reject_target_clone_version (string_slice str ATTRIBUTE_UNUSED,
>     location_t loc ATTRIBUTE_UNUSED)
> diff --git a/gcc/attribs.h b/gcc/attribs.h
> index b8b6838599c..fc343c0eab5 100644
> --- a/gcc/attribs.h
> +++ b/gcc/attribs.h
> @@ -54,7 +54,8 @@ extern struct scoped_attributes *
>   register_scoped_attributes (const scoped_attribute_specs &, bool = false);
> 
> extern char *sorted_attr_string (tree);
> -extern bool common_function_versions (tree, tree);
> +extern bool common_function_versions (string_slice, string_slice);
> +extern bool reject_target_clone_version (string_slice, location_t);
> extern tree make_dispatcher_decl (const tree);
> extern bool is_function_default_version (const tree);
> extern void handle_ignored_attributes_option (vec<char *> *);
> diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
> index 1b9d91d268a..d542941b2d9 100644
> --- a/gcc/config/aarch64/aarch64.cc
> +++ b/gcc/config/aarch64/aarch64.cc
> @@ -20792,13 +20792,19 @@ aarch64_get_function_versions_dispatcher (void 
> *decl)
>    This assumes that FN1 and FN2 have the same signature.  */
> 
> bool
> -aarch64_common_function_versions (tree fn1, tree fn2)
> +aarch64_common_function_versions (string_slice str1, string_slice str2)
> {
> -  if (TREE_CODE (fn1) != FUNCTION_DECL
> -      || TREE_CODE (fn2) != FUNCTION_DECL)
> -    return false;
> +  enum aarch_parse_opt_result parse_res;
> +  aarch64_fmv_feature_mask feature_mask1;
> +  aarch64_fmv_feature_mask feature_mask2;
> +  parse_res = aarch64_parse_fmv_features (str1, NULL,
> +  &feature_mask1, NULL);
> +  gcc_assert (parse_res == AARCH_PARSE_OK);
> +  parse_res = aarch64_parse_fmv_features (str2, NULL,
> +  &feature_mask2, NULL);
> +  gcc_assert (parse_res == AARCH_PARSE_OK);
> 
> -  return (aarch64_compare_version_priority (fn1, fn2) != 0);
> +  return feature_mask1 == feature_mask2;
> }
> 
> /* Implement TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P.  Use an opt-out
> diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
> index b7588ec297a..6bc1200f258 100644
> --- a/gcc/config/riscv/riscv.cc
> +++ b/gcc/config/riscv/riscv.cc
> @@ -13177,6 +13177,24 @@ compare_fmv_features (const struct 
> riscv_feature_bits &mask1,
>      1: mask1 is higher priority
>     -1: mask2 is higher priority
>      0: masks are equal.  */

These comments are intended for the function
riscv_compare_version_priority. Please rearrange them accordingly.

Thanks,
Yangyu Chen

> +bool
> +riscv_common_function_versions (string_slice v1, string_slice v2)
> +{
> +  struct riscv_feature_bits mask1, mask2;
> +  int prio1, prio2;
> +
> +  /* Invalid features should have already been rejected by this point so
> +     providing no location should be okay.  */
> +  parse_features_for_version (v1, UNKNOWN_LOCATION, mask1, prio1);
> +  parse_features_for_version (v2, UNKNOWN_LOCATION, mask2, prio2);
> +
> +  return compare_fmv_features (mask1, mask2, prio1, prio2) == 0;
> +}
> +
> +/* 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.  */
> +
> int
> riscv_compare_version_priority (tree decl1, tree decl2)
> {
> @@ -13197,20 +13215,6 @@ riscv_compare_version_priority (tree decl1, tree 
> decl2)
>   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;
> -}
> -
> bool
> riscv_reject_target_clone_version (string_slice str, location_t loc)
> {
> diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
> index ea19604ede2..b35fc34a291 100644
> --- a/gcc/cp/class.cc
> +++ b/gcc/cp/class.cc
> @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
> #include "gimplify.h"
> #include "intl.h"
> #include "asan.h"
> +#include "attribs.h"
> 
> /* Id for dumping the class hierarchy.  */
> int class_dump_id;
> @@ -8998,8 +8999,7 @@ resolve_address_of_overloaded_function (tree 
> target_type,
> decls_match will return false as they are different.  */
>       for (match = TREE_CHAIN (matches); match; match = TREE_CHAIN (match))
> if (!decls_match (fn, TREE_PURPOSE (match))
> -    && !targetm.target_option.function_versions
> -       (fn, TREE_PURPOSE (match)))
> +    && !distinct_version_decls (fn, TREE_PURPOSE (match)))
>           break;
> 
>       if (match)
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 51fda134403..9741313af45 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -7120,7 +7120,7 @@ extern void note_iteration_stmt_body_end (bool);
> extern void determine_local_discriminator (tree, tree = NULL_TREE);
> extern bool member_like_constrained_friend_p (tree);
> extern bool fns_correspond (tree, tree);
> -extern int decls_match (tree, tree, bool = true);
> +extern int decls_match (tree, tree, bool = true, string_slice* = NULL);
> extern bool maybe_version_functions (tree, tree);
> extern bool validate_constexpr_redeclaration (tree, tree);
> extern bool merge_default_template_args (tree, tree, bool);
> diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
> index b1b0cb4f89e..83f24b1ddb2 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -1120,7 +1120,10 @@ fns_correspond (tree newdecl, tree olddecl)
>    `const int&'.  */
> 
> int
> -decls_match (tree newdecl, tree olddecl, bool record_versions /* = true */)
> +decls_match (tree newdecl,
> +     tree olddecl,
> +     bool record_versions /* = true */,
> +     string_slice *conflicting_version)
> {
>   int types_match;
> 
> @@ -1213,7 +1216,7 @@ decls_match (tree newdecl, tree olddecl, bool 
> record_versions /* = true */)
>       if (types_match
>  && !DECL_EXTERN_C_P (newdecl)
>  && !DECL_EXTERN_C_P (olddecl)
> -  && targetm.target_option.function_versions (newdecl, olddecl))
> +  && distinct_version_decls (newdecl, olddecl, conflicting_version))
> {
>  if (record_versions)
>    maybe_version_functions (newdecl, olddecl);
> @@ -1296,7 +1299,7 @@ maybe_mark_function_versioned (tree decl)
> bool
> maybe_version_functions (tree newdecl, tree olddecl)
> {
> -  if (!targetm.target_option.function_versions (newdecl, olddecl))
> +  if (!distinct_version_decls (newdecl, olddecl))
>     return false;
> 
>   maybe_mark_function_versioned (olddecl);
> @@ -1686,11 +1689,12 @@ duplicate_decls (tree newdecl, tree olddecl, bool 
> hiding, bool was_hidden)
>   tree new_template_info;
>   location_t olddecl_loc = DECL_SOURCE_LOCATION (olddecl);
>   location_t newdecl_loc = DECL_SOURCE_LOCATION (newdecl);
> +  string_slice conflicting_version = string_slice::invalid ();
> 
>   if (newdecl == olddecl)
>     return olddecl;
> 
> -  types_match = decls_match (newdecl, olddecl);
> +  types_match = decls_match (newdecl, olddecl, true, &conflicting_version);
> 
>   /* If either the type of the new decl or the type of the old decl is an
>      error_mark_node, then that implies that we have already issued an
> @@ -2106,6 +2110,16 @@ duplicate_decls (tree newdecl, tree olddecl, bool 
> hiding, bool was_hidden)
>       /* Leave it to update_binding to merge or report error.  */
>       return NULL_TREE;
>     }
> +  else if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
> +   && !mergeable_version_decls (newdecl, olddecl))
> +    {
> +      /* newdecl defines an overlapping FMV version with olddecl but they
> + cannot be merged so are conflicting.  */
> +      gcc_assert (conflicting_version.is_valid ());
> +      error_at (newdecl_loc, "conflicting %qB versions", 
> &conflicting_version);
> +      inform (olddecl_loc, "previous definition");
> +      return error_mark_node;
> +    }
>   else
>     {
>       const char *errmsg = redeclaration_error_message (newdecl, olddecl);
> @@ -2114,10 +2128,23 @@ duplicate_decls (tree newdecl, tree olddecl, bool 
> hiding, bool was_hidden)
>  auto_diagnostic_group d;
>  error_at (newdecl_loc, errmsg, newdecl);
>  if (DECL_NAME (olddecl) != NULL_TREE)
> -    inform (olddecl_loc,
> -    (DECL_INITIAL (olddecl) && namespace_bindings_p ())
> -    ? G_("%q#D previously defined here")
> -    : G_("%q#D previously declared here"), olddecl);
> +    {
> +      /* If conflicting_version is set then this collision is between
> + two FMV annotated functions.  */
> +      if (conflicting_version.is_valid ())
> + inform (olddecl_loc,
> + (DECL_INITIAL (olddecl) && namespace_bindings_p ())
> + ? G_("%qB version of %q#D previously defined here")
> + : G_("%qB version of %q#D previously declared here"),
> + &conflicting_version,
> + olddecl);
> +      else
> + inform (olddecl_loc,
> + (DECL_INITIAL (olddecl) && namespace_bindings_p ())
> + ? G_("%q#D previously defined here")
> + : G_("%q#D previously declared here"),
> + olddecl);
> +    }
>  if (cxx_dialect >= cxx26
>      && DECL_NAME (newdecl)
>      && id_equal (DECL_NAME (newdecl), "_")
> diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
> index 21156f1dd3b..299385cb654 100644
> --- a/gcc/cp/decl2.cc
> +++ b/gcc/cp/decl2.cc
> @@ -876,7 +876,7 @@ check_classfn (tree ctype, tree function, tree 
> template_parms)
>       if (same_type_p (TREE_TYPE (TREE_TYPE (function)),
>       TREE_TYPE (TREE_TYPE (fndecl)))
>  && compparms (p1, p2)
> -  && !targetm.target_option.function_versions (function, fndecl)
> +  && !distinct_version_decls (function, fndecl)
>  && (!is_template
>      || comp_template_parms (template_parms,
>      DECL_TEMPLATE_PARMS (fndecl)))
> diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
> index 2746c2505aa..3d25213fb6b 100644
> --- a/gcc/doc/tm.texi
> +++ b/gcc/doc/tm.texi
> @@ -10960,8 +10960,8 @@ changed via the optimize attribute or pragma, see
> @code{TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE}
> @end deftypefn
> 
> -@deftypefn {Target Hook} bool TARGET_OPTION_FUNCTION_VERSIONS (tree 
> @var{decl1}, tree @var{decl2})
> -This target hook returns @code{true} if @var{DECL1} and @var{DECL2} are
> +@deftypefn {Target Hook} bool TARGET_OPTION_FUNCTION_VERSIONS (string_slice 
> @var{fn1}, string_slice @var{fn2})
> +This target hook returns @code{true} if @var{fn1} and @var{fn2} are
> versions of the same function.  @var{DECL1} and @var{DECL2} are function
> versions if and only if they have the same function signature and
> different target specific attributes, that is, they are compiled for
> diff --git a/gcc/target.def b/gcc/target.def
> index e01eb2a2413..98bcf09fa76 100644
> --- a/gcc/target.def
> +++ b/gcc/target.def
> @@ -6912,13 +6912,13 @@ changed via the optimize attribute or pragma, see\n\
>    that is, they are compiled for different target machines.  */
> DEFHOOK
> (function_versions,
> - "This target hook returns @code{true} if @var{DECL1} and @var{DECL2} are\n\
> + "This target hook returns @code{true} if @var{fn1} and @var{fn2} are\n\
> versions of the same function.  @var{DECL1} and @var{DECL2} are function\n\
> versions if and only if they have the same function signature and\n\
> different target specific attributes, that is, they are compiled for\n\
> different target machines.",
> - bool, (tree decl1, tree decl2),
> - hook_bool_tree_tree_false)
> + bool, (string_slice fn1, string_slice fn2),
> + NULL)
> 
> /* Function to determine if one function can inline another function.  */
> #undef HOOK_PREFIX
> diff --git a/gcc/tree.cc b/gcc/tree.cc
> index 932e161da14..0095d70c375 100644
> --- a/gcc/tree.cc
> +++ b/gcc/tree.cc
> @@ -15411,6 +15411,210 @@ get_target_version (const tree decl)
>   .strip ();
> }
> 
> +bool
> +distinct_version_decls (tree fn1, tree fn2, string_slice 
> *conflicting_version)
> +{
> +  if (TREE_CODE (fn1) != FUNCTION_DECL
> +      || TREE_CODE (fn2) != FUNCTION_DECL)
> +    return false;
> +
> +  if (TARGET_HAS_FMV_TARGET_ATTRIBUTE)
> +    {
> +      tree attr1 = lookup_attribute ("target", DECL_ATTRIBUTES (fn1));
> +      tree attr2 = lookup_attribute ("target", DECL_ATTRIBUTES (fn2));
> +
> +      /* At least one function decl should have the target attribute
> + specified.  */
> +      if (attr1 == NULL_TREE && attr2 == NULL_TREE)
> + return false;
> +
> +      /* Diagnose missing target attribute if one of the decls is already
> + multi-versioned.  */
> +      if (attr1 == NULL_TREE || attr2 == NULL_TREE)
> + {
> +  if (DECL_FUNCTION_VERSIONED (fn1) || DECL_FUNCTION_VERSIONED (fn2))
> +    {
> +      if (attr2 != NULL_TREE)
> + {
> +  std::swap (fn1, fn2);
> +  attr1 = attr2;
> + }
> +      auto_diagnostic_group d;
> +      error_at (DECL_SOURCE_LOCATION (fn2),
> + "missing %<target%> attribute for multi-versioned %qD",
> + fn2);
> +      inform (DECL_SOURCE_LOCATION (fn1),
> +      "previous declaration of %qD", fn1);
> +      /* Prevent diagnosing of the same error multiple times.  */
> +      DECL_ATTRIBUTES (fn2)
> + = tree_cons (get_identifier ("target"),
> +     copy_node (TREE_VALUE (attr1)),
> +     DECL_ATTRIBUTES (fn2));
> +    }
> +  return false;
> + }
> +
> +      char *target1 = sorted_attr_string (TREE_VALUE (attr1));
> +      char *target2 = sorted_attr_string (TREE_VALUE (attr2));
> +
> +      /* The sorted target strings must be different for fn1 and fn2
> + to be versions.  */
> +      bool result = strcmp (target1, target2) != 0;
> +
> +      XDELETEVEC (target1);
> +      XDELETEVEC (target2);
> +
> +      return result;
> +    }
> +  else
> +    {
> +      /* As this is symmetric, can remove the case where fn2 is target clone
> + and fn1 is target version by swapping here.  */
> +      if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (fn2)))
> + std::swap (fn1, fn2);
> +
> +      if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (fn1)))
> + {
> +  auto_vec<string_slice> fn1_versions = get_clone_versions (fn1);
> +  /* fn1 is target_clone.  */
> +  if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (fn2)))
> +    {
> +      /* Both are target_clone.  */
> +      auto_vec<string_slice> fn2_versions = get_clone_versions (fn2);
> +      for (string_slice v1 : fn1_versions)
> + {
> +  for (string_slice v2 : fn2_versions)
> +    if (targetm.target_option.function_versions (v1, v2))
> +      {
> + if (conflicting_version)
> +  *conflicting_version= v1;
> + return false;
> +      }
> + }
> +      return true;
> +    }
> +  else
> +    {
> +      string_slice v2 = get_target_version (fn2);
> +
> +      /* target and target_clones is always conflicting for target
> + semantics.  */
> +      if (TARGET_HAS_FMV_TARGET_ATTRIBUTE)
> + return false;
> +
> +      /* Only fn1 is target clone.  */
> +      if (!v2.is_valid ())
> + v2 = "default";
> +      for (string_slice v1 : fn1_versions)
> + if (targetm.target_option.function_versions (v1, v2))
> +  {
> +    if (conflicting_version)
> +      *conflicting_version= v1;
> +    return false;
> +  }
> +      return true;
> +    }
> + }
> +      else
> + {
> +  /* Both are target_version.  */
> +  string_slice v1 = get_target_version (fn1);
> +  string_slice v2 = get_target_version (fn2);
> +
> +  if (!v1.is_valid () && !v2.is_valid ())
> +    return false;
> +
> +  if (!v1.is_valid ())
> +    v1 = "default";
> +  if (!v2.is_valid ())
> +    v2 = "default";
> +
> +  if (targetm.target_option.function_versions (v1, v2))
> +    {
> +      if (conflicting_version)
> + *conflicting_version = v1;
> +      return false;
> +    }
> +
> +  return true;
> + }
> +    }
> +}
> +
> +/* check if the target_version/target_clones attributes are mergeable
> +   for two decls.  */
> +bool
> +mergeable_version_decls (tree fn1, tree fn2)
> +{
> +  gcc_assert (!TARGET_HAS_FMV_TARGET_ATTRIBUTE);
> +
> +  string_slice fn1_target_attr = get_target_version (fn1);
> +  string_slice fn2_target_attr = get_target_version (fn2);
> +
> +  tree fn1_target_clones_attr = lookup_attribute ("target_clones",
> +  DECL_ATTRIBUTES (fn1));
> +  tree fn2_target_clones_attr = lookup_attribute ("target_clones",
> +  DECL_ATTRIBUTES (fn2));
> +
> +  /* If none of these are annotated, then it is mergeable.  */
> +  if (!fn1_target_attr.is_valid ()
> +      && !fn1_target_attr.is_valid ()
> +      && !fn1_target_clones_attr
> +      && !fn2_target_clones_attr)
> +    return true;
> +
> +  /* If fn1 is unnanotated and fn2 contains default, then is mergeable.  */
> +  if (!fn1_target_attr.is_valid ()
> +      && !fn1_target_clones_attr
> +      && is_function_default_version (fn2))
> +    return true;
> +
> +  if (fn1_target_clones_attr && fn2_target_clones_attr)
> +    {
> +      auto_vec<string_slice> fn1_versions = get_clone_versions (fn1);
> +      auto_vec<string_slice> fn2_versions = get_clone_versions (fn2);
> +
> +      if (fn1_versions.length () != fn2_versions.length ())
> + return false;
> +
> +      /* Check both inclusion directions.  */
> +      for (auto fn1v : fn1_versions)
> + {
> +  bool matched = false;
> +  for (auto fn2v : fn2_versions)
> +    if (targetm.target_option.function_versions (fn1v, fn2v))
> +      matched = true;
> +  if (!matched)
> +    return false;
> + }
> +
> +      for (auto fn2v : fn2_versions)
> + {
> +  bool matched = false;
> +  for (auto fn1v : fn1_versions)
> +    if (targetm.target_option.function_versions (fn1v, fn2v))
> +      matched = true;
> +  if (!matched)
> +    return false;
> + }
> +
> +      return true;
> +    }
> +
> +  /* If olddecl is target clones but not newdecl, never mergeable.  */
> +  if (fn1_target_clones_attr || fn2_target_clones_attr)
> +    return false;
> +
> +  if (!fn1_target_attr.is_valid ())
> +    fn1_target_attr = "default";
> +  if (!fn2_target_attr.is_valid ())
> +    fn2_target_attr = "default";
> +
> +  /* Mergeable if define the same version.  */
> +  return targetm.target_option.function_versions (fn1_target_attr,
> +  fn2_target_attr);
> +}
> +
> void
> tree_cc_finalize (void)
> {
> diff --git a/gcc/tree.h b/gcc/tree.h
> index 796e858cf26..f76301ab260 100644
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -7054,4 +7054,10 @@ extern auto_vec<string_slice> get_clone_attr_versions 
> (const tree, int *,
>       location_t loc,
>       bool = true);
> 
> +/* Checks if two decls define any overlapping versions.  If they do updates
> +   the string slice with the overlapping version.  */
> +extern bool distinct_version_decls (tree, tree, string_slice * = NULL);
> +/* Checks if two overlapping decls are mergeable..  */
> +extern bool mergeable_version_decls (tree, tree);
> +
> #endif  /* GCC_TREE_H  */


Reply via email to