This patch is an overhaul of how FMV name mangling works. Previously mangling logic was duplicated in several places across both target specific and independent code. This patch changes this such that all mangling is done in targetm.mangle_decl_assembler_name (including for the dispatched symbol and dispatcher resolver).
This allows for the removing of previous hacks, such as where the default mangled decl's assembler name was unmangled to then remangle all versions and the resolver and dispatched symbol. This 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 (delete_function_version): Made public static member of cgraph_node. * cgraph.h (delete_function_version): Ditto. * config/aarch64/aarch64.cc (aarch64_parse_fmv_features): Change to support string_slice. (aarch64_process_target_version_attr): Ditto. (get_feature_mask_for_version): Ditto. (aarch64_mangle_decl_assembler_name): Add logic for mangling dispatched symbol and resolver. (get_suffixed_assembler_name): Removed. (make_resolver_func): Refactor to use aarch64_mangle_decl_assembler_name for mangling. (aarch64_generate_version_dispatcher_body): Remove remangling. (aarch64_get_function_versions_dispatcher): Refactor to remove duplicated cgraph logic. * config/i386/i386-features.cc (is_valid_asm_symbol): Moved from multiple_target.cc. (create_new_asm_name): Ditto. (ix86_mangle_function_version_assembler_name): Refactor to use clone_identifier and to mangle default. (ix86_mangle_decl_assembler_name): Add logic for mangling dispatched symbol and resolver. (ix86_get_function_versions_dispatcher): Remove duplicated cgraph logic. (make_resolver_func): Refactor to use ix86_mangle_decl_assembler_name for mangling. * config/riscv/riscv.cc (riscv_mangle_decl_assembler_name): Add logic for FMV mangling. (get_suffixed_assembler_name): Removed. (make_resolver_func): Refactor to use riscv_mangle_decl_assembler_name for mangling. (riscv_generate_version_dispatcher_body): Remove unnecessary remangling. (riscv_get_function_versions_dispatcher): Remove duplicated cgraph logic. * config/rs6000/rs6000.cc (rs6000_mangle_decl_assembler_name): New function. (rs6000_get_function_versions_dispatcher): Remove duplicated cgraph logic. (make_resolver_func): Refactor to use rs6000_mangle_decl_assembler_name for mangling. (is_valid_asm_symbol): Move from multiple_target.cc. (create_new_asm_name): Ditto. (rs6000_mangle_function_version_assembler_name): New function. * multiple_target.cc (create_dispatcher_calls): Remove mangling code. (get_attr_str): Removed. (separate_attrs): Ditto. (is_valid_asm_symbol): Moved to target specific. (create_new_asm_name): Ditto. (expand_target_clones): Refactor to use targetm.mangle_decl_assembler_name for mangling and be more general. * tree.cc (get_target_clone_attr_len): Removed. * tree.h (get_target_clone_attr_len): Removed. gcc/cp/ChangeLog: * decl.cc (maybe_mark_function_versioned): Change to insert function version and therefore record assembler name. gcc/testsuite/ChangeLog: * g++.target/i386/mv-symbols1.C: Update x86 FMV mangling. * g++.target/i386/mv-symbols3.C: Ditto. * g++.target/i386/mv-symbols4.C: Ditto. * g++.target/i386/mv-symbols5.C: Ditto. --- gcc/attribs.cc | 45 +++- gcc/cgraph.cc | 4 +- gcc/cgraph.h | 2 + gcc/config/aarch64/aarch64.cc | 163 +++++------- gcc/config/i386/i386-features.cc | 108 +++++--- gcc/config/riscv/riscv.cc | 110 +++----- gcc/config/rs6000/rs6000.cc | 115 +++++++-- gcc/cp/decl.cc | 7 + gcc/multiple_target.cc | 262 +++++++------------- 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, 424 insertions(+), 462 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 23f7748e49e..2d293e28bb8 100644 --- a/gcc/cgraph.cc +++ b/gcc/cgraph.cc @@ -198,8 +198,8 @@ cgraph_node::insert_new_function_version (void) } /* Remove the cgraph_function_version_info node given by DECL_V. */ -static void -delete_function_version (cgraph_function_version_info *decl_v) +void +cgraph_node::delete_function_version (cgraph_function_version_info *decl_v) { if (decl_v == NULL) return; diff --git a/gcc/cgraph.h b/gcc/cgraph.h index d6d8e066da6..0eed6a9d46d 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -1332,6 +1332,8 @@ struct GTY((tag ("SYMTAB_FUNCTION"))) cgraph_node : public symtab_node DECL is a duplicate declaration. */ static void delete_function_version_by_decl (tree decl); + static void delete_function_version (cgraph_function_version_info *); + /* Add the function FNDECL to the call graph. Unlike finalize_function, this function is intended to be used by middle end and allows insertion of new function at arbitrary point diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc index ffaa5611eaf..99e351fb65b 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -20009,7 +20009,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 @@ -20021,37 +20021,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; @@ -20063,7 +20060,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; } } @@ -20075,14 +20073,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; @@ -20119,7 +20112,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; @@ -20143,13 +20136,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: @@ -20221,13 +20214,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); @@ -20322,36 +20316,33 @@ tree aarch64_mangle_decl_assembler_name (tree decl, tree id) { /* For function version, add the target suffix to the assembler name. */ - if (TREE_CODE (decl) == FUNCTION_DECL - && DECL_FUNCTION_VERSIONED (decl)) + if (TREE_CODE (decl) == FUNCTION_DECL) { - aarch64_fmv_feature_mask feature_mask = get_feature_mask_for_version (decl); - - std::string name = IDENTIFIER_POINTER (id); - - /* For the default version, append ".default". */ - if (feature_mask == 0ULL) + cgraph_node *node = cgraph_node::get (decl); + if (node && node->dispatcher_function) + return id; + else if (node && node->dispatcher_resolver_function) + return clone_identifier (id, "resolver"); + else if (DECL_FUNCTION_VERSIONED (decl)) { - name += ".default"; - return get_identifier (name.c_str()); - } + aarch64_fmv_feature_mask feature_mask + = get_feature_mask_for_version (decl); - name += "._"; + if (feature_mask == 0ULL) + return clone_identifier (id, "default"); - int num_features = ARRAY_SIZE (aarch64_fmv_feature_data); - for (int i = 0; i < num_features; i++) - { - if (feature_mask & aarch64_fmv_feature_data[i].feature_mask) - { - name += "M"; - name += aarch64_fmv_feature_data[i].name; - } - } + std::string suffix = "_"; - if (DECL_ASSEMBLER_NAME_SET_P (decl)) - SET_DECL_RTL (decl, NULL); + int num_features = ARRAY_SIZE (aarch64_fmv_feature_data); + for (int i = 0; i < num_features; i++) + if (feature_mask & aarch64_fmv_feature_data[i].feature_mask) + { + suffix += "M"; + suffix += aarch64_fmv_feature_data[i].name; + } - id = get_identifier (name.c_str()); + id = clone_identifier (id, suffix.c_str ()); + } } return id; } @@ -20360,18 +20351,6 @@ aarch64_mangle_decl_assembler_name (tree decl, tree id) This is computed by taking the default version's assembler name, and stripping off the ".default" suffix if it's already been appended. */ -static tree -get_suffixed_assembler_name (tree default_decl, const char *suffix) -{ - std::string name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (default_decl)); - - auto size = name.size (); - if (size >= 8 && name.compare (size - 8, 8, ".default") == 0) - name.resize (size - 8); - name += suffix; - return get_identifier (name.c_str()); -} - /* Make the resolver function decl to dispatch the versions of a multi-versioned function, DEFAULT_DECL. IFUNC_ALIAS_DECL is ifunc alias that will point to the created resolver. Create an @@ -20385,11 +20364,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, @@ -20397,10 +20371,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; @@ -20465,7 +20450,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. */ @@ -20742,27 +20727,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; } @@ -20806,20 +20770,9 @@ aarch64_get_function_versions_dispatcher (void *decl) if (targetm.has_ifunc_p ()) { struct cgraph_function_version_info *it_v = NULL; - struct cgraph_node *dispatcher_node = NULL; - struct cgraph_function_version_info *dispatcher_version_info = NULL; /* Right now, the dispatching is done via ifunc. */ dispatch_decl = make_dispatcher_decl (default_node->decl); - TREE_NOTHROW (dispatch_decl) = TREE_NOTHROW (fn); - - dispatcher_node = cgraph_node::get_create (dispatch_decl); - gcc_assert (dispatcher_node != NULL); - dispatcher_node->dispatcher_function = 1; - dispatcher_version_info - = dispatcher_node->insert_new_function_version (); - dispatcher_version_info->next = default_version_info; - dispatcher_node->definition = 1; /* Set the dispatcher for all the versions. */ it_v = default_version_info; diff --git a/gcc/config/i386/i386-features.cc b/gcc/config/i386/i386-features.cc index 2eb3a21bb5d..f8eb6a95c00 100644 --- a/gcc/config/i386/i386-features.cc +++ b/gcc/config/i386/i386-features.cc @@ -3888,6 +3888,37 @@ dispatch_function_versions (tree dispatch_decl, return 0; } +/* Return true if symbol is valid in assembler name. */ + +static bool +is_valid_asm_symbol (char c) +{ + if ('a' <= c && c <= 'z') + return true; + if ('A' <= c && c <= 'Z') + return true; + if ('0' <= c && c <= '9') + return true; + if (c == '_') + return true; + return false; +} + +/* Replace all not valid assembler symbols with '_'. */ +static void +create_new_asm_name (char *old_asm_name, char *new_asm_name) +{ + int i; + int old_name_len = strlen (old_asm_name); + /* Replace all not valid assembler symbols with '_'. */ + for (i = 0; i < old_name_len; i++) + if (!is_valid_asm_symbol (old_asm_name[i])) + new_asm_name[i] = '_'; + else + new_asm_name[i] = old_asm_name[i]; + new_asm_name[old_name_len] = '\0'; +} + /* This function changes the assembler name for functions that are versions. If DECL is a function version and has a "target" attribute, it appends the attribute string to its assembler name. */ @@ -3896,8 +3927,7 @@ static tree ix86_mangle_function_version_assembler_name (tree decl, tree id) { tree version_attr; - const char *orig_name, *version_string; - char *attr_str, *assembler_name; + char *attr_str; if (DECL_DECLARED_INLINE_P (decl) && lookup_attribute ("gnu_inline", @@ -3915,25 +3945,24 @@ ix86_mangle_function_version_assembler_name (tree decl, tree id) /* target attribute string cannot be NULL. */ gcc_assert (version_attr != NULL_TREE); - orig_name = IDENTIFIER_POINTER (id); - version_string - = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (version_attr))); - - if (strcmp (version_string, "default") == 0) + cgraph_node *node = cgraph_node::get (decl); + if (!node && node->is_target_clone && is_function_default_version (decl)) return id; attr_str = sorted_attr_string (TREE_VALUE (version_attr)); - assembler_name = XNEWVEC (char, strlen (orig_name) + strlen (attr_str) + 2); - sprintf (assembler_name, "%s.%s", orig_name, attr_str); + char *suffix = XNEWVEC (char, strlen (attr_str) + 1); + create_new_asm_name (attr_str, suffix); /* Allow assembler name to be modified if already set. */ if (DECL_ASSEMBLER_NAME_SET_P (decl)) SET_DECL_RTL (decl, NULL); - tree ret = get_identifier (assembler_name); + tree ret = clone_identifier (id, suffix); + XDELETEVEC (attr_str); - XDELETEVEC (assembler_name); + XDELETEVEC (suffix); + return ret; } @@ -3941,9 +3970,21 @@ tree ix86_mangle_decl_assembler_name (tree decl, tree id) { /* For function version, add the target suffix to the assembler name. */ - if (TREE_CODE (decl) == FUNCTION_DECL - && DECL_FUNCTION_VERSIONED (decl)) - id = ix86_mangle_function_version_assembler_name (decl, id); + if (TREE_CODE (decl) == FUNCTION_DECL) + { + cgraph_node *node = cgraph_node::get (decl); + /* Mangle all versions when annotated with target_clones, but only + non-default versions when annotated with target attributes. */ + if (DECL_FUNCTION_VERSIONED (decl) + && (node->is_target_clone + || !is_function_default_version (node->decl))) + id = ix86_mangle_function_version_assembler_name (decl, id); + /* Mangle the dispatched symbol but only in the case of target clones. */ + else if (node && node->dispatcher_function && !node->is_target_clone) + id = clone_identifier (id, "ifunc"); + else if (node && node->dispatcher_resolver_function) + id = clone_identifier (id, "resolver"); + } #ifdef SUBTARGET_MANGLE_DECL_ASSEMBLER_NAME id = SUBTARGET_MANGLE_DECL_ASSEMBLER_NAME (decl, id); #endif @@ -3982,6 +4023,7 @@ ix86_get_function_versions_dispatcher (void *decl) default_version_info = node_v; while (default_version_info->prev != NULL) default_version_info = default_version_info->prev; + default_node = default_version_info->this_node; /* If there is no default node, just return NULL. */ if (!is_function_default_version (default_node->decl)) @@ -3991,20 +4033,9 @@ ix86_get_function_versions_dispatcher (void *decl) if (targetm.has_ifunc_p ()) { struct cgraph_function_version_info *it_v = NULL; - struct cgraph_node *dispatcher_node = NULL; - struct cgraph_function_version_info *dispatcher_version_info = NULL; /* Right now, the dispatching is done via ifunc. */ dispatch_decl = make_dispatcher_decl (default_node->decl); - TREE_NOTHROW (dispatch_decl) = TREE_NOTHROW (fn); - - dispatcher_node = cgraph_node::get_create (dispatch_decl); - gcc_assert (dispatcher_node != NULL); - dispatcher_node->dispatcher_function = 1; - dispatcher_version_info - = dispatcher_node->insert_new_function_version (); - dispatcher_version_info->next = default_version_info; - dispatcher_node->definition = 1; /* Set the dispatcher for all the versions. */ it_v = default_version_info; @@ -4038,17 +4069,28 @@ make_resolver_func (const tree default_decl, { tree decl, type, t; - /* Create resolver function name based on default_decl. */ - tree decl_name = clone_function_name (default_decl, "resolver"); - const char *resolver_name = IDENTIFIER_POINTER (decl_name); - /* The resolver function should return a (void *). */ type = build_function_type_list (ptr_type_node, NULL_TREE); - decl = build_fn_decl (resolver_name, type); - SET_DECL_ASSEMBLER_NAME (decl, decl_name); + cgraph_node *node = cgraph_node::get (default_decl); + gcc_assert (node && node->function_version ()); + + decl = build_fn_decl (IDENTIFIER_POINTER (DECL_NAME (default_decl)), type); + + /* Set the assembler name to prevent cgraph_node attempting to mangle. */ + SET_DECL_ASSEMBLER_NAME (decl, DECL_ASSEMBLER_NAME (default_decl)); + + cgraph_node *resolver_node = cgraph_node::get_create (decl); + resolver_node->dispatcher_resolver_function = true; + + if (node->is_target_clone) + resolver_node->is_target_clone = true; + + tree id = ix86_mangle_decl_assembler_name + (decl, node->function_version ()->assembler_name); + SET_DECL_ASSEMBLER_NAME (decl, id); - DECL_NAME (decl) = decl_name; + DECL_NAME (decl) = DECL_NAME (default_decl); TREE_USED (decl) = 1; DECL_ARTIFICIAL (decl) = 1; DECL_IGNORED_P (decl) = 1; @@ -4095,7 +4137,7 @@ make_resolver_func (const tree default_decl, gcc_assert (ifunc_alias_decl != NULL); /* Mark ifunc_alias_decl as "ifunc" with resolver as resolver_name. */ DECL_ATTRIBUTES (ifunc_alias_decl) - = make_attribute ("ifunc", resolver_name, + = make_attribute ("ifunc", IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)), DECL_ATTRIBUTES (ifunc_alias_decl)); /* Create the alias for dispatch to resolver here. */ diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index 99f548785db..946658e0d5e 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -13220,32 +13220,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; } @@ -13526,22 +13525,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 @@ -13555,10 +13538,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 *) */ @@ -13567,10 +13548,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; @@ -13635,7 +13627,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. */ @@ -13700,27 +13692,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; } @@ -13764,20 +13735,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 c644d821b48..0cf4bdfe603 100644 --- a/gcc/config/rs6000/rs6000.cc +++ b/gcc/config/rs6000/rs6000.cc @@ -88,6 +88,7 @@ extern tree rs6000_builtin_mask_for_load (void); extern tree rs6000_builtin_md_vectorized_function (tree, tree, tree); extern tree rs6000_builtin_reciprocal (tree); +static tree rs6000_mangle_decl_assembler_name (tree, tree); /* Set -mabi=ieeelongdouble on some old targets. In the future, power server systems will also set long double to be IEEE 128-bit. AIX and Darwin @@ -25352,20 +25353,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; @@ -25398,13 +25388,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; @@ -25450,7 +25451,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); @@ -28490,6 +28492,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 @@ -28675,6 +28748,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 fb6cde487d3..7cf39bcee7f 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -1274,6 +1274,13 @@ maybe_mark_function_versioned (tree decl) { if (!DECL_FUNCTION_VERSIONED (decl)) { + /* We need to insert function version now to make sure the correct + pre-mangled assembler name is recorded. */ + cgraph_node *node = cgraph_node::get_create (decl); + + if (!node->function_version ()) + node->insert_new_function_version (); + DECL_FUNCTION_VERSIONED (decl) = 1; /* If DECL_ASSEMBLER_NAME has already been set, re-mangle to include the version marker. */ diff --git a/gcc/multiple_target.cc b/gcc/multiple_target.cc index 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 fdcdfb336bc..77005bd0e03 100644 --- a/gcc/tree.cc +++ b/gcc/tree.cc @@ -15346,32 +15346,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 a89f3cf7189..ea9d2e119f9 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -7051,8 +7051,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