FWIW, I agree with Jeff's comment in the v6 series against the duplication
of is_valid_asm_symbol and create_new_asm_name.  On the aarch64 bits:

Alfie Richards <alfie.richa...@arm.com> writes:
> @@ -20549,18 +20540,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());
> -}
> -

The comment above the function should go too.

The target-independent and aarch64-specific parts LGTM with those changes.
Thanks for all your work on this -- it's a nice improvement over the v1
version, which was itself a nice improvement over the status quo.

Richard

>  /* 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
> @@ -20574,11 +20553,6 @@ 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);
> -
>    /* The resolver function should have signature
>       (void *) resolver (uint64_t, const __ifunc_arg_t *) */
>    type = build_function_type_list (ptr_type_node,
> @@ -20586,10 +20560,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;
> @@ -20654,7 +20639,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.  */
> @@ -20931,27 +20916,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;
>  }
>  
> @@ -20995,20 +20959,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 c131577805f..684767714af 100644
> --- a/gcc/config/i386/i386-features.cc
> +++ b/gcc/config/i386/i386-features.cc
> @@ -4549,6 +4549,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.  */
> @@ -4557,8 +4588,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",
> @@ -4576,25 +4606,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;
>  }
>  
> @@ -4602,9 +4631,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
> @@ -4653,20 +4694,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;
> @@ -4700,17 +4730,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;
> @@ -4757,7 +4798,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 cb9fe31c8b1..65b179f36ab 100644
> --- a/gcc/config/riscv/riscv.cc
> +++ b/gcc/config/riscv/riscv.cc
> @@ -14113,32 +14113,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;
>  }
>  
> @@ -14419,22 +14418,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
> @@ -14448,10 +14431,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 *) */
> @@ -14460,10 +14441,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;
> @@ -14528,7 +14520,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.  */
> @@ -14593,27 +14585,6 @@ riscv_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 = 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;
>  }
>  
> @@ -14657,20 +14628,9 @@ riscv_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/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc
> index 7ee26e52b13..357eb4533d7 100644
> --- a/gcc/config/rs6000/rs6000.cc
> +++ b/gcc/config/rs6000/rs6000.cc
> @@ -87,6 +87,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
> @@ -25350,20 +25351,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;
> @@ -25396,13 +25386,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;
> @@ -25448,7 +25449,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);
>  
> @@ -28491,6 +28493,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
> @@ -28676,6 +28749,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 0ac92f84b0e..46d680e3197 100644
> --- a/gcc/cp/decl.cc
> +++ b/gcc/cp/decl.cc
> @@ -1272,6 +1272,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 d25277c0a93..44340cbc6a4 100644
> --- a/gcc/multiple_target.cc
> +++ b/gcc/multiple_target.cc
> @@ -166,9 +166,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
> @@ -184,100 +181,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 *
> @@ -313,7 +216,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));
> @@ -321,11 +223,12 @@ 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_defaults = 0;
> +  auto_vec<string_slice> attr_list = get_clone_versions (node->decl,
> +                                                      &num_defaults);
>  
>    /* 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");
> @@ -352,95 +255,114 @@ 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_defaults > 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_defaults == 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");
> -  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)
> -    decl1_v = node->insert_new_function_version ();
> -  before = decl1_v;
> -  DECL_FUNCTION_VERSIONED (node->decl) = 1;
> -
> -  for (i = 0; i < attrnum; i++)
> +  /* Disallow any empty values in the clone attr.  */
> +  for (string_slice attr : attr_list)
> +    if (attr.empty () || !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 = TARGET_HAS_FMV_TARGET_ATTRIBUTE
> +                            ? "target"
> +                            : "target_version";
> +
> +  cgraph_function_version_info *node_v = node->function_version ();
> +
> +  if (!node_v)
> +    node_v = node->insert_new_function_version ();
> +
> +  /* If this target_clones contains a default, then convert this node to the
> +     default.  If this node does not contain default (this is only possible
> +     in target_version semantics) then remove the node.  This is safe at the
> +     point as only target_clones declarations containing default version is
> +     resolvable so this decl will have no calls/refrences.  */
> +
> +  tree attrs =  remove_attribute ("target_clones",
> +                               DECL_ATTRIBUTES (node->decl));
> +  tree assembler_name = node_v->assembler_name;
> +
> +  /* Change the current node into the default node.  */
> +  if (num_defaults == 1)
>      {
> -      char *attr = attrs[i];
> +      /* Setting new attribute to initial function.  */
> +      tree attributes = make_attribute (new_attr_name, "default", attrs);
> +      DECL_ATTRIBUTES (node->decl) = attributes;
> +      DECL_FUNCTION_VERSIONED (node->decl) = true;
> +
> +      node->is_target_clone = true;
> +      node->local = false;
> +
> +      /* Remangle base node after new target version string set.  */
> +      tree id = targetm.mangle_decl_assembler_name (node->decl, 
> assembler_name);
> +      symtab->change_decl_assembler_name (node->decl, id);
> +    }
> +  else
> +    {
> +      /* Target clones without a default are only allowed for target_version
> +      semantics where we can have target_clones/target_version mixing.  */
> +      gcc_assert (!TARGET_HAS_FMV_TARGET_ATTRIBUTE);
> +
> +      /* If there isn't a default version, can safely remove this version.
> +      The node itself gets removed after the other versions are created.  */
> +      cgraph_function_version_info *temp = node_v;
> +      node_v = node_v->next ? node_v->next : node_v->prev;
> +      cgraph_node::delete_function_version (temp);
> +    }
> +
> +  for (string_slice attr : attr_list)
> +    {
> +      /* Skip default nodes.  */
> +      if (attr == "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);
> +      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 ();
> -      if (decl2_v != NULL)
> -        continue;
> -      decl2_v = new_node->insert_new_function_version ();
> -
> -      /* Chain decl2_v and decl1_v.  All semantically identical versions
> -      will be chained together.  */
> -      after = decl2_v;
> -      while (before->next != NULL)
> -     before = before->next;
> -      while (after->prev != NULL)
> -     after = after->prev;
> -
> -      before->next = after;
> -      after->prev = before;
> -      DECL_FUNCTION_VERSIONED (new_node->decl) = 1;
> +      DECL_FUNCTION_VERSIONED (new_node->decl) = true;
> +      if (!node_v)
> +     node_v = new_node->insert_new_function_version ();
> +      else
> +     cgraph_node::add_function_version (node_v, new_node->decl);
> +
> +      /* Use the base nodes assembler name for all created nodes.  */
> +      new_node->function_version ()->assembler_name = assembler_name;
> +      new_node->is_target_clone = true;
> +
> +      /* 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);
>      }
>  
> -  XDELETEVEC (attrs);
> -  XDELETEVEC (attr_str);
> +  /* If there are no default versions in the target_clones, this node is not
> +     reused, so can delete this node.  */
> +  if (num_defaults == 0)
> +    node->remove ();
>  
> -  /* Setting new attribute to initial function.  */
> -  tree attributes = make_attribute (new_attr_name, "default",
> -                                 DECL_ATTRIBUTES (node->decl));
> -  DECL_ATTRIBUTES (node->decl) = attributes;
> -  node->local = false;
>    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 0f02924763f..2335a6993b9 100644
> --- a/gcc/tree.cc
> +++ b/gcc/tree.cc
> @@ -15394,32 +15394,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 c0e434ba897..eadf0603066 100644
> --- a/gcc/tree.h
> +++ b/gcc/tree.h
> @@ -7087,8 +7087,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);
> -
>  /* Returns the version string for a decl with target_version attribute.
>     Returns an invalid string_slice if no attribute is present.  */
>  extern string_slice get_target_version (const tree);

Reply via email to