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 | 45 +++-
gcc/cgraph.cc | 1 +
gcc/cgraph.h | 13 +-
gcc/config/aarch64/aarch64.cc | 167 +++++--------
gcc/config/i386/i386-features.cc | 74 +++---
gcc/config/riscv/riscv.cc | 110 +++------
gcc/config/rs6000/rs6000.cc | 82 +++++--
gcc/cp/decl.cc | 7 +
gcc/multiple_target.cc | 250 ++++++--------------
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 | 2 -
15 files changed, 350 insertions(+), 469 deletions(-)
diff --git a/gcc/attribs.cc b/gcc/attribs.cc
index 3fce9d62525..c75fd1371fd 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. */
@@ -1248,18 +1249,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;
@@ -1269,6 +1264,34 @@ 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);
+
+ /* Set flags on the cgraph_node for the new decl. */
+ 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/cgraph.cc b/gcc/cgraph.cc
index 32071a84bac..c0be16edfcb 100644
--- a/gcc/cgraph.cc
+++ b/gcc/cgraph.cc
@@ -322,6 +322,7 @@ cgraph_node::insert_new_function_version (void)
version_info_node = NULL;
version_info_node = ggc_cleared_alloc<cgraph_function_version_info> ();
version_info_node->this_node = this;
+ version_info_node->assembler_name = DECL_ASSEMBLER_NAME (this->decl);
if (cgraph_fnver_htab == NULL)
cgraph_fnver_htab = hash_table<function_version_hasher>::create_ggc (2);
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 189fa74497b..21f89112769 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -856,6 +856,9 @@ struct GTY((for_user)) cgraph_function_version_info {
dispatcher. The dispatcher decl is an alias to the resolver
function decl. */
tree dispatcher_resolver;
+
+ /* The assmbly name of the function set before version mangling. */
+ tree assembler_name;
};
#define DEFCIFCODE(code, type, string) CIF_ ## code,
@@ -904,7 +907,9 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public
symtab_node
used_as_abstract_origin (false),
lowered (false), process (false), frequency (NODE_FREQUENCY_NORMAL),
only_called_at_startup (false), only_called_at_exit (false),
- tm_clone (false), dispatcher_function (false), calls_comdat_local
(false),
+ tm_clone (false), dispatcher_function (false),
+ dispatcher_resolver_function (false), is_target_clone (false),
+ calls_comdat_local (false),
icf_merged (false), nonfreeing_fn (false), merged_comdat (false),
merged_extern_inline (false), parallelized_function (false),
split_part (false), indirect_call_target (false), local (false),
@@ -1482,6 +1487,12 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node :
public symtab_node
unsigned tm_clone : 1;
/* True if this decl is a dispatcher for function versions. */
unsigned dispatcher_function : 1;
+ /* True if this decl is a resolver for function versions. */
+ unsigned dispatcher_resolver_function : 1;
+ /* True this is part of a multiversioned set and this version comes from a
+ target_clone attribute. Or if this is a dispatched symbol or resolver
+ and the default version comes from a target_clones. */
+ unsigned is_target_clone : 1;
/* True if this decl calls a COMDAT-local function. This is set up in
compute_fn_summary and inline_call. */
unsigned calls_comdat_local : 1;
diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index f88b7c95f97..78db3a436a7 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -20323,7 +20323,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
@@ -20335,37 +20335,34 @@ 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)
+ gcc_assert (str.is_valid ());
+
+ while (str.is_valid ())
{
- const char *ext;
- size_t len;
+ string_slice ext;
- ext = strchr (str, '+');
+ ext = string_slice::tokenize (&str, "+");
- 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;
@@ -20377,7 +20374,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;
}
}
@@ -20389,14 +20387,9 @@ aarch64_parse_fmv_features (const char *str,
aarch64_feature_flags *isa_flags,
{
/* Feature not found in list. */
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;
@@ -20433,7 +20426,7 @@ aarch64_process_target_version_attr (tree args)
return false;
}
- const char *str = TREE_STRING_POINTER (args);
+ string_slice str = TREE_STRING_POINTER (args);
enum aarch_parse_opt_result parse_res;
auto isa_flags = aarch64_asm_isa_flags;
@@ -20457,13 +20450,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:
@@ -20535,13 +20528,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
+ = 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);
@@ -20648,56 +20642,37 @@ 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 (), true);
+ }
}
return id;
}
-/* 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
@@ -20711,11 +20686,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,
@@ -20723,10 +20693,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;
@@ -20791,7 +20772,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. */
@@ -21068,27 +21049,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;
}
@@ -21132,20 +21092,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 b4aa5fa1de7..9b798b3560e 100644
--- a/gcc/config/i386/i386-features.cc
+++ b/gcc/config/i386/i386-features.cc
@@ -4566,8 +4566,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",
@@ -4585,25 +4584,20 @@ 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);
/* 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, attr_str, true);
+
XDELETEVEC (attr_str);
- XDELETEVEC (assembler_name);
+
return ret;
}
@@ -4611,9 +4605,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
@@ -4662,20 +4668,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;
@@ -4709,17 +4704,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;
@@ -4766,7 +4772,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 e394cac7d41..257bb56886f 100644
--- a/gcc/config/riscv/riscv.cc
+++ b/gcc/config/riscv/riscv.cc
@@ -14074,32 +14074,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;
}
@@ -14380,22 +14379,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
@@ -14409,10 +14392,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 *) */
@@ -14421,10 +14402,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;
@@ -14489,7 +14481,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. */
@@ -14554,27 +14546,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;
}
@@ -14618,20 +14589,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 764b4992fb5..4b3b261da9e 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
@@ -25366,20 +25367,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;
@@ -25412,13 +25402,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));
- DECL_NAME (decl) = decl_name;
+ 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 (default_decl);
TREE_USED (decl) = 1;
DECL_ARTIFICIAL (decl) = 1;
DECL_IGNORED_P (decl) = 0;
@@ -25464,7 +25465,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);
@@ -28507,6 +28509,44 @@ complex_divide_builtin_code (machine_mode mode)
return (built_in_function) func;
}
+/* 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));
+
+ tree ret = clone_identifier (id, attr_str, true);
+
+ XDELETEVEC (attr_str);
+ 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
@@ -28692,6 +28732,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 5aa8203649c..0671c9b594b 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..dc3b72c8e85 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,96 @@ 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 this
+ point as only target_clones declarations containing default version is
+ resolvable so this decl will have no calls/references. */
+
+ 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. */
+ gcc_assert (num_defaults == 1);
+
+ /* 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);
+
+ for (string_slice attr : attr_list)
{
- char *attr = attrs[i];
+ /* 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);
+ tree attributes = make_attribute (new_attr_name, attr, attrs);
+
+ 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 node's 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);
- /* 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 dcc1abd0542..ad381091820 100644
--- a/gcc/tree.cc
+++ b/gcc/tree.cc
@@ -15397,32 +15397,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 f1aacb31b60..b67331aef74 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -7097,8 +7097,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);