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
@@ -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
+
+ 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);
+ }