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);