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

Reply via email to