This patch is an overhaul of how FMV name mangling works. Previously
mangling logic was duplicated in several places across both target
specific and independent code. This patch changes this such that all
mangling is done in targetm.mangle_decl_assembler_name (including for the
dispatched symbol and dispatcher resolver).

This allows for the removing of previous hacks, such as where the default
mangled decl's assembler name was unmangled to then remangle all versions
and the resolver and dispatched symbol.

This does introduce a change though (shown in test changes) where
previously x86 for target annotated FMV sets set the function name to
the assembler name and remangled this. This was hard to reproduce without
resorting to hacks I wasn't comfortable with so the mangling is changed
to append ".ifunc" which matches clang.

This change also refactors expand_target_clone using
targetm.mangle_decl_assembler_name for mangling and get_clone_versions.

gcc/ChangeLog:

        * attribs.cc (make_dispatcher_decl): Move duplicated cgraph logic into
        this function and change to use targetm.mangle_decl_assembler_name for
        mangling.
        * config/aarch64/aarch64.cc (aarch64_parse_fmv_features): Change to
        support string_slice.
        (aarch64_process_target_version_attr): Ditto.
        (get_feature_mask_for_version): Ditto.
        (aarch64_mangle_decl_assembler_name): Add logic for mangling dispatched
        symbol and resolver.
        (get_suffixed_assembler_name): Removed.
        (make_resolver_func): Refactor to use
        aarch64_mangle_decl_assembler_name for mangling.
        (aarch64_generate_version_dispatcher_body): Remove remangling.
        (aarch64_get_function_versions_dispatcher): Refactor to remove
        duplicated cgraph logic.
        * config/i386/i386-features.cc (is_valid_asm_symbol): Moved from
        multiple_target.cc.
        (create_new_asm_name): Ditto.
        (ix86_mangle_function_version_assembler_name): Refactor to use
        clone_identifier and to mangle default.
        (ix86_mangle_decl_assembler_name): Add logic for mangling dispatched
        symbol and resolver.
        (ix86_get_function_versions_dispatcher): Remove duplicated cgraph
        logic.
        (make_resolver_func): Refactor to use ix86_mangle_decl_assembler_name
        for mangling.
        * config/riscv/riscv.cc (riscv_mangle_decl_assembler_name): Add logic
        for FMV mangling.
        (get_suffixed_assembler_name): Removed.
        (make_resolver_func): Refactor to use riscv_mangle_decl_assembler_name
        for mangling.
        (riscv_generate_version_dispatcher_body): Remove unnecessary remangling.
        (riscv_get_function_versions_dispatcher): Remove duplicated cgraph
        logic.
        * config/rs6000/rs6000.cc (rs6000_mangle_decl_assembler_name): New
        function.
        (rs6000_get_function_versions_dispatcher): Remove duplicated cgraph
        logic.
        (make_resolver_func): Refactor to use rs6000_mangle_decl_assembler_name
        for mangling.
        (is_valid_asm_symbol): Move from multiple_target.cc.
        (create_new_asm_name): Ditto.
        (rs6000_mangle_function_version_assembler_name): New function.
        * multiple_target.cc (create_dispatcher_calls): Remove mangling code.
        (get_attr_str): Removed.
        (separate_attrs): Ditto.
        (is_valid_asm_symbol): Moved to target specific.
        (create_new_asm_name): Ditto.
        (expand_target_clones): Refactor to use
        targetm.mangle_decl_assembler_name for mangling and be more general.
        * tree.cc (get_target_clone_attr_len): Removed.
        * tree.h (get_target_clone_attr_len): Removed.

gcc/cp/ChangeLog:

        * decl.cc (maybe_mark_function_versioned): Change to insert function 
version
        and therefore record assembler name.

gcc/testsuite/ChangeLog:

        * g++.target/i386/mv-symbols1.C: Update x86 FMV mangling.
        * g++.target/i386/mv-symbols3.C: Ditto.
        * g++.target/i386/mv-symbols4.C: Ditto.
        * g++.target/i386/mv-symbols5.C: Ditto.
---
 gcc/attribs.cc                              |  44 +++--
 gcc/config/aarch64/aarch64.cc               | 162 ++++++---------
 gcc/config/i386/i386-features.cc            | 108 ++++++----
 gcc/config/riscv/riscv.cc                   | 106 ++++------
 gcc/config/rs6000/rs6000.cc                 | 115 +++++++++--
 gcc/cp/decl.cc                              |   7 +
 gcc/multiple_target.cc                      | 209 ++++++--------------
 gcc/testsuite/g++.target/i386/mv-symbols1.C |  12 +-
 gcc/testsuite/g++.target/i386/mv-symbols3.C |  10 +-
 gcc/testsuite/g++.target/i386/mv-symbols4.C |  10 +-
 gcc/testsuite/g++.target/i386/mv-symbols5.C |  10 +-
 gcc/tree.cc                                 |  26 ---
 gcc/tree.h                                  |   1 -
 13 files changed, 395 insertions(+), 425 deletions(-)

diff --git a/gcc/attribs.cc b/gcc/attribs.cc
index b00d9529a8d..d0f37d77098 100644
--- a/gcc/attribs.cc
+++ b/gcc/attribs.cc
@@ -39,6 +39,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-pretty-print.h"
 #include "intl.h"
 #include "gcc-urlifier.h"
+#include "cgraph.h"
 
 /* Table of the tables of attributes (common, language, format, machine)
    searched.  */
@@ -1266,18 +1267,12 @@ common_function_versions (tree fn1, tree fn2)
 tree
 make_dispatcher_decl (const tree decl)
 {
-  tree func_decl;
-  char *func_name;
-  tree fn_type, func_type;
+  tree fn_type = TREE_TYPE (decl);
+  tree func_type = build_function_type (TREE_TYPE (fn_type),
+					TYPE_ARG_TYPES (fn_type));
+  tree func_decl = build_fn_decl (IDENTIFIER_POINTER (DECL_NAME (decl)),
+				  func_type);
 
-  func_name = xstrdup (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)));
-
-  fn_type = TREE_TYPE (decl);
-  func_type = build_function_type (TREE_TYPE (fn_type),
-				   TYPE_ARG_TYPES (fn_type));
-
-  func_decl = build_fn_decl (func_name, func_type);
-  XDELETEVEC (func_name);
   TREE_USED (func_decl) = 1;
   DECL_CONTEXT (func_decl) = NULL_TREE;
   DECL_INITIAL (func_decl) = error_mark_node;
@@ -1287,6 +1282,33 @@ make_dispatcher_decl (const tree decl)
   DECL_EXTERNAL (func_decl) = 1;
   /* This will be of type IFUNCs have to be externally visible.  */
   TREE_PUBLIC (func_decl) = 1;
+  TREE_NOTHROW (func_decl) = TREE_NOTHROW (decl);
+
+  /* Set the decl name to avoid graph_node re-mangling it.  */
+  SET_DECL_ASSEMBLER_NAME (func_decl, DECL_ASSEMBLER_NAME (decl));
+
+  cgraph_node *node = cgraph_node::get (decl);
+  gcc_assert (node);
+  cgraph_function_version_info *node_v = node->function_version ();
+  gcc_assert (node_v);
+
+  cgraph_node *func_node = cgraph_node::get_create (func_decl);
+  func_node->dispatcher_function = true;
+  func_node->definition = true;
+
+  cgraph_function_version_info *func_v
+    = func_node->insert_new_function_version ();
+  func_v->next = node_v;
+  func_v->assembler_name = node_v->assembler_name;
+
+  /* If the default node is from a target_clone, mark the dispatcher as from
+     target_clone.  */
+  func_node->is_target_clone = node->is_target_clone;
+
+  /* Get the assembler name by mangling with the base assembler name.  */
+  tree id = targetm.mangle_decl_assembler_name
+    (func_decl, func_v->assembler_name);
+  symtab->change_decl_assembler_name (func_decl, id);
 
   return func_decl;
 }
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 418f3039329..6b2247be7e7 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -19882,7 +19882,7 @@ static aarch64_fmv_feature_datum aarch64_fmv_feature_data[] = {
 #include "config/aarch64/aarch64-option-extensions.def"
 };
 
-/* Parse a function multiversioning feature string STR, as found in a
+/* Parse a function multiversioning feature string_slice STR, as found in a
    target_version or target_clones attribute.
 
    If ISA_FLAGS is nonnull, then update it with the specified architecture
@@ -19894,37 +19894,35 @@ static aarch64_fmv_feature_datum aarch64_fmv_feature_data[] = {
    the extension string is created and stored to INVALID_EXTENSION.  */
 
 static enum aarch_parse_opt_result
-aarch64_parse_fmv_features (const char *str, aarch64_feature_flags *isa_flags,
+aarch64_parse_fmv_features (string_slice str, aarch64_feature_flags *isa_flags,
 			    aarch64_fmv_feature_mask *feature_mask,
 			    std::string *invalid_extension)
 {
   if (feature_mask)
     *feature_mask = 0ULL;
 
-  if (strcmp (str, "default") == 0)
+  if (str == "default")
     return AARCH_PARSE_OK;
 
-  while (str != NULL && *str != 0)
+  string_slice str_parse = str;
+
+  gcc_assert (str.is_valid ());
+  while (str_parse.is_valid ())
     {
-      const char *ext;
-      size_t len;
+      string_slice ext;
 
-      ext = strchr (str, '+');
+      ext = string_slice::tokenize (&str_parse, string_slice ("+"));
 
-      if (ext != NULL)
-	len = ext - str;
-      else
-	len = strlen (str);
+      gcc_assert (ext.is_valid ());
 
-      if (len == 0)
+      if (!ext.is_valid () || ext.empty ())
 	return AARCH_PARSE_MISSING_ARG;
 
       int num_features = ARRAY_SIZE (aarch64_fmv_feature_data);
       int i;
       for (i = 0; i < num_features; i++)
 	{
-	  if (strlen (aarch64_fmv_feature_data[i].name) == len
-	      && strncmp (aarch64_fmv_feature_data[i].name, str, len) == 0)
+	  if (aarch64_fmv_feature_data[i].name == ext)
 	    {
 	      if (isa_flags)
 		*isa_flags |= aarch64_fmv_feature_data[i].opt_flags;
@@ -19936,7 +19934,8 @@ aarch64_parse_fmv_features (const char *str, aarch64_feature_flags *isa_flags,
 		    {
 		      /* Duplicate feature.  */
 		      if (invalid_extension)
-			*invalid_extension = std::string (str, len);
+			*invalid_extension
+			  = std::string (ext.begin (), ext.size ());
 		      return AARCH_PARSE_DUPLICATE_FEATURE;
 		    }
 		}
@@ -19947,15 +19946,11 @@ aarch64_parse_fmv_features (const char *str, aarch64_feature_flags *isa_flags,
       if (i == num_features)
 	{
 	  /* Feature not found in list.  */
+	  gcc_assert (str.is_valid ());
 	  if (invalid_extension)
-	    *invalid_extension = std::string (str, len);
+	    *invalid_extension = std::string (ext.begin (), ext.size ());
 	  return AARCH_PARSE_INVALID_FEATURE;
 	}
-
-      str = ext;
-      if (str)
-	/* Skip over the next '+'.  */
-	str++;
     }
 
   return AARCH_PARSE_OK;
@@ -19992,7 +19987,7 @@ aarch64_process_target_version_attr (tree args)
       return false;
     }
 
-  const char *str = TREE_STRING_POINTER (args);
+  string_slice str = string_slice (TREE_STRING_POINTER (args));
 
   enum aarch_parse_opt_result parse_res;
   auto isa_flags = aarch64_asm_isa_flags;
@@ -20016,13 +20011,13 @@ aarch64_process_target_version_attr (tree args)
     case AARCH_PARSE_INVALID_FEATURE:
       error ("invalid feature modifier %qs of value %qs in "
 	     "%<target_version%> attribute", invalid_extension.c_str (),
-	     str);
+	     TREE_STRING_POINTER (args));
       break;
 
     case AARCH_PARSE_DUPLICATE_FEATURE:
       error ("duplicate feature modifier %qs of value %qs in "
 	     "%<target_version%> attribute", invalid_extension.c_str (),
-	     str);
+	     TREE_STRING_POINTER (args));
       break;
 
     default:
@@ -20094,13 +20089,14 @@ get_feature_mask_for_version (tree decl)
   if (version_attr == NULL)
     return 0;
 
-  const char *version_string = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE
-						    (version_attr)));
+  string_slice version_string = string_slice
+    (TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (version_attr))));
+
   enum aarch_parse_opt_result parse_res;
   aarch64_fmv_feature_mask feature_mask;
 
-  parse_res = aarch64_parse_fmv_features (version_string, NULL, &feature_mask,
-					  NULL);
+  parse_res = aarch64_parse_fmv_features (version_string, NULL,
+					  &feature_mask, NULL);
 
   /* We should have detected any errors before getting here.  */
   gcc_assert (parse_res == AARCH_PARSE_OK);
@@ -20195,36 +20191,33 @@ tree
 aarch64_mangle_decl_assembler_name (tree decl, tree id)
 {
   /* For function version, add the target suffix to the assembler name.  */
-  if (TREE_CODE (decl) == FUNCTION_DECL
-      && DECL_FUNCTION_VERSIONED (decl))
+  if (TREE_CODE (decl) == FUNCTION_DECL)
     {
-      aarch64_fmv_feature_mask feature_mask = get_feature_mask_for_version (decl);
-
-      std::string name = IDENTIFIER_POINTER (id);
-
-      /* For the default version, append ".default".  */
-      if (feature_mask == 0ULL)
+      cgraph_node *node = cgraph_node::get (decl);
+      if (node && node->dispatcher_function)
+	return id;
+      else if (node && node->dispatcher_resolver_function)
+	return clone_identifier (id, "resolver");
+      else if (DECL_FUNCTION_VERSIONED (decl))
 	{
-	  name += ".default";
-	  return get_identifier (name.c_str());
-	}
+	  aarch64_fmv_feature_mask feature_mask
+	    = get_feature_mask_for_version (decl);
 
-      name += "._";
+	  if (feature_mask == 0ULL)
+	    return clone_identifier (id, "default");
 
-      int num_features = ARRAY_SIZE (aarch64_fmv_feature_data);
-      for (int i = 0; i < num_features; i++)
-	{
-	  if (feature_mask & aarch64_fmv_feature_data[i].feature_mask)
-	    {
-	      name += "M";
-	      name += aarch64_fmv_feature_data[i].name;
-	    }
-	}
+	  std::string suffix = "_";
 
-      if (DECL_ASSEMBLER_NAME_SET_P (decl))
-	SET_DECL_RTL (decl, NULL);
+	  int num_features = ARRAY_SIZE (aarch64_fmv_feature_data);
+	  for (int i = 0; i < num_features; i++)
+	    if (feature_mask & aarch64_fmv_feature_data[i].feature_mask)
+	      {
+		suffix += "M";
+		suffix += aarch64_fmv_feature_data[i].name;
+	      }
 
-      id = get_identifier (name.c_str());
+	  id = clone_identifier (id, suffix.c_str ());
+	}
     }
   return id;
 }
@@ -20233,18 +20226,6 @@ aarch64_mangle_decl_assembler_name (tree decl, tree id)
    This is computed by taking the default version's assembler name, and
    stripping off the ".default" suffix if it's already been appended.  */
 
-static tree
-get_suffixed_assembler_name (tree default_decl, const char *suffix)
-{
-  std::string name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (default_decl));
-
-  auto size = name.size ();
-  if (size >= 8 && name.compare (size - 8, 8, ".default") == 0)
-    name.resize (size - 8);
-  name += suffix;
-  return get_identifier (name.c_str());
-}
-
 /* Make the resolver function decl to dispatch the versions of
    a multi-versioned function,  DEFAULT_DECL.  IFUNC_ALIAS_DECL is
    ifunc alias that will point to the created resolver.  Create an
@@ -20260,8 +20241,6 @@ make_resolver_func (const tree default_decl,
 
   /* Create resolver function name based on default_decl.  We need to remove an
      existing ".default" suffix if this has already been appended.  */
-  tree decl_name = get_suffixed_assembler_name (default_decl, ".resolver");
-  const char *resolver_name = IDENTIFIER_POINTER (decl_name);
 
   /* The resolver function should have signature
      (void *) resolver (uint64_t, const __ifunc_arg_t *) */
@@ -20270,10 +20249,21 @@ make_resolver_func (const tree default_decl,
 				   build_ifunc_arg_type (),
 				   NULL_TREE);
 
-  decl = build_fn_decl (resolver_name, type);
-  SET_DECL_ASSEMBLER_NAME (decl, decl_name);
+  cgraph_node *node = cgraph_node::get (default_decl);
+  gcc_assert (node && node->function_version ());
+
+  decl = build_fn_decl (IDENTIFIER_POINTER (DECL_NAME (default_decl)), type);
+
+  /* Set the assembler name to prevent cgraph_node attempting to mangle.  */
+  SET_DECL_ASSEMBLER_NAME (decl, DECL_ASSEMBLER_NAME (default_decl));
+
+  cgraph_node *resolver_node = cgraph_node::get_create (decl);
+  resolver_node->dispatcher_resolver_function = true;
+
+  tree id = aarch64_mangle_decl_assembler_name
+    (decl, node->function_version ()->assembler_name);
+  symtab->change_decl_assembler_name (decl, id);
 
-  DECL_NAME (decl) = decl_name;
   TREE_USED (decl) = 1;
   DECL_ARTIFICIAL (decl) = 1;
   DECL_IGNORED_P (decl) = 1;
@@ -20338,7 +20328,7 @@ make_resolver_func (const tree default_decl,
   gcc_assert (ifunc_alias_decl != NULL);
   /* Mark ifunc_alias_decl as "ifunc" with resolver as resolver_name.  */
   DECL_ATTRIBUTES (ifunc_alias_decl)
-    = make_attribute ("ifunc", resolver_name,
+    = make_attribute ("ifunc", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)),
 		      DECL_ATTRIBUTES (ifunc_alias_decl));
 
   /* Create the alias for dispatch to resolver here.  */
@@ -20615,27 +20605,6 @@ aarch64_generate_version_dispatcher_body (void *node_p)
   cgraph_edge::rebuild_edges ();
   pop_cfun ();
 
-  /* Fix up symbol names.  First we need to obtain the base name, which may
-     have already been mangled.  */
-  tree base_name = get_suffixed_assembler_name (default_ver_decl, "");
-
-  /* We need to redo the version mangling on the non-default versions for the
-     target_clones case.  Redoing the mangling for the target_version case is
-     redundant but does no harm.  We need to skip the default version, because
-     expand_clones will append ".default" later; fortunately that suffix is the
-     one we want anyway.  */
-  for (versn_info = node_version_info->next->next; versn_info;
-       versn_info = versn_info->next)
-    {
-      tree version_decl = versn_info->this_node->decl;
-      tree name = aarch64_mangle_decl_assembler_name (version_decl,
-						      base_name);
-      symtab->change_decl_assembler_name (version_decl, name);
-    }
-
-  /* We also need to use the base name for the ifunc declaration.  */
-  symtab->change_decl_assembler_name (node->decl, base_name);
-
   return resolver_decl;
 }
 
@@ -20680,20 +20649,9 @@ aarch64_get_function_versions_dispatcher (void *decl)
   if (targetm.has_ifunc_p ())
     {
       struct cgraph_function_version_info *it_v = NULL;
-      struct cgraph_node *dispatcher_node = NULL;
-      struct cgraph_function_version_info *dispatcher_version_info = NULL;
 
       /* Right now, the dispatching is done via ifunc.  */
       dispatch_decl = make_dispatcher_decl (default_node->decl);
-      TREE_NOTHROW (dispatch_decl) = TREE_NOTHROW (fn);
-
-      dispatcher_node = cgraph_node::get_create (dispatch_decl);
-      gcc_assert (dispatcher_node != NULL);
-      dispatcher_node->dispatcher_function = 1;
-      dispatcher_version_info
-	= dispatcher_node->insert_new_function_version ();
-      dispatcher_version_info->next = default_version_info;
-      dispatcher_node->definition = 1;
 
       /* Set the dispatcher for all the versions.  */
       it_v = default_version_info;
diff --git a/gcc/config/i386/i386-features.cc b/gcc/config/i386/i386-features.cc
index 2eb3a21bb5d..f8eb6a95c00 100644
--- a/gcc/config/i386/i386-features.cc
+++ b/gcc/config/i386/i386-features.cc
@@ -3888,6 +3888,37 @@ dispatch_function_versions (tree dispatch_decl,
   return 0;
 }
 
+/*  Return true if symbol is valid in assembler name.  */
+
+static bool
+is_valid_asm_symbol (char c)
+{
+  if ('a' <= c && c <= 'z')
+    return true;
+  if ('A' <= c && c <= 'Z')
+    return true;
+  if ('0' <= c && c <= '9')
+    return true;
+  if (c == '_')
+    return true;
+  return false;
+}
+
+/*  Replace all not valid assembler symbols with '_'.  */
+static void
+create_new_asm_name (char *old_asm_name, char *new_asm_name)
+{
+  int i;
+  int old_name_len = strlen (old_asm_name);
+  /* Replace all not valid assembler symbols with '_'.  */
+  for (i = 0; i < old_name_len; i++)
+    if (!is_valid_asm_symbol (old_asm_name[i]))
+      new_asm_name[i] = '_';
+    else
+      new_asm_name[i] = old_asm_name[i];
+  new_asm_name[old_name_len] = '\0';
+}
+
 /* This function changes the assembler name for functions that are
    versions.  If DECL is a function version and has a "target"
    attribute, it appends the attribute string to its assembler name.  */
@@ -3896,8 +3927,7 @@ static tree
 ix86_mangle_function_version_assembler_name (tree decl, tree id)
 {
   tree version_attr;
-  const char *orig_name, *version_string;
-  char *attr_str, *assembler_name;
+  char *attr_str;
 
   if (DECL_DECLARED_INLINE_P (decl)
       && lookup_attribute ("gnu_inline",
@@ -3915,25 +3945,24 @@ ix86_mangle_function_version_assembler_name (tree decl, tree id)
   /* target attribute string cannot be NULL.  */
   gcc_assert (version_attr != NULL_TREE);
 
-  orig_name = IDENTIFIER_POINTER (id);
-  version_string
-    = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (version_attr)));
-
-  if (strcmp (version_string, "default") == 0)
+  cgraph_node *node = cgraph_node::get (decl);
+  if (!node && node->is_target_clone && is_function_default_version (decl))
     return id;
 
   attr_str = sorted_attr_string (TREE_VALUE (version_attr));
-  assembler_name = XNEWVEC (char, strlen (orig_name) + strlen (attr_str) + 2);
 
-  sprintf (assembler_name, "%s.%s", orig_name, attr_str);
+  char *suffix = XNEWVEC (char, strlen (attr_str) + 1);
+  create_new_asm_name (attr_str, suffix);
 
   /* Allow assembler name to be modified if already set.  */
   if (DECL_ASSEMBLER_NAME_SET_P (decl))
     SET_DECL_RTL (decl, NULL);
 
-  tree ret = get_identifier (assembler_name);
+  tree ret = clone_identifier (id, suffix);
+
   XDELETEVEC (attr_str);
-  XDELETEVEC (assembler_name);
+  XDELETEVEC (suffix);
+
   return ret;
 }
 
@@ -3941,9 +3970,21 @@ tree
 ix86_mangle_decl_assembler_name (tree decl, tree id)
 {
   /* For function version, add the target suffix to the assembler name.  */
-  if (TREE_CODE (decl) == FUNCTION_DECL
-      && DECL_FUNCTION_VERSIONED (decl))
-    id = ix86_mangle_function_version_assembler_name (decl, id);
+  if (TREE_CODE (decl) == FUNCTION_DECL)
+    {
+      cgraph_node *node = cgraph_node::get (decl);
+      /* Mangle all versions when annotated with target_clones, but only
+	 non-default versions when annotated with target attributes.  */
+      if (DECL_FUNCTION_VERSIONED (decl)
+	  && (node->is_target_clone
+	      || !is_function_default_version (node->decl)))
+	id = ix86_mangle_function_version_assembler_name (decl, id);
+      /* Mangle the dispatched symbol but only in the case of target clones.  */
+      else if (node && node->dispatcher_function && !node->is_target_clone)
+	id = clone_identifier (id, "ifunc");
+      else if (node && node->dispatcher_resolver_function)
+	id = clone_identifier (id, "resolver");
+    }
 #ifdef SUBTARGET_MANGLE_DECL_ASSEMBLER_NAME
   id = SUBTARGET_MANGLE_DECL_ASSEMBLER_NAME (decl, id);
 #endif
@@ -3982,6 +4023,7 @@ ix86_get_function_versions_dispatcher (void *decl)
   default_version_info = node_v;
   while (default_version_info->prev != NULL)
     default_version_info = default_version_info->prev;
+  default_node = default_version_info->this_node;
 
   /* If there is no default node, just return NULL.  */
   if (!is_function_default_version (default_node->decl))
@@ -3991,20 +4033,9 @@ ix86_get_function_versions_dispatcher (void *decl)
   if (targetm.has_ifunc_p ())
     {
       struct cgraph_function_version_info *it_v = NULL;
-      struct cgraph_node *dispatcher_node = NULL;
-      struct cgraph_function_version_info *dispatcher_version_info = NULL;
 
       /* Right now, the dispatching is done via ifunc.  */
       dispatch_decl = make_dispatcher_decl (default_node->decl);
-      TREE_NOTHROW (dispatch_decl) = TREE_NOTHROW (fn);
-
-      dispatcher_node = cgraph_node::get_create (dispatch_decl);
-      gcc_assert (dispatcher_node != NULL);
-      dispatcher_node->dispatcher_function = 1;
-      dispatcher_version_info
-	= dispatcher_node->insert_new_function_version ();
-      dispatcher_version_info->next = default_version_info;
-      dispatcher_node->definition = 1;
 
       /* Set the dispatcher for all the versions.  */
       it_v = default_version_info;
@@ -4038,17 +4069,28 @@ make_resolver_func (const tree default_decl,
 {
   tree decl, type, t;
 
-  /* Create resolver function name based on default_decl.  */
-  tree decl_name = clone_function_name (default_decl, "resolver");
-  const char *resolver_name = IDENTIFIER_POINTER (decl_name);
-
   /* The resolver function should return a (void *). */
   type = build_function_type_list (ptr_type_node, NULL_TREE);
 
-  decl = build_fn_decl (resolver_name, type);
-  SET_DECL_ASSEMBLER_NAME (decl, decl_name);
+  cgraph_node *node = cgraph_node::get (default_decl);
+  gcc_assert (node && node->function_version ());
+
+  decl = build_fn_decl (IDENTIFIER_POINTER (DECL_NAME (default_decl)), type);
+
+  /* Set the assembler name to prevent cgraph_node attempting to mangle.  */
+  SET_DECL_ASSEMBLER_NAME (decl, DECL_ASSEMBLER_NAME (default_decl));
+
+  cgraph_node *resolver_node = cgraph_node::get_create (decl);
+  resolver_node->dispatcher_resolver_function = true;
+
+  if (node->is_target_clone)
+    resolver_node->is_target_clone = true;
+
+  tree id = ix86_mangle_decl_assembler_name
+    (decl, node->function_version ()->assembler_name);
+  SET_DECL_ASSEMBLER_NAME (decl, id);
 
-  DECL_NAME (decl) = decl_name;
+  DECL_NAME (decl) = DECL_NAME (default_decl);
   TREE_USED (decl) = 1;
   DECL_ARTIFICIAL (decl) = 1;
   DECL_IGNORED_P (decl) = 1;
@@ -4095,7 +4137,7 @@ make_resolver_func (const tree default_decl,
   gcc_assert (ifunc_alias_decl != NULL);
   /* Mark ifunc_alias_decl as "ifunc" with resolver as resolver_name.  */
   DECL_ATTRIBUTES (ifunc_alias_decl)
-    = make_attribute ("ifunc", resolver_name,
+    = make_attribute ("ifunc", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)),
 		      DECL_ATTRIBUTES (ifunc_alias_decl));
 
   /* Create the alias for dispatch to resolver here.  */
diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc
index e5aa99a4965..8bccbfd6550 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -13211,32 +13211,31 @@ tree
 riscv_mangle_decl_assembler_name (tree decl, tree id)
 {
   /* For function version, add the target suffix to the assembler name.  */
-  if (TREE_CODE (decl) == FUNCTION_DECL
-      && DECL_FUNCTION_VERSIONED (decl))
+  if (TREE_CODE (decl) == FUNCTION_DECL)
     {
-      std::string name = IDENTIFIER_POINTER (id) + std::string (".");
-      tree target_attr = lookup_attribute ("target_version",
-					   DECL_ATTRIBUTES (decl));
-
-      if (target_attr == NULL_TREE)
+      cgraph_node *node = cgraph_node::get (decl);
+      if (node && node->dispatcher_resolver_function)
+	return clone_identifier (id, "resolver");
+      else if (DECL_FUNCTION_VERSIONED (decl))
 	{
-	  name += "default";
-	  return get_identifier (name.c_str ());
-	}
+	  tree target_attr
+	    = lookup_attribute ("target_version", DECL_ATTRIBUTES (decl));
 
-      const char *version_string = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE
-							(target_attr)));
+	  if (target_attr == NULL_TREE)
+	    return clone_identifier (id, "default");
 
-      /* Replace non-alphanumeric characters with underscores as the suffix.  */
-      for (const char *c = version_string; *c; c++)
-	name += ISALNUM (*c) == 0 ? '_' : *c;
+	  const char *version_string
+	    = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (target_attr)));
 
-      if (DECL_ASSEMBLER_NAME_SET_P (decl))
-	SET_DECL_RTL (decl, NULL);
+	  /* Replace non-alphanumeric characters with underscores as the suffix.
+	   */
+	  std::string suffix = "";
+	  for (const char *c = version_string; *c; c++)
+	    suffix += ISALNUM (*c) == 0 ? '_' : *c;
 
-      id = get_identifier (name.c_str ());
+	  id = clone_identifier (id, suffix.c_str ());
+	}
     }
-
   return id;
 }
 
@@ -13517,22 +13516,6 @@ dispatch_function_versions (tree dispatch_decl,
   return 0;
 }
 
-/* Return an identifier for the base assembler name of a versioned function.
-   This is computed by taking the default version's assembler name, and
-   stripping off the ".default" suffix if it's already been appended.  */
-
-static tree
-get_suffixed_assembler_name (tree default_decl, const char *suffix)
-{
-  std::string name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (default_decl));
-
-  auto size = name.size ();
-  if (size >= 8 && name.compare (size - 8, 8, ".default") == 0)
-    name.resize (size - 8);
-  name += suffix;
-  return get_identifier (name.c_str ());
-}
-
 /* Make the resolver function decl to dispatch the versions of
    a multi-versioned function,  DEFAULT_DECL.  IFUNC_ALIAS_DECL is
    ifunc alias that will point to the created resolver.  Create an
@@ -13546,10 +13529,8 @@ make_resolver_func (const tree default_decl,
 {
   tree decl, type, t;
 
-  /* Create resolver function name based on default_decl.  We need to remove an
-     existing ".default" suffix if this has already been appended.  */
-  tree decl_name = get_suffixed_assembler_name (default_decl, ".resolver");
-  const char *resolver_name = IDENTIFIER_POINTER (decl_name);
+  cgraph_node *node = cgraph_node::get (default_decl);
+  gcc_assert (node && node->function_version ());
 
   /* The resolver function should have signature
      (void *) resolver (uint64_t, void *) */
@@ -13558,10 +13539,21 @@ make_resolver_func (const tree default_decl,
 				   ptr_type_node,
 				   NULL_TREE);
 
-  decl = build_fn_decl (resolver_name, type);
-  SET_DECL_ASSEMBLER_NAME (decl, decl_name);
+  decl = build_fn_decl (IDENTIFIER_POINTER (DECL_NAME (default_decl)), type);
+
+  /* Set the assembler name to prevent cgraph_node attempting to mangle.  */
+  SET_DECL_ASSEMBLER_NAME (decl, DECL_ASSEMBLER_NAME (default_decl));
+
+  cgraph_node *resolver_node = cgraph_node::get_create (decl);
+  resolver_node->dispatcher_resolver_function = true;
+
+  if (node->is_target_clone)
+    resolver_node->is_target_clone = true;
+
+  tree id = riscv_mangle_decl_assembler_name
+    (decl, node->function_version ()->assembler_name);
+  symtab->change_decl_assembler_name (decl, id);
 
-  DECL_NAME (decl) = decl_name;
   TREE_USED (decl) = 1;
   DECL_ARTIFICIAL (decl) = 1;
   DECL_IGNORED_P (decl) = 1;
@@ -13626,7 +13618,7 @@ make_resolver_func (const tree default_decl,
   gcc_assert (ifunc_alias_decl != NULL);
   /* Mark ifunc_alias_decl as "ifunc" with resolver as resolver_name.  */
   DECL_ATTRIBUTES (ifunc_alias_decl)
-    = make_attribute ("ifunc", resolver_name,
+    = make_attribute ("ifunc", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)),
 		      DECL_ATTRIBUTES (ifunc_alias_decl));
 
   /* Create the alias for dispatch to resolver here.  */
@@ -13693,24 +13685,12 @@ riscv_generate_version_dispatcher_body (void *node_p)
 
   /* Fix up symbol names.  First we need to obtain the base name, which may
      have already been mangled.  */
-  tree base_name = get_suffixed_assembler_name (default_ver_decl, "");
 
   /* We need to redo the version mangling on the non-default versions for the
      target_clones case.  Redoing the mangling for the target_version case is
      redundant but does no harm.  We need to skip the default version, because
      expand_clones will append ".default" later; fortunately that suffix is the
      one we want anyway.  */
-  for (versn_info = node_version_info->next->next; versn_info;
-       versn_info = versn_info->next)
-    {
-      tree version_decl = versn_info->this_node->decl;
-      tree name = riscv_mangle_decl_assembler_name (version_decl,
-						    base_name);
-      symtab->change_decl_assembler_name (version_decl, name);
-    }
-
-  /* We also need to use the base name for the ifunc declaration.  */
-  symtab->change_decl_assembler_name (node->decl, base_name);
 
   return resolver_decl;
 }
@@ -13749,29 +13729,15 @@ riscv_get_function_versions_dispatcher (void *decl)
   default_node = default_version_info->this_node;
 
   /* If there is no default node, just return NULL.  */
-  struct riscv_feature_bits res;
-  int priority; /* Unused.  */
-  parse_features_for_version (default_node->decl, res, priority);
-  if (res.length != 0)
+  if (!is_function_default_version (default_node->decl))
     return NULL;
 
   if (targetm.has_ifunc_p ())
     {
       struct cgraph_function_version_info *it_v = NULL;
-      struct cgraph_node *dispatcher_node = NULL;
-      struct cgraph_function_version_info *dispatcher_version_info = NULL;
 
       /* Right now, the dispatching is done via ifunc.  */
       dispatch_decl = make_dispatcher_decl (default_node->decl);
-      TREE_NOTHROW (dispatch_decl) = TREE_NOTHROW (fn);
-
-      dispatcher_node = cgraph_node::get_create (dispatch_decl);
-      gcc_assert (dispatcher_node != NULL);
-      dispatcher_node->dispatcher_function = 1;
-      dispatcher_version_info
-	= dispatcher_node->insert_new_function_version ();
-      dispatcher_version_info->next = default_version_info;
-      dispatcher_node->definition = 1;
 
       /* Set the dispatcher for all the versions.  */
       it_v = default_version_info;
diff --git a/gcc/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc
index c59bd43c76d..1f04563acef 100644
--- a/gcc/config/rs6000/rs6000.cc
+++ b/gcc/config/rs6000/rs6000.cc
@@ -88,6 +88,7 @@
 extern tree rs6000_builtin_mask_for_load (void);
 extern tree rs6000_builtin_md_vectorized_function (tree, tree, tree);
 extern tree rs6000_builtin_reciprocal (tree);
+static tree rs6000_mangle_decl_assembler_name (tree, tree);
 
   /* Set -mabi=ieeelongdouble on some old targets.  In the future, power server
      systems will also set long double to be IEEE 128-bit.  AIX and Darwin
@@ -25333,20 +25334,9 @@ rs6000_get_function_versions_dispatcher (void *decl)
   if (targetm.has_ifunc_p ())
     {
       struct cgraph_function_version_info *it_v = NULL;
-      struct cgraph_node *dispatcher_node = NULL;
-      struct cgraph_function_version_info *dispatcher_version_info = NULL;
 
       /* Right now, the dispatching is done via ifunc.  */
       dispatch_decl = make_dispatcher_decl (default_node->decl);
-      TREE_NOTHROW (dispatch_decl) = TREE_NOTHROW (fn);
-
-      dispatcher_node = cgraph_node::get_create (dispatch_decl);
-      gcc_assert (dispatcher_node != NULL);
-      dispatcher_node->dispatcher_function = 1;
-      dispatcher_version_info
-	= dispatcher_node->insert_new_function_version ();
-      dispatcher_version_info->next = default_version_info;
-      dispatcher_node->definition = 1;
 
       /* Set the dispatcher for all the versions.  */
       it_v = default_version_info;
@@ -25379,13 +25369,24 @@ make_resolver_func (const tree default_decl,
 {
   /* Make the resolver function static.  The resolver function returns
      void *.  */
-  tree decl_name = clone_function_name (default_decl, "resolver");
-  const char *resolver_name = IDENTIFIER_POINTER (decl_name);
   tree type = build_function_type_list (ptr_type_node, NULL_TREE);
-  tree decl = build_fn_decl (resolver_name, type);
-  SET_DECL_ASSEMBLER_NAME (decl, decl_name);
+  tree decl = build_fn_decl (IDENTIFIER_POINTER (DECL_NAME (default_decl)),
+			     type);
+
+  cgraph_node *node = cgraph_node::get (default_decl);
+  gcc_assert (node && node->function_version ());
+
+  /* Set the assembler name to prevent cgraph_node attempting to mangle.  */
+  SET_DECL_ASSEMBLER_NAME (decl, DECL_ASSEMBLER_NAME (default_decl));
+
+  cgraph_node *resolver_node = cgraph_node::get_create (decl);
+  resolver_node->dispatcher_resolver_function = true;
+
+  tree id = rs6000_mangle_decl_assembler_name
+    (decl, node->function_version ()->assembler_name);
+  symtab->change_decl_assembler_name (decl, id);
 
-  DECL_NAME (decl) = decl_name;
+  DECL_NAME (decl) = DECL_NAME (default_decl);
   TREE_USED (decl) = 1;
   DECL_ARTIFICIAL (decl) = 1;
   DECL_IGNORED_P (decl) = 0;
@@ -25431,7 +25432,8 @@ make_resolver_func (const tree default_decl,
 
   /* Mark dispatch_decl as "ifunc" with resolver as resolver_name.  */
   DECL_ATTRIBUTES (dispatch_decl)
-    = make_attribute ("ifunc", resolver_name, DECL_ATTRIBUTES (dispatch_decl));
+    = make_attribute ("ifunc", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)),
+		      DECL_ATTRIBUTES (dispatch_decl));
 
   cgraph_node::create_same_body_alias (dispatch_decl, decl);
 
@@ -28471,6 +28473,77 @@ complex_divide_builtin_code (machine_mode mode)
   return (built_in_function) func;
 }
 
+static bool
+is_valid_asm_symbol (char c)
+{
+  if ('a' <= c && c <= 'z')
+    return true;
+  if ('A' <= c && c <= 'Z')
+    return true;
+  if ('0' <= c && c <= '9')
+    return true;
+  if (c == '_')
+    return true;
+  return false;
+}
+
+/*  Replace all not valid assembler symbols with '_'.  */
+static void
+create_new_asm_name (char *old_asm_name, char *new_asm_name)
+{
+  int i;
+  int old_name_len = strlen (old_asm_name);
+  /* Replace all not valid assembler symbols with '_'.  */
+  for (i = 0; i < old_name_len; i++)
+    if (!is_valid_asm_symbol (old_asm_name[i]))
+      new_asm_name[i] = '_';
+    else
+      new_asm_name[i] = old_asm_name[i];
+  new_asm_name[old_name_len] = '\0';
+}
+
+/* This function changes the assembler name for functions that are
+   versions.  If DECL is a function version and has a "target"
+   attribute, it appends the attribute string to its assembler name.  */
+
+static tree
+rs6000_mangle_function_version_assembler_name (tree decl, tree id)
+{
+  tree version_attr;
+  const char *version_string;
+  char *attr_str;
+
+  if (DECL_DECLARED_INLINE_P (decl)
+      && lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (decl)))
+    error_at (DECL_SOURCE_LOCATION (decl),
+	      "function versions cannot be marked as %<gnu_inline%>,"
+	      " bodies have to be generated");
+
+  if (DECL_VIRTUAL_P (decl) || DECL_VINDEX (decl))
+    sorry ("virtual function multiversioning not supported");
+
+  version_attr = lookup_attribute ("target", DECL_ATTRIBUTES (decl));
+
+  /* target attribute string cannot be NULL.  */
+  gcc_assert (version_attr != NULL_TREE);
+
+  version_string = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (version_attr)));
+
+  if (strcmp (version_string, "default") == 0)
+    return clone_identifier (id, "default");
+
+  attr_str = sorted_attr_string (TREE_VALUE (version_attr));
+
+  char *suffix = XNEWVEC (char, strlen (attr_str) + 1);
+  create_new_asm_name (attr_str, suffix);
+
+  tree ret = clone_identifier (id, suffix);
+
+  XDELETEVEC (attr_str);
+  XDELETEVEC (suffix);
+  return ret;
+}
+
 /* On 64-bit Linux and Freebsd systems, possibly switch the long double library
    function names from <foo>l to <foo>f128 if the default long double type is
    IEEE 128-bit.  Typically, with the C and C++ languages, the standard math.h
@@ -28656,6 +28729,14 @@ rs6000_mangle_decl_assembler_name (tree decl, tree id)
 	}
     }
 
+  if (TREE_CODE (decl) == FUNCTION_DECL)
+    {
+      cgraph_node *node = cgraph_node::get (decl);
+      if (node && node->dispatcher_resolver_function)
+	id = clone_identifier (id, "resolver");
+      else if (DECL_FUNCTION_VERSIONED (decl))
+	id = rs6000_mangle_function_version_assembler_name (decl, id);
+    }
   return id;
 }
 
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 45c8b304fc0..4edf5435c35 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -1274,6 +1274,13 @@ maybe_mark_function_versioned (tree decl)
 {
   if (!DECL_FUNCTION_VERSIONED (decl))
     {
+      /* We need to insert function version now to make sure the correct
+	 pre-mangled assembler name is recorded.  */
+      cgraph_node *node = cgraph_node::get_create (decl);
+
+      if (!node->function_version ())
+	node->insert_new_function_version ();
+
       DECL_FUNCTION_VERSIONED (decl) = 1;
       /* If DECL_ASSEMBLER_NAME has already been set, re-mangle
 	 to include the version marker.  */
diff --git a/gcc/multiple_target.cc b/gcc/multiple_target.cc
index d8becf4d9a9..cd7570a9aad 100644
--- a/gcc/multiple_target.cc
+++ b/gcc/multiple_target.cc
@@ -162,9 +162,6 @@ create_dispatcher_calls (struct cgraph_node *node)
 	}
     }
 
-  tree fname = clone_function_name (node->decl, "default");
-  symtab->change_decl_assembler_name (node->decl, fname);
-
   if (node->definition)
     {
       /* FIXME: copy of cgraph_node::make_local that should be cleaned up
@@ -180,100 +177,6 @@ create_dispatcher_calls (struct cgraph_node *node)
     }
 }
 
-/* Create string with attributes separated by TARGET_CLONES_ATTR_SEPARATOR.
-   Return number of attributes.  */
-
-static int
-get_attr_str (tree arglist, char *attr_str)
-{
-  tree arg;
-  size_t str_len_sum = 0;
-  int argnum = 0;
-
-  for (arg = arglist; arg; arg = TREE_CHAIN (arg))
-    {
-      const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
-      size_t len = strlen (str);
-      for (const char *p = strchr (str, TARGET_CLONES_ATTR_SEPARATOR);
-	   p;
-	   p = strchr (p + 1, TARGET_CLONES_ATTR_SEPARATOR))
-	argnum++;
-      memcpy (attr_str + str_len_sum, str, len);
-      attr_str[str_len_sum + len]
-	= TREE_CHAIN (arg) ? TARGET_CLONES_ATTR_SEPARATOR : '\0';
-      str_len_sum += len + 1;
-      argnum++;
-    }
-  return argnum;
-}
-
-/* Return number of attributes separated by TARGET_CLONES_ATTR_SEPARATOR
-   and put them into ARGS.
-   If there is no DEFAULT attribute return -1.
-   If there is an empty string in attribute return -2.
-   If there are multiple DEFAULT attributes return -3.
-   */
-
-static int
-separate_attrs (char *attr_str, char **attrs, int attrnum)
-{
-  int i = 0;
-  int default_count = 0;
-  static const char separator_str[] = { TARGET_CLONES_ATTR_SEPARATOR, 0 };
-
-  for (char *attr = strtok (attr_str, separator_str);
-       attr != NULL; attr = strtok (NULL, separator_str))
-    {
-      if (strcmp (attr, "default") == 0)
-	{
-	  default_count++;
-	  continue;
-	}
-      attrs[i++] = attr;
-    }
-  if (default_count == 0)
-    return -1;
-  else if (default_count > 1)
-    return -3;
-  else if (i + default_count < attrnum)
-    return -2;
-
-  return i;
-}
-
-/*  Return true if symbol is valid in assembler name.  */
-
-static bool
-is_valid_asm_symbol (char c)
-{
-  if ('a' <= c && c <= 'z')
-    return true;
-  if ('A' <= c && c <= 'Z')
-    return true;
-  if ('0' <= c && c <= '9')
-    return true;
-  if (c == '_')
-    return true;
-  return false;
-}
-
-/*  Replace all not valid assembler symbols with '_'.  */
-
-static void
-create_new_asm_name (char *old_asm_name, char *new_asm_name)
-{
-  int i;
-  int old_name_len = strlen (old_asm_name);
-
-  /* Replace all not valid assembler symbols with '_'.  */
-  for (i = 0; i < old_name_len; i++)
-    if (!is_valid_asm_symbol (old_asm_name[i]))
-      new_asm_name[i] = '_';
-    else
-      new_asm_name[i] = old_asm_name[i];
-  new_asm_name[old_name_len] = '\0';
-}
-
 /*  Creates target clone of NODE.  */
 
 static cgraph_node *
@@ -309,7 +212,6 @@ create_target_clone (cgraph_node *node, bool definition, char *name,
 static bool
 expand_target_clones (struct cgraph_node *node, bool definition)
 {
-  int i;
   /* Parsing target attributes separated by TARGET_CLONES_ATTR_SEPARATOR.  */
   tree attr_target = lookup_attribute ("target_clones",
 				       DECL_ATTRIBUTES (node->decl));
@@ -317,11 +219,11 @@ expand_target_clones (struct cgraph_node *node, bool definition)
   if (!attr_target)
     return false;
 
-  tree arglist = TREE_VALUE (attr_target);
-  int attr_len = get_target_clone_attr_len (arglist);
+  int num_def = 0;
+  auto_vec<string_slice> attr_list = get_clone_versions (node->decl, &num_def);
 
   /* No need to clone for 1 target attribute.  */
-  if (attr_len == -1)
+  if (attr_list.length () == 1)
     {
       warning_at (DECL_SOURCE_LOCATION (node->decl),
 		  0, "single %<target_clones%> attribute is ignored");
@@ -348,67 +250,71 @@ expand_target_clones (struct cgraph_node *node, bool definition)
       return false;
     }
 
-  char *attr_str = XNEWVEC (char, attr_len);
-  int attrnum = get_attr_str (arglist, attr_str);
-  char **attrs = XNEWVEC (char *, attrnum);
-
-  attrnum = separate_attrs (attr_str, attrs, attrnum);
-  switch (attrnum)
+  /* Disallow multiple defaults.  */
+  if (num_def > 1)
     {
-    case -1:
-      error_at (DECL_SOURCE_LOCATION (node->decl),
-		"%<default%> target was not set");
-      break;
-    case -2:
-      error_at (DECL_SOURCE_LOCATION (node->decl),
-		"an empty string cannot be in %<target_clones%> attribute");
-      break;
-    case -3:
       error_at (DECL_SOURCE_LOCATION (node->decl),
 		"multiple %<default%> targets were set");
-      break;
-    default:
-      break;
+      return false;
     }
-
-  if (attrnum < 0)
+  /* Disallow target clones with no defaults.  */
+  if (num_def == 0)
     {
-      XDELETEVEC (attrs);
-      XDELETEVEC (attr_str);
+      error_at (DECL_SOURCE_LOCATION (node->decl),
+		"%<default%> target was not set");
       return false;
     }
 
-  const char *new_attr_name = (TARGET_HAS_FMV_TARGET_ATTRIBUTE
-			       ? "target" : "target_version");
+  /* Disallow any empty values in the clone attr.  */
+  for (string_slice attr : attr_list)
+    if (attr == string_slice ("") || !attr.is_valid ())
+      {
+	error_at (DECL_SOURCE_LOCATION (node->decl),
+		  "an empty string cannot be in %<target_clones%> attribute");
+	return false;
+      }
+
+  string_slice new_attr_name = string_slice
+    (TARGET_HAS_FMV_TARGET_ATTRIBUTE ? "target" : "target_version");
   cgraph_function_version_info *decl1_v = NULL;
   cgraph_function_version_info *decl2_v = NULL;
   cgraph_function_version_info *before = NULL;
   cgraph_function_version_info *after = NULL;
   decl1_v = node->function_version ();
-  if (decl1_v == NULL)
+  if (!decl1_v)
     decl1_v = node->insert_new_function_version ();
-  before = decl1_v;
+
+  node->is_target_clone = true;
   DECL_FUNCTION_VERSIONED (node->decl) = 1;
 
-  for (i = 0; i < attrnum; i++)
+  before = decl1_v;
+
+  /* The existing decl is turned into one of the target versions.
+     If there is a default in the list then this decl is used for that version
+     as the calls are already to it so in the case where there is no
+     implementation and !TARGET_HAS_FMV_TARGET_ATTRIBUTE there is no redirection
+     necessary.
+     Otherwise, the first version listed in the attribute is used.  */
+  string_slice this_node_version
+    = num_def ? string_slice ("default") : attr_list.pop ();
+
+  for (string_slice attr : attr_list)
     {
-      char *attr = attrs[i];
+      /* Skip default nodes.  */
+      if (attr == string_slice ("default"))
+	continue;
 
       /* Create new target clone.  */
       tree attributes = make_attribute (new_attr_name, attr,
 					DECL_ATTRIBUTES (node->decl));
 
-      char *suffix = XNEWVEC (char, strlen (attr) + 1);
-      create_new_asm_name (attr, suffix);
-      cgraph_node *new_node = create_target_clone (node, definition, suffix,
-						   attributes);
-      XDELETEVEC (suffix);
+      /* Remove the target_clones attribute, as this can confuse
+	 is_function_default_version.  */
+      remove_attribute ("target_clones", attributes);
+      cgraph_node *new_node
+	= create_target_clone (node, definition, NULL, attributes);
       if (new_node == NULL)
-	{
-	  XDELETEVEC (attrs);
-	  XDELETEVEC (attr_str);
-	  return false;
-	}
+	return false;
       new_node->local = false;
 
       decl2_v = new_node->function_version ();
@@ -426,17 +332,32 @@ expand_target_clones (struct cgraph_node *node, bool definition)
 
       before->next = after;
       after->prev = before;
-      DECL_FUNCTION_VERSIONED (new_node->decl) = 1;
-    }
+      DECL_FUNCTION_VERSIONED (new_node->decl) = true;
+
+      /* Use the base nodes assembler name for all created nodes.  */
+      new_node->function_version ()->assembler_name
+	= node->function_version ()->assembler_name;
+      new_node->is_target_clone = true;
 
-  XDELETEVEC (attrs);
-  XDELETEVEC (attr_str);
+      /* Mangle all new nodes.  */
+      tree id = targetm.mangle_decl_assembler_name
+	(new_node->decl, new_node->function_version ()->assembler_name);
+      symtab->change_decl_assembler_name (new_node->decl, id);
+    }
 
   /* Setting new attribute to initial function.  */
-  tree attributes = make_attribute (new_attr_name, "default",
+  tree attributes = make_attribute (new_attr_name, this_node_version,
 				    DECL_ATTRIBUTES (node->decl));
+
+  remove_attribute ("target_clones", attributes);
   DECL_ATTRIBUTES (node->decl) = attributes;
   node->local = false;
+
+  /* Remangle base node after new target version string set.  */
+  tree id = targetm.mangle_decl_assembler_name
+	      (node->decl, node->function_version ()->assembler_name);
+  symtab->change_decl_assembler_name (node->decl, id);
+
   return true;
 }
 
diff --git a/gcc/testsuite/g++.target/i386/mv-symbols1.C b/gcc/testsuite/g++.target/i386/mv-symbols1.C
index 1290299aea5..3163f03ddd8 100644
--- a/gcc/testsuite/g++.target/i386/mv-symbols1.C
+++ b/gcc/testsuite/g++.target/i386/mv-symbols1.C
@@ -55,14 +55,14 @@ int bar(int x)
 /* { dg-final { scan-assembler-times "\n_Z3foov\.arch_slm:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\.sse4.2:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\tcall\t_Z7_Z3foovv\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z7_Z3foovv, @gnu_indirect_function\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z7_Z3foovv,_Z3foov\.resolver\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tcall\t_Z3foov.ifunc\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov.ifunc, @gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov.ifunc,_Z3foov\.resolver\n" 1 } } */
 
 /* { dg-final { scan-assembler-times "\n_Z3fooi:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.arch_slm:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.sse4.2:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\tcall\t_Z7_Z3fooii\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z7_Z3fooii, @gnu_indirect_function\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z7_Z3fooii,_Z3fooi\.resolver\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tcall\t_Z3fooi.ifunc\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi.ifunc, @gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi.ifunc,_Z3fooi\.resolver\n" 1 } } */
diff --git a/gcc/testsuite/g++.target/i386/mv-symbols3.C b/gcc/testsuite/g++.target/i386/mv-symbols3.C
index a5cf3445a43..67b27351143 100644
--- a/gcc/testsuite/g++.target/i386/mv-symbols3.C
+++ b/gcc/testsuite/g++.target/i386/mv-symbols3.C
@@ -32,13 +32,13 @@ int bar()
 /* { dg-final { scan-assembler-times "\n_Z3foov\.arch_slm:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\.sse4.2:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\tcall\t_Z7_Z3foovv\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z7_Z3foovv, @gnu_indirect_function\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z7_Z3foovv,_Z3foov\.resolver\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tcall\t_Z3foov.ifunc\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov.ifunc, @gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov.ifunc,_Z3foov\.resolver\n" 1 } } */
 
 /* { dg-final { scan-assembler-times "\n_Z3fooi:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.arch_slm:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.sse4.2:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z7_Z3fooii, @gnu_indirect_function\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z7_Z3fooii,_Z3fooi\.resolver\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi.ifunc, @gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi.ifunc,_Z3fooi\.resolver\n" 0 } } */
diff --git a/gcc/testsuite/g++.target/i386/mv-symbols4.C b/gcc/testsuite/g++.target/i386/mv-symbols4.C
index bb10f126f67..c82db70da35 100644
--- a/gcc/testsuite/g++.target/i386/mv-symbols4.C
+++ b/gcc/testsuite/g++.target/i386/mv-symbols4.C
@@ -38,13 +38,13 @@ int bar()
 /* { dg-final { scan-assembler-times "\n_Z3foov\.arch_slm:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\.sse4.2:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\tcall\t_Z7_Z3foovv\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z7_Z3foovv, @gnu_indirect_function\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z7_Z3foovv,_Z3foov\.resolver\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tcall\t_Z3foov.ifunc\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov.ifunc, @gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov.ifunc,_Z3foov\.resolver\n" 1 } } */
 
 /* { dg-final { scan-assembler-times "\n_Z3fooi:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.arch_slm:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.sse4.2:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z7_Z3fooii, @gnu_indirect_function\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z7_Z3fooii,_Z3fooi\.resolver\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi.ifunc, @gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi.ifunc,_Z3fooi\.resolver\n" 0 } } */
diff --git a/gcc/testsuite/g++.target/i386/mv-symbols5.C b/gcc/testsuite/g++.target/i386/mv-symbols5.C
index d36e4c304c2..7792f113f22 100644
--- a/gcc/testsuite/g++.target/i386/mv-symbols5.C
+++ b/gcc/testsuite/g++.target/i386/mv-symbols5.C
@@ -44,13 +44,13 @@ int bar()
 /* { dg-final { scan-assembler-times "\n_Z3foov\.arch_slm:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\.sse4.2:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3foov\.resolver:\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\tcall\t_Z7_Z3foovv\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z7_Z3foovv, @gnu_indirect_function\n" 1 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z7_Z3foovv,_Z3foov\.resolver\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\tcall\t_Z3foov.ifunc\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3foov.ifunc, @gnu_indirect_function\n" 1 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3foov.ifunc,_Z3foov\.resolver\n" 1 } } */
 
 /* { dg-final { scan-assembler-times "\n_Z3fooi:\n" 0 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.arch_slm:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.sse4.2:\n" 1 } } */
 /* { dg-final { scan-assembler-times "\n_Z3fooi\.resolver:\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.type\t_Z7_Z3fooii, @gnu_indirect_function\n" 0 } } */
-/* { dg-final { scan-assembler-times "\n\t\.set\t_Z7_Z3fooii,_Z3fooi\.resolver\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.type\t_Z3fooi.ifunc, @gnu_indirect_function\n" 0 } } */
+/* { dg-final { scan-assembler-times "\n\t\.set\t_Z3fooi.ifunc,_Z3fooi\.resolver\n" 0 } } */
diff --git a/gcc/tree.cc b/gcc/tree.cc
index 83dc9f32f96..38e3151a8a9 100644
--- a/gcc/tree.cc
+++ b/gcc/tree.cc
@@ -15330,32 +15330,6 @@ get_attr_nonstring_decl (tree expr, tree *ref)
   return NULL_TREE;
 }
 
-/* Return length of attribute names string,
-   if arglist chain > 1, -1 otherwise.  */
-
-int
-get_target_clone_attr_len (tree arglist)
-{
-  tree arg;
-  int str_len_sum = 0;
-  int argnum = 0;
-
-  for (arg = arglist; arg; arg = TREE_CHAIN (arg))
-    {
-      const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
-      size_t len = strlen (str);
-      str_len_sum += len + 1;
-      for (const char *p = strchr (str, TARGET_CLONES_ATTR_SEPARATOR);
-	   p;
-	   p = strchr (p + 1, TARGET_CLONES_ATTR_SEPARATOR))
-	argnum++;
-      argnum++;
-    }
-  if (argnum <= 1)
-    return -1;
-  return str_len_sum;
-}
-
 /* Returns an auto_vec of string_slices containing the version strings from
    ARGLIST.  DEFAULT_COUNT is incremented for each default version found.  */
 
diff --git a/gcc/tree.h b/gcc/tree.h
index 70541070c40..cd62c7b9c8e 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -7035,7 +7035,6 @@ extern unsigned fndecl_dealloc_argno (tree);
    object or pointer.  Otherwise return null.  */
 extern tree get_attr_nonstring_decl (tree, tree * = NULL);
 
-extern int get_target_clone_attr_len (tree);
 auto_vec<string_slice> get_clone_versions (const tree, int * = NULL);
 auto_vec<string_slice> get_clone_attr_versions (const tree, int * = NULL);
 

Reply via email to