Alfie Richards <alfie.richa...@arm.com> writes: > diff --git a/gcc/attribs.cc b/gcc/attribs.cc > index b00d9529a8d..d0f37d77098 100644 > --- a/gcc/attribs.cc > +++ b/gcc/attribs.cc > [...] > @@ -1287,6 +1282,33 @@ 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);
Very minor suggestion, but: all callers already have the node to hand and pass the decl inside it, so perhaps it would make sense to change make_dispatcher_decl so that it takes the cgraph node instead. > [...] > @@ -19894,37 +19894,35 @@ 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) > + string_slice str_parse = str; > + > + gcc_assert (str.is_valid ()); > + while (str_parse.is_valid ()) > { > - const char *ext; > - size_t len; > + string_slice ext; > > - ext = strchr (str, '+'); > + ext = string_slice::tokenize (&str_parse, string_slice ("+")); Following on from the comment about explicit constructors, it'd be nice not to need the explicit constructor here. > - if (ext != NULL) > - len = ext - str; > - else > - len = strlen (str); > + gcc_assert (ext.is_valid ()); > > - if (len == 0) > + if (!ext.is_valid () || ext.empty ()) The assert makes the !ext.is_valid () part redundant. > 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; > [...] > @@ -19992,7 +19987,7 @@ aarch64_process_target_version_attr (tree args) > return false; > } > > - const char *str = TREE_STRING_POINTER (args); > + string_slice str = string_slice (TREE_STRING_POINTER (args)); Similarly here, I'd hope: string_slice str = TREE_STRING_POINTER (args); would be enough. > > enum aarch_parse_opt_result parse_res; > auto isa_flags = aarch64_asm_isa_flags; > @@ -20195,36 +20191,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; > } Thanks, the new flow makes a lot more intuitive sense. > @@ -20233,18 +20226,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. */ The comment above the function should go as well. > > -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 > [...] > @@ -20270,10 +20249,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); Similarly to the comment above, the caller already has this node to hand, so it might be better to pass that instead of the 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; > @@ -20338,7 +20328,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. */ Sorry to create more work :), but how much of make_resolver_func is shared between AArch64 and RISC-V? If the two functions are essentially identical, minus using things like aarch64_mangle_decl_assembler_name directly instead of the hook, then it might be worth moving this into target-independent code. To be clear, this is for target_version targets only. It's fine (IMO) if x86 and powerpc need ad hoc variants that stay inside the target code. > [...] > diff --git a/gcc/multiple_target.cc b/gcc/multiple_target.cc > index d8becf4d9a9..cd7570a9aad 100644 > --- a/gcc/multiple_target.cc > +++ b/gcc/multiple_target.cc > [...] > @@ -317,11 +219,11 @@ 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_def = 0; > + auto_vec<string_slice> attr_list = get_clone_versions (node->decl, > &num_def); > > /* 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"); Sorry for only bringing this up now, but I wonder if we should bump this to an error for target_version targets. It seems odd that we make no attempt to validate the string further. > @@ -348,67 +250,71 @@ 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_def > 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_def == 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"); > + /* Disallow any empty values in the clone attr. */ > + for (string_slice attr : attr_list) > + if (attr == string_slice ("") || !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 = string_slice > + (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) > + if (!decl1_v) > decl1_v = node->insert_new_function_version (); > - before = decl1_v; > + > + node->is_target_clone = true; > DECL_FUNCTION_VERSIONED (node->decl) = 1; > > - for (i = 0; i < attrnum; i++) > + before = decl1_v; > + > + /* The existing decl is turned into one of the target versions. > + If there is a default in the list then this decl is used for that > version > + as the calls are already to it so in the case where there is no > + implementation and !TARGET_HAS_FMV_TARGET_ATTRIBUTE there is no > redirection > + necessary. > + Otherwise, the first version listed in the attribute is used. */ Looks like it uses the last version listed, rather than the first. > + string_slice this_node_version > + = num_def ? string_slice ("default") : attr_list.pop (); > + > + for (string_slice attr : attr_list) > { > - char *attr = attrs[i]; > + /* Skip default nodes. */ > + if (attr == string_slice ("default")) Would be good to avoid the explicit constructor. > + 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); > + /* Remove the target_clones attribute, as this can confuse > + is_function_default_version. */ > + remove_attribute ("target_clones", attributes); The make_attribute call above reuses the old DECL_ATTRIBUTES, rather than copying it, so isn't the effect of this to remove the target_clones attribute from node->decl as well, if the attribute isn't the first one in node->decl? If that's ok (it might be), then perhaps we should do this once, before the loop. Otherwise the attribs.cc, multiple-target.cc, tree.* and aarch64 bits look really good. I'm not qualified to review the rest. Thanks, Richard