Ping for the front-end agnostic parts of this patch. (attribs.h/cc, hooks.h/cc, tree.h/cc, docs)
All other parts have been approved. Thanks, Alfie The 08/28/2025 09:49, alfie.richa...@arm.com wrote: > From: Alfie Richards <alfie.richa...@arm.com> > > This change refactors FMV handling in the frontend to allows greater > reasoning about versions in shared code. > > This is needed for allowing target_clones and target_versions to be used > together in a function set, as there is then two distinct concerns when > encountering two declarations that previously were conflated: > > 1. Are these two declarations completely disjoint FMV declarations > (ie. the sets of versions they define have no overlap). If so, they don't > conflict so there is no need to merge and both can be pushed. > 2. For two declarations that aren't completely dijoint, are they 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, continue to the existing merging logic > to try to merge these and diagnose if it's not possible. > If not, then diagnose the confliciting declarations. > > To do this the common_function_versions function has been renamed > disjoint_function_versions (meaning, are the version sets defined by these > two decl's completely distinct from eachother). > > A new hook called same_function_version is introduces taking two > string_slice's (each representing a single version) and determining if they > define the same version. > > A new function, called diagnose_versioned_decls is added, which checks > if two decls (with overlapping version sets) can be merged and diagnose when > they cannot be (only in terms of the attributes, the existing logic is used to > detect other mergability conflicts like redefinition). > > This only effects targets with TARGET_HAS_FMV_TARGET_ATTRIBUTE set to false. > (ie. aarch64 and riscv), the existing logic for i86 and ppc is unchanged. > This also means the same function version hook is only used for aarch64 and > riscv. > > gcc/ChangeLog: > > * attribs.h (common_function_versions): Removed. > * attribs.cc (common_function_versions): Removed. > * config/aarch64/aarch64.cc (aarch64_common_function_versions): Removed. > (aarch64_same_function_versions): New function to check if two version > strings imply the same version. > (TARGET_OPTION_FUNCTION_VERSIONS): Removed. > (TARGET_OPTION_SAME_FUNCTION_VERSIONS): New macro. > * config/i386/i386.cc (TARGET_OPTION_FUNCTION_VERSIONS): Removed. > * config/rs6000/rs6000.cc (TARGET_OPTION_FUNCTION_VERSIONS): Removed. > * config/riscv/riscv.cc (riscv_same_function_versions): New function > to check if two version strings imply the same version. > (riscv_common_function_versions): Removed. > (TARGET_OPTION_FUNCTION_VERSIONS): Removed. > (TARGET_OPTION_SAME_FUNCTION_VERSIONS): New macro. > * doc/tm.texi: Regenerated. > * target.def: Remove common_version hook and add same_function_version > hook. > * doc/tm.texi.in: Ditto. > * tree.cc (distinct_version_decls): New function. > (mergeable_version_decls): Ditto. > * tree.h (distinct_version_decls): New function. > (mergeable_version_decls): Ditto. > * hooks.h (hook_stringslice_stringslice_unreachable): New function. > * hooks.cc (hook_stringslice_stringslice_unreachable): New function. > > gcc/cp/ChangeLog: > > * class.cc (resolve_address_of_overloaded_function): Updated to use > dijoint_versions_decls instead of common_function_version hook. > * decl.cc (decls_match): Refacture to use disjoint_version_decls and > to pass through conflicting_version argument. > (maybe_version_functions): Updated to use > disjoint_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 > disjoint_version_decls instead of common_function_version hook. > --- > gcc/attribs.cc | 67 ---------- > gcc/attribs.h | 1 - > gcc/config/aarch64/aarch64.cc | 25 ++-- > gcc/config/i386/i386.cc | 3 - > gcc/config/riscv/riscv.cc | 35 ++--- > gcc/config/rs6000/rs6000.cc | 3 - > gcc/cp/class.cc | 3 +- > gcc/cp/decl.cc | 8 +- > gcc/cp/decl2.cc | 2 +- > gcc/doc/tm.texi | 9 +- > gcc/doc/tm.texi.in | 2 +- > gcc/hooks.cc | 7 + > gcc/hooks.h | 1 + > gcc/target.def | 21 ++- > gcc/tree.cc | 243 ++++++++++++++++++++++++++++++++++ > gcc/tree.h | 5 + > 16 files changed, 310 insertions(+), 125 deletions(-) > > diff --git a/gcc/attribs.cc b/gcc/attribs.cc > index c75fd1371fd..9efc327553f 100644 > --- a/gcc/attribs.cc > +++ b/gcc/attribs.cc > @@ -1086,8 +1086,6 @@ make_attribute (string_slice name, string_slice > arg_name, tree chain) > return attr; > } > > -/* Common functions used for target clone support. */ > - > /* Comparator function to be used in qsort routine to sort attribute > specification strings to "target". */ > > @@ -1177,71 +1175,6 @@ sorted_attr_string (tree arglist) > 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; > -} > - > /* 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/attribs.h b/gcc/attribs.h > index b8b6838599c..c4a4fb0e50b 100644 > --- a/gcc/attribs.h > +++ b/gcc/attribs.h > @@ -54,7 +54,6 @@ 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 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 766da917732..d89c9df3df2 100644 > --- a/gcc/config/aarch64/aarch64.cc > +++ b/gcc/config/aarch64/aarch64.cc > @@ -21161,18 +21161,23 @@ aarch64_get_function_versions_dispatcher (void > *decl) > return dispatch_decl; > } > > -/* 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. */ > +/* This function returns true if STR1 and STR2 are version strings for the > same > + function. */ > > bool > -aarch64_common_function_versions (tree fn1, tree fn2) > +aarch64_same_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 > @@ -32837,8 +32842,8 @@ aarch64_libgcc_floating_mode_supported_p > #undef TARGET_EMIT_EPILOGUE_FOR_SIBCALL > #define TARGET_EMIT_EPILOGUE_FOR_SIBCALL aarch64_expand_epilogue > > -#undef TARGET_OPTION_FUNCTION_VERSIONS > -#define TARGET_OPTION_FUNCTION_VERSIONS aarch64_common_function_versions > +#undef TARGET_OPTION_SAME_FUNCTION_VERSIONS > +#define TARGET_OPTION_SAME_FUNCTION_VERSIONS aarch64_same_function_versions > > #undef TARGET_CHECK_TARGET_CLONE_VERSION > #define TARGET_CHECK_TARGET_CLONE_VERSION aarch64_check_target_clone_version > diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc > index 1ca6c612137..273ad312a54 100644 > --- a/gcc/config/i386/i386.cc > +++ b/gcc/config/i386/i386.cc > @@ -28163,9 +28163,6 @@ ix86_libgcc_floating_mode_supported_p > #undef TARGET_OPTION_PRINT > #define TARGET_OPTION_PRINT ix86_function_specific_print > > -#undef TARGET_OPTION_FUNCTION_VERSIONS > -#define TARGET_OPTION_FUNCTION_VERSIONS common_function_versions > - > #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 e01aa37f947..e45dc0e68f5 100644 > --- a/gcc/config/riscv/riscv.cc > +++ b/gcc/config/riscv/riscv.cc > @@ -14044,6 +14044,23 @@ compare_fmv_features (const struct > riscv_feature_bits &mask1, > return 0; > } > > +/* This function returns true if V1 and V2 specify the same function > + version. */ > + > +bool > +riscv_same_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; > +} > + > /* Compare priorities of two version decls. Return: > 1: mask1 is higher priority > -1: mask2 is higher priority > @@ -14068,20 +14085,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; > -} > - > /* 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. */ > @@ -15854,8 +15857,8 @@ riscv_prefetch_offset_address_p (rtx x, machine_mode > mode) > #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 > +#undef TARGET_OPTION_SAME_FUNCTION_VERSIONS > +#define TARGET_OPTION_SAME_FUNCTION_VERSIONS riscv_same_function_versions > > #undef TARGET_MANGLE_DECL_ASSEMBLER_NAME > #define TARGET_MANGLE_DECL_ASSEMBLER_NAME riscv_mangle_decl_assembler_name > diff --git a/gcc/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc > index d00d5f27121..1049c446c40 100644 > --- a/gcc/config/rs6000/rs6000.cc > +++ b/gcc/config/rs6000/rs6000.cc > @@ -1730,9 +1730,6 @@ static const scoped_attribute_specs *const > rs6000_attribute_table[] = > #define TARGET_GET_FUNCTION_VERSIONS_DISPATCHER > \ > rs6000_get_function_versions_dispatcher > > -#undef TARGET_OPTION_FUNCTION_VERSIONS > -#define TARGET_OPTION_FUNCTION_VERSIONS common_function_versions > - > #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/cp/class.cc b/gcc/cp/class.cc > index df8d3c9321e..b56cc518a11 100644 > --- a/gcc/cp/class.cc > +++ b/gcc/cp/class.cc > @@ -9127,8 +9127,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))) > + && !disjoint_version_decls (fn, TREE_PURPOSE (match))) > break; > > if (match) > diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc > index 5dd5d767c19..616498e23ea 100644 > --- a/gcc/cp/decl.cc > +++ b/gcc/cp/decl.cc > @@ -1211,7 +1211,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)) > + && disjoint_version_decls (newdecl, olddecl)) > { > if (record_versions) > maybe_version_functions (newdecl, olddecl); > @@ -1294,7 +1294,7 @@ maybe_mark_function_versioned (tree decl) > bool > maybe_version_functions (tree newdecl, tree olddecl) > { > - if (!targetm.target_option.function_versions (newdecl, olddecl)) > + if (!disjoint_version_decls (newdecl, olddecl)) > return false; > > maybe_mark_function_versioned (olddecl); > @@ -2106,6 +2106,10 @@ duplicate_decls (tree newdecl, tree olddecl, bool > hiding, bool was_hidden) > /* Leave it to update_binding to merge or report error. */ > return NULL_TREE; > } > + /* Check if the two decls are non-mergeable versioned decls. */ > + else if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE > + && diagnose_versioned_decls (olddecl, newdecl)) > + return error_mark_node; > else > { > const char *errmsg = redeclaration_error_message (newdecl, olddecl); > diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc > index c6c9dfc7723..7e5225cf03e 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) > + && !disjoint_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 4650e805846..65e2ce8ad42 100644 > --- a/gcc/doc/tm.texi > +++ b/gcc/doc/tm.texi > @@ -10977,12 +10977,9 @@ 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 > -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 > -different target machines. > +@deftypefn {Target Hook} bool TARGET_OPTION_SAME_FUNCTION_VERSIONS > (string_slice @var{fn1}, string_slice @var{fn2}) > +This target hook returns @code{true} if the target/target-version strings > +@var{fn1} and @var{fn2} imply the same function version. > @end deftypefn > > @deftypefn {Target Hook} bool TARGET_CAN_INLINE_P (tree @var{caller}, tree > @var{callee}) > diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in > index 30a9400f0f9..57653a133a6 100644 > --- a/gcc/doc/tm.texi.in > +++ b/gcc/doc/tm.texi.in > @@ -7140,7 +7140,7 @@ with the target specific attributes. The default value > is @code{','}. > > @hook TARGET_OPTION_OVERRIDE > > -@hook TARGET_OPTION_FUNCTION_VERSIONS > +@hook TARGET_OPTION_SAME_FUNCTION_VERSIONS > > @hook TARGET_CAN_INLINE_P > > diff --git a/gcc/hooks.cc b/gcc/hooks.cc > index 865820d80b1..820c81e31e4 100644 > --- a/gcc/hooks.cc > +++ b/gcc/hooks.cc > @@ -27,6 +27,7 @@ > #include "coretypes.h" > #include "tm.h" > #include "hooks.h" > +#include "vec.h" > > /* Generic hook that does absolutely zappo. */ > void > @@ -593,3 +594,9 @@ hook_stringslice_locationtptr_true (string_slice, > location_t *) > { > return true; > } > + > +bool > +hook_stringslice_stringslice_unreachable (string_slice, string_slice) > +{ > + gcc_unreachable (); > +} > diff --git a/gcc/hooks.h b/gcc/hooks.h > index 08542d7a24e..a7021f532a5 100644 > --- a/gcc/hooks.h > +++ b/gcc/hooks.h > @@ -139,5 +139,6 @@ 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 *); > +extern bool hook_stringslice_stringslice_unreachable (string_slice, > string_slice); > > #endif > diff --git a/gcc/target.def b/gcc/target.def > index adb8edc7353..1b95525a902 100644 > --- a/gcc/target.def > +++ b/gcc/target.def > @@ -6925,19 +6925,14 @@ changed via the optimize attribute or pragma, see\n\ > void, (void), > hook_void_void) > > -/* This function returns true if DECL1 and DECL2 are versions of the same > - function. DECL1 and 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 different target machines. */ > -DEFHOOK > -(function_versions, > - "This target hook returns @code{true} if @var{DECL1} and @var{DECL2} 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) > +/* This function returns true if FN1 and FN2 define the same version of a > + function. */ > +DEFHOOK > +(same_function_versions, > + "This target hook returns @code{true} if the target/target-version > strings\n\ > +@var{fn1} and @var{fn2} imply the same function version.", > + bool, (string_slice fn1, string_slice fn2), > + hook_stringslice_stringslice_unreachable) > > /* Function to determine if one function can inline another function. */ > #undef HOOK_PREFIX > diff --git a/gcc/tree.cc b/gcc/tree.cc > index 05fd3d097e0..e3fc7f4fe58 100644 > --- a/gcc/tree.cc > +++ b/gcc/tree.cc > @@ -15486,6 +15486,249 @@ get_target_version (const tree decl) > .strip (); > } > > +/* Returns true if FN1 and FN2 define dijoint function versions in an FMV > + function set. That is, the two declarations are completely > non-overlapping. > + For target_version semantics, that means if one is a target clone and one > is > + a target version, the target_version must not be defined by the > target_clone, > + and for two target_clones, they must not define any of the same version. > + > + FN1 and FN2 should be function decls. */ > + > +bool > +disjoint_version_decls (tree fn1, tree fn2) > +{ > + 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.same_function_versions (v1, v2)) > + 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.same_function_versions (v1, v2)) > + 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.same_function_versions (v1, v2)) > + return false; > + > + return true; > + } > + } > +} > + > +/* Check if the target_version/target_clones attributes are mergeable > + for two decls, and if so returns false. > + If they aren't mergeable, diagnose this and return true. > + Only works for target_version semantics. */ > +bool > +diagnose_versioned_decls (tree old_decl, tree new_decl) > +{ > + gcc_assert (!TARGET_HAS_FMV_TARGET_ATTRIBUTE); > + > + string_slice old_target_attr = get_target_version (old_decl); > + string_slice new_target_attr = get_target_version (new_decl); > + > + tree old_target_clones_attr = lookup_attribute ("target_clones", > + DECL_ATTRIBUTES (old_decl)); > + tree new_target_clones_attr = lookup_attribute ("target_clones", > + DECL_ATTRIBUTES (new_decl)); > + > + /* If none of these are annotated, then it is mergeable. */ > + if (!old_target_attr.is_valid () > + && !old_target_attr.is_valid () > + && !old_target_clones_attr > + && !new_target_clones_attr) > + return false; > + > + /* If fn1 is unnanotated and fn2 contains default, then is mergeable. */ > + if (!old_target_attr.is_valid () > + && !old_target_clones_attr > + && is_function_default_version (new_decl)) > + return false; > + > + /* If fn2 is unnanotated and fn1 contains default, then is mergeable. */ > + if (!new_target_attr.is_valid () > + && !new_target_clones_attr > + && is_function_default_version (old_decl)) > + return false; > + > + /* In the case where both are annotated with target_clones, only mergeable > if > + the two sets of target_clones imply the same set of versions. */ > + if (old_target_clones_attr && new_target_clones_attr) > + { > + auto_vec<string_slice> fn1_versions = get_clone_versions (old_decl); > + auto_vec<string_slice> fn2_versions = get_clone_versions (new_decl); > + > + bool mergeable = true; > + > + if (fn1_versions.length () != fn2_versions.length ()) > + mergeable = false; > + > + /* Check both inclusion directions. */ > + for (auto fn1v : fn1_versions) > + { > + bool matched = false; > + for (auto fn2v : fn2_versions) > + if (targetm.target_option.same_function_versions (fn1v, fn2v)) > + matched = true; > + if (!matched) > + mergeable = false; > + } > + > + for (auto fn2v : fn2_versions) > + { > + bool matched = false; > + for (auto fn1v : fn1_versions) > + if (targetm.target_option.same_function_versions (fn1v, fn2v)) > + matched = true; > + if (!matched) > + mergeable = false; > + } > + > + if (!mergeable) > + { > + error_at (DECL_SOURCE_LOCATION (new_decl), > + "%qD conflicts with overlapping %<target_clone%> " > + "declaration", > + new_decl); > + inform (DECL_SOURCE_LOCATION (old_decl), > + "previous declaration of %qD", old_decl); > + return true; > + } > + > + return false; > + } > + > + /* If olddecl is target clones and newdecl is a target_version. > + As they are not distinct this implies newdecl redefines a version of > + olddecl. Not mergeable. */ > + if (new_target_clones_attr) > + { > + gcc_assert (old_target_attr.is_valid ()); > + > + error_at (DECL_SOURCE_LOCATION (new_decl), > + "%qD conflicts for version %qB", > + new_decl, &old_target_attr); > + inform (DECL_SOURCE_LOCATION (old_decl), > + "previous declaration of %qD", > + old_decl); > + return true; > + } > + > + if (old_target_clones_attr) > + { > + gcc_assert (new_target_attr.is_valid ()); > + > + error_at (DECL_SOURCE_LOCATION (new_decl), > + "%qD conflicts with a previous declaration for version %qB", > + new_decl, &new_target_attr); > + inform (DECL_SOURCE_LOCATION (old_decl), > + "previous declaration of %qD", > + old_decl); > + return true; > + } > + > + /* The only remaining case is two target_version annoteted decls. Must > + be mergeable as otherwise are distinct. */ > + return false; > +} > + > void > tree_cc_finalize (void) > { > diff --git a/gcc/tree.h b/gcc/tree.h > index b22dce5214a..b1d14341666 100644 > --- a/gcc/tree.h > +++ b/gcc/tree.h > @@ -7116,4 +7116,9 @@ extern auto_vec<string_slice> get_clone_versions > extern auto_vec<string_slice> get_clone_attr_versions > (const tree, int *, bool = true); > > +/* Checks if two decls define any overlapping versions. */ > +extern bool disjoint_version_decls (tree, tree); > +/* Checks if two overlapping decls are not mergeable. */ > +extern bool diagnose_versioned_decls (tree, tree); > + > #endif /* GCC_TREE_H */ > -- > 2.34.1 > -- Alfie Richards