This changes the hook from being symmetric, to explicitly taking a version
string from the old decl and one from the new decl.
Additionally, it documents that we support emitting a diagnostic if the strings
do imply the same version, but are incompatible.
This is a change required for adding priority version support in aarch64.
gcc/ChangeLog:
* target.def (TARGET_OPTION_SAME_FUNCTION_VERSIONS): Update
documentation.
* tree.cc (disjoint_version_decls): Change to explicitly have old_decl
and new_decl.
* doc/tm.texi: Regenerate.
gcc/c/ChangeLog:
* c-decl.cc (pushdecl): Change argument order in call to
disjoint_version_decls.
---
gcc/c/c-decl.cc | 2 +-
gcc/doc/tm.texi | 8 +++-
gcc/target.def | 12 +++--
gcc/tree.cc | 122 ++++++++++++++++++++++++++++--------------------
4 files changed, 86 insertions(+), 58 deletions(-)
diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
index 061892ac95b..a4d218d9170 100644
--- a/gcc/c/c-decl.cc
+++ b/gcc/c/c-decl.cc
@@ -3438,7 +3438,7 @@ pushdecl (tree x)
in the set. */
cgraph_function_version_info *version = b_v;
for (; version; version = version->next)
- if (!disjoint_version_decls (version->this_node->decl, x))
+ if (!disjoint_version_decls (x, version->this_node->decl))
{
/* The decls define overlapping version, so attempt to merge
or diagnose the conflict. */
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index fd208f53844..08d006902a2 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -10997,9 +10997,13 @@ changed via the optimize attribute or pragma, see
@code{TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE}
@end deftypefn
-@deftypefn {Target Hook} bool TARGET_OPTION_SAME_FUNCTION_VERSIONS
(string_slice @var{fn1}, string_slice @var{fn2})
+@deftypefn {Target Hook} bool TARGET_OPTION_SAME_FUNCTION_VERSIONS
(string_slice @var{old_str}, string_slice @var{new_str})
This target hook returns @code{true} if the target/target-version strings
-@var{fn1} and @var{fn2} imply the same function version.
+@var{old_str} and @var{new_str} imply the same function version.
+Where @var{old_str} is a version string from an earlier declaration and
+@var{new_str} is a version string from a later declaration.
+Can also produce a diagnostic if the version strings do imply the same
+version, but are incompatible.
@end deftypefn
@deftypefn {Target Hook} bool TARGET_OPTION_FUNCTIONS_B_RESOLVABLE_FROM_A
(tree @var{decl_a}, tree @var{decl_v}, tree @var{base})
diff --git a/gcc/target.def b/gcc/target.def
index f288329ffca..f9d375403ee 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6954,13 +6954,17 @@ changed via the optimize attribute or pragma, see\n\
void, (void),
hook_void_void)
-/* This function returns true if FN1 and FN2 define the same version of a
- function. */
+/* This function returns true if OLD_STR and NEW_STR 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),
+@var{old_str} and @var{new_str} imply the same function version.\n\
+Where @var{old_str} is a version string from an earlier declaration and\n\
+@var{new_str} is a version string from a later declaration.\n\
+Can also produce a diagnostic if the version strings do imply the same\n\
+version, but are incompatible.",
+ bool, (string_slice old_str, string_slice new_str),
hook_stringslice_stringslice_unreachable)
/* Checks if we can be certain that function DECL_A could resolve DECL_B. */
diff --git a/gcc/tree.cc b/gcc/tree.cc
index 446261a8a8c..4e41335cff3 100644
--- a/gcc/tree.cc
+++ b/gcc/tree.cc
@@ -15506,25 +15506,26 @@ get_target_version (const tree decl)
.strip ();
}
-/* Returns true if FN1 and FN2 define disjoint function versions in an FMV
- function set. That is, the two declarations are completely non-overlapping.
+/* Returns true if NEW_DECL and OLD_DECL define disjoint 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. */
+ NEW_DECL and OLD_DECL should be function decls. */
bool
-disjoint_version_decls (tree fn1, tree fn2)
+disjoint_version_decls (tree new_decl, tree old_decl)
{
- if (TREE_CODE (fn1) != FUNCTION_DECL
- || TREE_CODE (fn2) != FUNCTION_DECL)
+ if (TREE_CODE (new_decl) != FUNCTION_DECL
+ || TREE_CODE (old_decl) != 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));
+ tree attr1 = lookup_attribute ("target", DECL_ATTRIBUTES (new_decl));
+ tree attr2 = lookup_attribute ("target", DECL_ATTRIBUTES (old_decl));
/* At least one function decl should have the target attribute
specified. */
@@ -15535,24 +15536,25 @@ disjoint_version_decls (tree fn1, tree fn2)
multi-versioned. */
if (attr1 == NULL_TREE || attr2 == NULL_TREE)
{
- if (DECL_FUNCTION_VERSIONED (fn1) || DECL_FUNCTION_VERSIONED (fn2))
+ if (DECL_FUNCTION_VERSIONED (new_decl)
+ || DECL_FUNCTION_VERSIONED (old_decl))
{
if (attr2 != NULL_TREE)
{
- std::swap (fn1, fn2);
+ std::swap (new_decl, old_decl);
attr1 = attr2;
}
auto_diagnostic_group d;
- error_at (DECL_SOURCE_LOCATION (fn2),
+ error_at (DECL_SOURCE_LOCATION (old_decl),
"missing %<target%> attribute for multi-versioned %qD",
- fn2);
- inform (DECL_SOURCE_LOCATION (fn1),
- "previous declaration of %qD", fn1);
+ old_decl);
+ inform (DECL_SOURCE_LOCATION (new_decl),
+ "previous declaration of %qD", new_decl);
/* Prevent diagnosing of the same error multiple times. */
- DECL_ATTRIBUTES (fn2)
+ DECL_ATTRIBUTES (old_decl)
= tree_cons (get_identifier ("target"),
copy_node (TREE_VALUE (attr1)),
- DECL_ATTRIBUTES (fn2));
+ DECL_ATTRIBUTES (old_decl));
}
return false;
}
@@ -15571,60 +15573,78 @@ disjoint_version_decls (tree fn1, tree fn2)
}
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);
+ tree tc_decl = new_decl;
+ tree other_decl = old_decl;
+ /* Swap the decls around so we only handle the case where one is
+ target_clones once. */
+ bool swapped = false;
+ if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (other_decl)))
+ {
+ swapped = true;
+ std::swap (tc_decl, other_decl);
+ }
- if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (fn1)))
+ if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (tc_decl)))
{
- auto_vec<string_slice> fn1_versions = get_clone_versions (fn1);
- /* fn1 is target_clone. */
- if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (fn2)))
+ auto_vec<string_slice> tc_decl_versions
+ = get_clone_versions (tc_decl);
+ if (lookup_attribute ("target_clones", DECL_ATTRIBUTES (other_decl)))
{
- /* Both are target_clone. */
- auto_vec<string_slice> fn2_versions = get_clone_versions (fn2);
- for (string_slice v1 : fn1_versions)
+ /* Both are target_clones. */
+ auto_vec<string_slice> other_decl_versions
+ = get_clone_versions (other_decl);
+ for (string_slice tc_v : tc_decl_versions)
{
- for (string_slice v2 : fn2_versions)
- if (targetm.target_option.same_function_versions (v1, v2))
- return false;
+ for (string_slice other_v : other_decl_versions)
+ {
+ /* Need to work out which came from the previous decl,
+ and which the newer one. */
+ if ((!swapped
+ && targetm.target_option.same_function_versions
+ (other_v, tc_v))
+ || (swapped
+ && targetm.target_option.same_function_versions
+ (tc_v, other_v)))
+ 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;
+ /* Only tc_decl is target clone, so other_decl is target_version
+ or unannotated (and therefore default). */
+ string_slice other_v = get_target_version (other_decl);
+ if (!other_v.is_valid ())
+ other_v = "default";
+ for (string_slice tc_v : tc_decl_versions)
+ {
+ if ((!swapped
+ && targetm.target_option.same_function_versions
+ (other_v, tc_v))
+ || (swapped
+ && targetm.target_option.same_function_versions
+ (tc_v, other_v)))
+ return false;
+ }
return true;
}
}
else
{
/* Both are target_version. */
- string_slice v1 = get_target_version (fn1);
- string_slice v2 = get_target_version (fn2);
+ string_slice new_v = get_target_version (new_decl);
+ string_slice old_v = get_target_version (old_decl);
- if (!v1.is_valid () && !v2.is_valid ())
+ if (!new_v.is_valid () && !old_v.is_valid ())
return false;
- if (!v1.is_valid ())
- v1 = "default";
- if (!v2.is_valid ())
- v2 = "default";
+ if (!new_v.is_valid ())
+ new_v = "default";
+ if (!old_v.is_valid ())
+ old_v = "default";
- if (targetm.target_option.same_function_versions (v1, v2))
+ if (targetm.target_option.same_function_versions (old_v, new_v))
return false;
return true;
--
2.34.1