Ping for the x86 parts of this patch. All other parts have been approved.
Thanks, Alfie The 08/28/2025 09:49, alfie.richa...@arm.com wrote: > From: Alfie Richards <alfie.richa...@arm.com> > > This patch is an overhaul of how FMV name mangling works. Previously > mangling logic was duplicated in several places across both target > specific and independent code. This patch changes this such that all > mangling is done in targetm.mangle_decl_assembler_name (including for the > dispatched symbol and dispatcher resolver). > > Adds the assembler_name member to cgraph_function_version_info to store > the base assembler name of the function set, before FMV mangling. > > This allows for the removing of previous hacks, such as where the default > mangled decl's assembler name was unmangled to then remangle all versions > and the resolver and dispatched symbol. > > This introduces a change (shown in test changes) for the assembler name of the > dispatched symbol for a x86 versioned function set. Previously it used the > function name mangled twice. This was hard to reproduce without hacks I > wasn't comfortable with. Therefore, the mangling is changed to instead append > ".ifunc" which matches clang's behavior. > > This change also refactors expand_target_clone using > targetm.mangle_decl_assembler_name for mangling and get_clone_versions. > It is modified such that if the target_clone is in a FMV structure > the ordering is preserved once expanded. This is used later for ACLE semantics > and target_clone/target_version mixing. > > gcc/ChangeLog: > > * attribs.cc (make_dispatcher_decl): Move duplicated cgraph logic into > this function and change to use targetm.mangle_decl_assembler_name for > mangling. > * cgraph.cc (cgraph_node::insert_new_function_version): Record > assembler_name. > * cgraph.h (struct cgraph_function_version_info): Add assembler_name. > (struct cgraph_node): Add dispatcher_resolver_function and > is_target_clone. > * config/aarch64/aarch64.cc (aarch64_parse_fmv_features): Change to > support string_slice. > (aarch64_process_target_version_attr): Ditto. > (get_feature_mask_for_version): Ditto. > (aarch64_mangle_decl_assembler_name): Add logic for mangling dispatched > symbol and resolver. > (get_suffixed_assembler_name): Removed. > (make_resolver_func): Refactor to use > aarch64_mangle_decl_assembler_name for mangling. > (aarch64_generate_version_dispatcher_body): Remove remangling. > (aarch64_get_function_versions_dispatcher): Refactor to remove > duplicated cgraph logic. > * config/i386/i386-features.cc > (ix86_mangle_function_version_assembler_name): Refactor to use > clone_identifier and to mangle default. > (ix86_mangle_decl_assembler_name): Add logic for mangling dispatched > symbol and resolver. > (ix86_get_function_versions_dispatcher): Remove duplicated cgraph > logic. > (make_resolver_func): Refactor to use ix86_mangle_decl_assembler_name > for mangling. > * config/riscv/riscv.cc (riscv_mangle_decl_assembler_name): Add logic > for FMV mangling. > (get_suffixed_assembler_name): Removed. > (make_resolver_func): Refactor to use riscv_mangle_decl_assembler_name > for mangling. > (riscv_generate_version_dispatcher_body): Remove unnecessary remangling. > (riscv_get_function_versions_dispatcher): Remove duplicated cgraph > logic. > * config/rs6000/rs6000.cc (rs6000_mangle_decl_assembler_name): New > function. > (rs6000_get_function_versions_dispatcher): Remove duplicated cgraph > logic. > (make_resolver_func): Refactor to use rs6000_mangle_decl_assembler_name > for mangling. > (rs6000_mangle_function_version_assembler_name): New function. > * multiple_target.cc (create_dispatcher_calls): Remove mangling code. > (get_attr_str): Removed. > (separate_attrs): Ditto. > (is_valid_asm_symbol): Removed. > (create_new_asm_name): Ditto. > (expand_target_clones): Refactor to use > targetm.mangle_decl_assembler_name for mangling and be more general. > * tree.cc (get_target_clone_attr_len): Removed. > * tree.h (get_target_clone_attr_len): Removed. > > gcc/cp/ChangeLog: > > * decl.cc (maybe_mark_function_versioned): Change to insert function > version > and therefore record assembler name. > > gcc/testsuite/ChangeLog: > > * g++.target/i386/mv-symbols1.C: Update x86 FMV mangling. > * g++.target/i386/mv-symbols3.C: Ditto. > * g++.target/i386/mv-symbols4.C: Ditto. > * g++.target/i386/mv-symbols5.C: Ditto. > --- > gcc/attribs.cc | 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 c5110566215..4c4ec2d25f3 100644 > --- a/gcc/config/aarch64/aarch64.cc > +++ b/gcc/config/aarch64/aarch64.cc > @@ -20383,7 +20383,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 > @@ -20395,37 +20395,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; > @@ -20437,7 +20434,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; > } > } > @@ -20449,14 +20447,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; > @@ -20493,7 +20486,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; > @@ -20517,13 +20510,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: > @@ -20595,13 +20588,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); > @@ -20708,56 +20702,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 > @@ -20771,11 +20746,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, > @@ -20783,10 +20753,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; > @@ -20851,7 +20832,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. */ > @@ -21128,27 +21109,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; > } > > @@ -21192,20 +21152,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 93e20947edf..f5250119d1f 100644 > --- a/gcc/config/i386/i386-features.cc > +++ b/gcc/config/i386/i386-features.cc > @@ -5266,8 +5266,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", > @@ -5285,25 +5284,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; > } > > @@ -5311,9 +5305,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 > @@ -5362,20 +5368,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; > @@ -5409,17 +5404,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; > @@ -5466,7 +5472,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 985fe67f822..8d321c21ccb 100644 > --- a/gcc/config/riscv/riscv.cc > +++ b/gcc/config/riscv/riscv.cc > @@ -14091,32 +14091,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; > } > > @@ -14398,22 +14397,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 > @@ -14427,10 +14410,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 *) */ > @@ -14439,10 +14420,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; > @@ -14507,7 +14499,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. */ > @@ -14572,27 +14564,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; > } > > @@ -14636,20 +14607,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 8dd23f8619c..d00d5f27121 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 4b1a335910b..d416f6200d6 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 905c2d6657f..d351e9e253c 100644 > --- a/gcc/tree.cc > +++ b/gcc/tree.cc > @@ -15420,32 +15420,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 ce8c778087f..6d28f2dddd8 100644 > --- a/gcc/tree.h > +++ b/gcc/tree.h > @@ -7101,8 +7101,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); > -- > 2.34.1 > -- Alfie Richards