xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/multiple_target.c (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
11debfc3dSmrg /* Pass for parsing functions with multiple target attributes.
21debfc3dSmrg 
31debfc3dSmrg    Contributed by Evgeny Stupachenko <evstupac@gmail.com>
41debfc3dSmrg 
5*8feb0f0bSmrg    Copyright (C) 2015-2020 Free Software Foundation, Inc.
61debfc3dSmrg 
71debfc3dSmrg This file is part of GCC.
81debfc3dSmrg 
91debfc3dSmrg GCC is free software; you can redistribute it and/or modify it under
101debfc3dSmrg the terms of the GNU General Public License as published by the Free
111debfc3dSmrg Software Foundation; either version 3, or (at your option) any later
121debfc3dSmrg version.
131debfc3dSmrg 
141debfc3dSmrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
151debfc3dSmrg WARRANTY; without even the implied warranty of MERCHANTABILITY or
161debfc3dSmrg FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
171debfc3dSmrg for more details.
181debfc3dSmrg 
191debfc3dSmrg You should have received a copy of the GNU General Public License
201debfc3dSmrg along with GCC; see the file COPYING3.  If not see
211debfc3dSmrg <http://www.gnu.org/licenses/>.  */
221debfc3dSmrg 
231debfc3dSmrg #include "config.h"
241debfc3dSmrg #include "system.h"
251debfc3dSmrg #include "coretypes.h"
261debfc3dSmrg #include "backend.h"
271debfc3dSmrg #include "tree.h"
281debfc3dSmrg #include "stringpool.h"
291debfc3dSmrg #include "gimple.h"
301debfc3dSmrg #include "diagnostic-core.h"
311debfc3dSmrg #include "gimple-ssa.h"
321debfc3dSmrg #include "cgraph.h"
331debfc3dSmrg #include "tree-pass.h"
341debfc3dSmrg #include "target.h"
351debfc3dSmrg #include "attribs.h"
361debfc3dSmrg #include "pretty-print.h"
37a2dc1f3fSmrg #include "gimple-iterator.h"
38a2dc1f3fSmrg #include "gimple-walk.h"
39a2dc1f3fSmrg #include "tree-inline.h"
40a2dc1f3fSmrg #include "intl.h"
41a2dc1f3fSmrg 
42a2dc1f3fSmrg /* Walker callback that replaces all FUNCTION_DECL of a function that's
43a2dc1f3fSmrg    going to be versioned.  */
44a2dc1f3fSmrg 
45a2dc1f3fSmrg static tree
replace_function_decl(tree * op,int * walk_subtrees,void * data)46a2dc1f3fSmrg replace_function_decl (tree *op, int *walk_subtrees, void *data)
47a2dc1f3fSmrg {
48a2dc1f3fSmrg   struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
49a2dc1f3fSmrg   cgraph_function_version_info *info = (cgraph_function_version_info *)wi->info;
50a2dc1f3fSmrg 
51a2dc1f3fSmrg   if (TREE_CODE (*op) == FUNCTION_DECL
52a2dc1f3fSmrg       && info->this_node->decl == *op)
53a2dc1f3fSmrg     {
54a2dc1f3fSmrg       *op = info->dispatcher_resolver;
55a2dc1f3fSmrg       *walk_subtrees = 0;
56a2dc1f3fSmrg     }
57a2dc1f3fSmrg 
58a2dc1f3fSmrg   return NULL;
59a2dc1f3fSmrg }
601debfc3dSmrg 
611debfc3dSmrg /* If the call in NODE has multiple target attribute with multiple fields,
621debfc3dSmrg    replace it with dispatcher call and create dispatcher (once).  */
631debfc3dSmrg 
641debfc3dSmrg static void
create_dispatcher_calls(struct cgraph_node * node)651debfc3dSmrg create_dispatcher_calls (struct cgraph_node *node)
661debfc3dSmrg {
67a2dc1f3fSmrg   ipa_ref *ref;
681debfc3dSmrg 
69a2dc1f3fSmrg   if (!DECL_FUNCTION_VERSIONED (node->decl)
70a2dc1f3fSmrg       || !is_function_default_version (node->decl))
71a2dc1f3fSmrg     return;
721debfc3dSmrg 
731debfc3dSmrg   if (!targetm.has_ifunc_p ())
741debfc3dSmrg     {
75a2dc1f3fSmrg       error_at (DECL_SOURCE_LOCATION (node->decl),
76c0a68be4Smrg 		"the call requires %<ifunc%>, which is not"
771debfc3dSmrg 		" supported by this target");
78a2dc1f3fSmrg       return;
791debfc3dSmrg     }
801debfc3dSmrg   else if (!targetm.get_function_versions_dispatcher)
811debfc3dSmrg     {
82a2dc1f3fSmrg       error_at (DECL_SOURCE_LOCATION (node->decl),
831debfc3dSmrg 		"target does not support function version dispatcher");
84a2dc1f3fSmrg       return;
851debfc3dSmrg     }
861debfc3dSmrg 
87a2dc1f3fSmrg   tree idecl = targetm.get_function_versions_dispatcher (node->decl);
881debfc3dSmrg   if (!idecl)
891debfc3dSmrg     {
90a2dc1f3fSmrg       error_at (DECL_SOURCE_LOCATION (node->decl),
91a2dc1f3fSmrg 		"default %<target_clones%> attribute was not set");
92a2dc1f3fSmrg       return;
931debfc3dSmrg     }
94a2dc1f3fSmrg 
95a2dc1f3fSmrg   cgraph_node *inode = cgraph_node::get (idecl);
961debfc3dSmrg   gcc_assert (inode);
97a2dc1f3fSmrg   tree resolver_decl = targetm.generate_version_dispatcher_body (inode);
981debfc3dSmrg 
991debfc3dSmrg   /* Update aliases.  */
1001debfc3dSmrg   inode->alias = true;
1011debfc3dSmrg   inode->alias_target = resolver_decl;
1021debfc3dSmrg   if (!inode->analyzed)
1031debfc3dSmrg     inode->resolve_alias (cgraph_node::get (resolver_decl));
1041debfc3dSmrg 
105a2dc1f3fSmrg   auto_vec<cgraph_edge *> edges_to_redirect;
106a2dc1f3fSmrg   /* We need to capture the references by value rather than just pointers to them
107a2dc1f3fSmrg      and remove them right away, as removing them later would invalidate what
108a2dc1f3fSmrg      some other reference pointers point to.  */
109a2dc1f3fSmrg   auto_vec<ipa_ref> references_to_redirect;
110a2dc1f3fSmrg 
111a2dc1f3fSmrg   while (node->iterate_referring (0, ref))
112a2dc1f3fSmrg     {
113a2dc1f3fSmrg       references_to_redirect.safe_push (*ref);
114a2dc1f3fSmrg       ref->remove_reference ();
115a2dc1f3fSmrg     }
116a2dc1f3fSmrg 
117a2dc1f3fSmrg   /* We need to remember NEXT_CALLER as it could be modified in the loop.  */
118a2dc1f3fSmrg   for (cgraph_edge *e = node->callers; e ; e = e->next_caller)
119a2dc1f3fSmrg     edges_to_redirect.safe_push (e);
120a2dc1f3fSmrg 
121a2dc1f3fSmrg   if (!edges_to_redirect.is_empty () || !references_to_redirect.is_empty ())
122a2dc1f3fSmrg     {
123a2dc1f3fSmrg       /* Redirect edges.  */
124a2dc1f3fSmrg       unsigned i;
125a2dc1f3fSmrg       cgraph_edge *e;
126a2dc1f3fSmrg       FOR_EACH_VEC_ELT (edges_to_redirect, i, e)
127a2dc1f3fSmrg 	{
1281debfc3dSmrg 	  e->redirect_callee (inode);
129*8feb0f0bSmrg 	  cgraph_edge::redirect_call_stmt_to_callee (e);
1301debfc3dSmrg 	}
131a2dc1f3fSmrg 
132a2dc1f3fSmrg       /* Redirect references.  */
133a2dc1f3fSmrg       FOR_EACH_VEC_ELT (references_to_redirect, i, ref)
134a2dc1f3fSmrg 	{
135a2dc1f3fSmrg 	  if (ref->use == IPA_REF_ADDR)
136a2dc1f3fSmrg 	    {
137a2dc1f3fSmrg 	      struct walk_stmt_info wi;
138a2dc1f3fSmrg 	      memset (&wi, 0, sizeof (wi));
139a2dc1f3fSmrg 	      wi.info = (void *)node->function_version ();
140a2dc1f3fSmrg 
141a2dc1f3fSmrg 	      if (dyn_cast<varpool_node *> (ref->referring))
142a2dc1f3fSmrg 		{
143a2dc1f3fSmrg 		  hash_set<tree> visited_nodes;
144a2dc1f3fSmrg 		  walk_tree (&DECL_INITIAL (ref->referring->decl),
145a2dc1f3fSmrg 			     replace_function_decl, &wi, &visited_nodes);
146a2dc1f3fSmrg 		}
147a2dc1f3fSmrg 	      else
148a2dc1f3fSmrg 		{
149a2dc1f3fSmrg 		  gimple_stmt_iterator it = gsi_for_stmt (ref->stmt);
150a2dc1f3fSmrg 		  if (ref->referring->decl != resolver_decl)
151a2dc1f3fSmrg 		    walk_gimple_stmt (&it, NULL, replace_function_decl, &wi);
152a2dc1f3fSmrg 		}
153a2dc1f3fSmrg 
154a2dc1f3fSmrg 	      symtab_node *source = ref->referring;
155a2dc1f3fSmrg 	      source->create_reference (inode, IPA_REF_ADDR);
156a2dc1f3fSmrg 	    }
157a2dc1f3fSmrg 	  else if (ref->use == IPA_REF_ALIAS)
158a2dc1f3fSmrg 	    {
159a2dc1f3fSmrg 	      symtab_node *source = ref->referring;
160a2dc1f3fSmrg 	      source->create_reference (inode, IPA_REF_ALIAS);
161a2dc1f3fSmrg 	      if (inode->get_comdat_group ())
162a2dc1f3fSmrg 		source->add_to_same_comdat_group (inode);
163a2dc1f3fSmrg 	    }
164a2dc1f3fSmrg 	  else
165a2dc1f3fSmrg 	    gcc_unreachable ();
166a2dc1f3fSmrg 	}
167a2dc1f3fSmrg     }
168a2dc1f3fSmrg 
169a2dc1f3fSmrg   symtab->change_decl_assembler_name (node->decl,
170c0a68be4Smrg 				      clone_function_name_numbered (
171c0a68be4Smrg 					  node->decl, "default"));
172a2dc1f3fSmrg 
173a2dc1f3fSmrg   /* FIXME: copy of cgraph_node::make_local that should be cleaned up
174a2dc1f3fSmrg 	    in next stage1.  */
175a2dc1f3fSmrg   node->make_decl_local ();
176a2dc1f3fSmrg   node->set_section (NULL);
177a2dc1f3fSmrg   node->set_comdat_group (NULL);
178a2dc1f3fSmrg   node->externally_visible = false;
179a2dc1f3fSmrg   node->forced_by_abi = false;
180a2dc1f3fSmrg   node->set_section (NULL);
181a2dc1f3fSmrg 
182a2dc1f3fSmrg   DECL_ARTIFICIAL (node->decl) = 1;
183a2dc1f3fSmrg   node->force_output = true;
1841debfc3dSmrg }
1851debfc3dSmrg 
1861debfc3dSmrg /* Return length of attribute names string,
1871debfc3dSmrg    if arglist chain > 1, -1 otherwise.  */
1881debfc3dSmrg 
1891debfc3dSmrg static int
get_attr_len(tree arglist)1901debfc3dSmrg get_attr_len (tree arglist)
1911debfc3dSmrg {
1921debfc3dSmrg   tree arg;
1931debfc3dSmrg   int str_len_sum = 0;
1941debfc3dSmrg   int argnum = 0;
1951debfc3dSmrg 
1961debfc3dSmrg   for (arg = arglist; arg; arg = TREE_CHAIN (arg))
1971debfc3dSmrg     {
1981debfc3dSmrg       const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
1991debfc3dSmrg       size_t len = strlen (str);
2001debfc3dSmrg       str_len_sum += len + 1;
2011debfc3dSmrg       for (const char *p = strchr (str, ','); p; p = strchr (p + 1, ','))
2021debfc3dSmrg 	argnum++;
2031debfc3dSmrg       argnum++;
2041debfc3dSmrg     }
2051debfc3dSmrg   if (argnum <= 1)
2061debfc3dSmrg     return -1;
2071debfc3dSmrg   return str_len_sum;
2081debfc3dSmrg }
2091debfc3dSmrg 
2101debfc3dSmrg /* Create string with attributes separated by comma.
2111debfc3dSmrg    Return number of attributes.  */
2121debfc3dSmrg 
2131debfc3dSmrg static int
get_attr_str(tree arglist,char * attr_str)2141debfc3dSmrg get_attr_str (tree arglist, char *attr_str)
2151debfc3dSmrg {
2161debfc3dSmrg   tree arg;
2171debfc3dSmrg   size_t str_len_sum = 0;
2181debfc3dSmrg   int argnum = 0;
2191debfc3dSmrg 
2201debfc3dSmrg   for (arg = arglist; arg; arg = TREE_CHAIN (arg))
2211debfc3dSmrg     {
2221debfc3dSmrg       const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
2231debfc3dSmrg       size_t len = strlen (str);
2241debfc3dSmrg       for (const char *p = strchr (str, ','); p; p = strchr (p + 1, ','))
2251debfc3dSmrg 	argnum++;
2261debfc3dSmrg       memcpy (attr_str + str_len_sum, str, len);
2271debfc3dSmrg       attr_str[str_len_sum + len] = TREE_CHAIN (arg) ? ',' : '\0';
2281debfc3dSmrg       str_len_sum += len + 1;
2291debfc3dSmrg       argnum++;
2301debfc3dSmrg     }
2311debfc3dSmrg   return argnum;
2321debfc3dSmrg }
2331debfc3dSmrg 
2341debfc3dSmrg /* Return number of attributes separated by comma and put them into ARGS.
235c0a68be4Smrg    If there is no DEFAULT attribute return -1.
236c0a68be4Smrg    If there is an empty string in attribute return -2.
237c0a68be4Smrg    If there are multiple DEFAULT attributes return -3.
238c0a68be4Smrg    */
2391debfc3dSmrg 
2401debfc3dSmrg static int
separate_attrs(char * attr_str,char ** attrs,int attrnum)241a2dc1f3fSmrg separate_attrs (char *attr_str, char **attrs, int attrnum)
2421debfc3dSmrg {
2431debfc3dSmrg   int i = 0;
244a2dc1f3fSmrg   int default_count = 0;
2451debfc3dSmrg 
2461debfc3dSmrg   for (char *attr = strtok (attr_str, ",");
2471debfc3dSmrg        attr != NULL; attr = strtok (NULL, ","))
2481debfc3dSmrg     {
2491debfc3dSmrg       if (strcmp (attr, "default") == 0)
2501debfc3dSmrg 	{
251a2dc1f3fSmrg 	  default_count++;
2521debfc3dSmrg 	  continue;
2531debfc3dSmrg 	}
2541debfc3dSmrg       attrs[i++] = attr;
2551debfc3dSmrg     }
256a2dc1f3fSmrg   if (default_count == 0)
2571debfc3dSmrg     return -1;
258c0a68be4Smrg   else if (default_count > 1)
259c0a68be4Smrg     return -3;
260a2dc1f3fSmrg   else if (i + default_count < attrnum)
261a2dc1f3fSmrg     return -2;
262a2dc1f3fSmrg 
2631debfc3dSmrg   return i;
2641debfc3dSmrg }
2651debfc3dSmrg 
2661debfc3dSmrg /*  Return true if symbol is valid in assembler name.  */
2671debfc3dSmrg 
2681debfc3dSmrg static bool
is_valid_asm_symbol(char c)2691debfc3dSmrg is_valid_asm_symbol (char c)
2701debfc3dSmrg {
2711debfc3dSmrg   if ('a' <= c && c <= 'z')
2721debfc3dSmrg     return true;
2731debfc3dSmrg   if ('A' <= c && c <= 'Z')
2741debfc3dSmrg     return true;
2751debfc3dSmrg   if ('0' <= c && c <= '9')
2761debfc3dSmrg     return true;
2771debfc3dSmrg   if (c == '_')
2781debfc3dSmrg     return true;
2791debfc3dSmrg   return false;
2801debfc3dSmrg }
2811debfc3dSmrg 
2821debfc3dSmrg /*  Replace all not valid assembler symbols with '_'.  */
2831debfc3dSmrg 
2841debfc3dSmrg static void
create_new_asm_name(char * old_asm_name,char * new_asm_name)2851debfc3dSmrg create_new_asm_name (char *old_asm_name, char *new_asm_name)
2861debfc3dSmrg {
2871debfc3dSmrg   int i;
2881debfc3dSmrg   int old_name_len = strlen (old_asm_name);
2891debfc3dSmrg 
2901debfc3dSmrg   /* Replace all not valid assembler symbols with '_'.  */
2911debfc3dSmrg   for (i = 0; i < old_name_len; i++)
2921debfc3dSmrg     if (!is_valid_asm_symbol (old_asm_name[i]))
2931debfc3dSmrg       new_asm_name[i] = '_';
2941debfc3dSmrg     else
2951debfc3dSmrg       new_asm_name[i] = old_asm_name[i];
2961debfc3dSmrg   new_asm_name[old_name_len] = '\0';
2971debfc3dSmrg }
2981debfc3dSmrg 
2991debfc3dSmrg /*  Creates target clone of NODE.  */
3001debfc3dSmrg 
3011debfc3dSmrg static cgraph_node *
create_target_clone(cgraph_node * node,bool definition,char * name,tree attributes)302a2dc1f3fSmrg create_target_clone (cgraph_node *node, bool definition, char *name,
303a2dc1f3fSmrg 		     tree attributes)
3041debfc3dSmrg {
3051debfc3dSmrg   cgraph_node *new_node;
3061debfc3dSmrg 
3071debfc3dSmrg   if (definition)
3081debfc3dSmrg     {
3091debfc3dSmrg       new_node = node->create_version_clone_with_body (vNULL, NULL,
3101debfc3dSmrg     						       NULL, NULL,
311*8feb0f0bSmrg 						       NULL, name, attributes);
312a2dc1f3fSmrg       if (new_node == NULL)
313a2dc1f3fSmrg 	return NULL;
3141debfc3dSmrg       new_node->force_output = true;
3151debfc3dSmrg     }
3161debfc3dSmrg   else
3171debfc3dSmrg     {
3181debfc3dSmrg       tree new_decl = copy_node (node->decl);
3191debfc3dSmrg       new_node = cgraph_node::get_create (new_decl);
320a2dc1f3fSmrg       DECL_ATTRIBUTES (new_decl) = attributes;
3211debfc3dSmrg       /* Generate a new name for the new version.  */
3221debfc3dSmrg       symtab->change_decl_assembler_name (new_node->decl,
323c0a68be4Smrg 					  clone_function_name_numbered (
324c0a68be4Smrg 					      node->decl, name));
3251debfc3dSmrg     }
3261debfc3dSmrg   return new_node;
3271debfc3dSmrg }
3281debfc3dSmrg 
3291debfc3dSmrg /* If the function in NODE has multiple target attributes
3301debfc3dSmrg    create the appropriate clone for each valid target attribute.  */
3311debfc3dSmrg 
3321debfc3dSmrg static bool
expand_target_clones(struct cgraph_node * node,bool definition)3331debfc3dSmrg expand_target_clones (struct cgraph_node *node, bool definition)
3341debfc3dSmrg {
3351debfc3dSmrg   int i;
3361debfc3dSmrg   /* Parsing target attributes separated by comma.  */
3371debfc3dSmrg   tree attr_target = lookup_attribute ("target_clones",
3381debfc3dSmrg 				       DECL_ATTRIBUTES (node->decl));
3391debfc3dSmrg   /* No targets specified.  */
3401debfc3dSmrg   if (!attr_target)
3411debfc3dSmrg     return false;
3421debfc3dSmrg 
3431debfc3dSmrg   tree arglist = TREE_VALUE (attr_target);
3441debfc3dSmrg   int attr_len = get_attr_len (arglist);
3451debfc3dSmrg 
3461debfc3dSmrg   /* No need to clone for 1 target attribute.  */
3471debfc3dSmrg   if (attr_len == -1)
3481debfc3dSmrg     {
3491debfc3dSmrg       warning_at (DECL_SOURCE_LOCATION (node->decl),
350c0a68be4Smrg 		  0, "single %<target_clones%> attribute is ignored");
351a2dc1f3fSmrg       return false;
352a2dc1f3fSmrg     }
353a2dc1f3fSmrg 
354a2dc1f3fSmrg   if (node->definition
355*8feb0f0bSmrg       && (node->alias || !tree_versionable_function_p (node->decl)))
356a2dc1f3fSmrg     {
357c0a68be4Smrg       auto_diagnostic_group d;
358a2dc1f3fSmrg       error_at (DECL_SOURCE_LOCATION (node->decl),
359a2dc1f3fSmrg 		"clones for %<target_clones%> attribute cannot be created");
360a2dc1f3fSmrg       const char *reason = NULL;
361a2dc1f3fSmrg       if (lookup_attribute ("noclone", DECL_ATTRIBUTES (node->decl)))
362a2dc1f3fSmrg 	reason = G_("function %q+F can never be copied "
363a2dc1f3fSmrg 		    "because it has %<noclone%> attribute");
364*8feb0f0bSmrg       else if (node->alias)
365*8feb0f0bSmrg 	reason
366*8feb0f0bSmrg 	  = "%<target_clones%> cannot be combined with %<alias%> attribute";
367a2dc1f3fSmrg       else
368a2dc1f3fSmrg 	reason = copy_forbidden (DECL_STRUCT_FUNCTION (node->decl));
369a2dc1f3fSmrg       if (reason)
370a2dc1f3fSmrg 	inform (DECL_SOURCE_LOCATION (node->decl), reason, node->decl);
3711debfc3dSmrg       return false;
3721debfc3dSmrg     }
3731debfc3dSmrg 
3741debfc3dSmrg   char *attr_str = XNEWVEC (char, attr_len);
3751debfc3dSmrg   int attrnum = get_attr_str (arglist, attr_str);
3761debfc3dSmrg   char **attrs = XNEWVEC (char *, attrnum);
3771debfc3dSmrg 
378a2dc1f3fSmrg   attrnum = separate_attrs (attr_str, attrs, attrnum);
379c0a68be4Smrg   switch (attrnum)
3801debfc3dSmrg     {
381c0a68be4Smrg     case -1:
3821debfc3dSmrg       error_at (DECL_SOURCE_LOCATION (node->decl),
383c0a68be4Smrg 		"%<default%> target was not set");
384c0a68be4Smrg       break;
385c0a68be4Smrg     case -2:
386a2dc1f3fSmrg       error_at (DECL_SOURCE_LOCATION (node->decl),
387a2dc1f3fSmrg 		"an empty string cannot be in %<target_clones%> attribute");
388c0a68be4Smrg       break;
389c0a68be4Smrg     case -3:
390c0a68be4Smrg       error_at (DECL_SOURCE_LOCATION (node->decl),
391c0a68be4Smrg 		"multiple %<default%> targets were set");
392c0a68be4Smrg       break;
393c0a68be4Smrg     default:
394c0a68be4Smrg       break;
395c0a68be4Smrg     }
396c0a68be4Smrg 
397c0a68be4Smrg   if (attrnum < 0)
398c0a68be4Smrg     {
399a2dc1f3fSmrg       XDELETEVEC (attrs);
400a2dc1f3fSmrg       XDELETEVEC (attr_str);
401a2dc1f3fSmrg       return false;
402a2dc1f3fSmrg     }
4031debfc3dSmrg 
4041debfc3dSmrg   cgraph_function_version_info *decl1_v = NULL;
4051debfc3dSmrg   cgraph_function_version_info *decl2_v = NULL;
4061debfc3dSmrg   cgraph_function_version_info *before = NULL;
4071debfc3dSmrg   cgraph_function_version_info *after = NULL;
4081debfc3dSmrg   decl1_v = node->function_version ();
4091debfc3dSmrg   if (decl1_v == NULL)
4101debfc3dSmrg     decl1_v = node->insert_new_function_version ();
4111debfc3dSmrg   before = decl1_v;
4121debfc3dSmrg   DECL_FUNCTION_VERSIONED (node->decl) = 1;
4131debfc3dSmrg 
4141debfc3dSmrg   for (i = 0; i < attrnum; i++)
4151debfc3dSmrg     {
4161debfc3dSmrg       char *attr = attrs[i];
4171debfc3dSmrg       char *suffix = XNEWVEC (char, strlen (attr) + 1);
4181debfc3dSmrg 
4191debfc3dSmrg       create_new_asm_name (attr, suffix);
4201debfc3dSmrg       /* Create new target clone.  */
421a2dc1f3fSmrg       tree attributes = make_attribute ("target", attr,
422a2dc1f3fSmrg 					DECL_ATTRIBUTES (node->decl));
423a2dc1f3fSmrg 
424a2dc1f3fSmrg       cgraph_node *new_node = create_target_clone (node, definition, suffix,
425a2dc1f3fSmrg 						   attributes);
426a2dc1f3fSmrg       if (new_node == NULL)
427a2dc1f3fSmrg 	return false;
428*8feb0f0bSmrg       new_node->local = false;
4291debfc3dSmrg       XDELETEVEC (suffix);
4301debfc3dSmrg 
4311debfc3dSmrg       decl2_v = new_node->function_version ();
4321debfc3dSmrg       if (decl2_v != NULL)
4331debfc3dSmrg         continue;
4341debfc3dSmrg       decl2_v = new_node->insert_new_function_version ();
4351debfc3dSmrg 
4361debfc3dSmrg       /* Chain decl2_v and decl1_v.  All semantically identical versions
4371debfc3dSmrg 	 will be chained together.  */
4381debfc3dSmrg       after = decl2_v;
4391debfc3dSmrg       while (before->next != NULL)
4401debfc3dSmrg 	before = before->next;
4411debfc3dSmrg       while (after->prev != NULL)
4421debfc3dSmrg 	after = after->prev;
4431debfc3dSmrg 
4441debfc3dSmrg       before->next = after;
4451debfc3dSmrg       after->prev = before;
4461debfc3dSmrg       DECL_FUNCTION_VERSIONED (new_node->decl) = 1;
4471debfc3dSmrg     }
4481debfc3dSmrg 
4491debfc3dSmrg   XDELETEVEC (attrs);
4501debfc3dSmrg   XDELETEVEC (attr_str);
4511debfc3dSmrg 
4521debfc3dSmrg   /* Setting new attribute to initial function.  */
4531debfc3dSmrg   tree attributes = make_attribute ("target", "default",
4541debfc3dSmrg 				    DECL_ATTRIBUTES (node->decl));
4551debfc3dSmrg   DECL_ATTRIBUTES (node->decl) = attributes;
456*8feb0f0bSmrg   node->local = false;
457a2dc1f3fSmrg   return true;
4581debfc3dSmrg }
4591debfc3dSmrg 
460c0a68be4Smrg /* When NODE is a target clone, consider all callees and redirect
461c0a68be4Smrg    to a clone with equal target attributes.  That prevents multiple
462c0a68be4Smrg    multi-versioning dispatches and a call-chain can be optimized.  */
463c0a68be4Smrg 
464c0a68be4Smrg static void
redirect_to_specific_clone(cgraph_node * node)465c0a68be4Smrg redirect_to_specific_clone (cgraph_node *node)
466c0a68be4Smrg {
467c0a68be4Smrg   cgraph_function_version_info *fv = node->function_version ();
468c0a68be4Smrg   if (fv == NULL)
469c0a68be4Smrg     return;
470c0a68be4Smrg 
471c0a68be4Smrg   tree attr_target = lookup_attribute ("target", DECL_ATTRIBUTES (node->decl));
472c0a68be4Smrg   if (attr_target == NULL_TREE)
473c0a68be4Smrg     return;
474c0a68be4Smrg 
475c0a68be4Smrg   /* We need to remember NEXT_CALLER as it could be modified in the loop.  */
476c0a68be4Smrg   for (cgraph_edge *e = node->callees; e ; e = e->next_callee)
477c0a68be4Smrg     {
478c0a68be4Smrg       cgraph_function_version_info *fv2 = e->callee->function_version ();
479c0a68be4Smrg       if (!fv2)
480c0a68be4Smrg 	continue;
481c0a68be4Smrg 
482c0a68be4Smrg       tree attr_target2 = lookup_attribute ("target",
483c0a68be4Smrg 					    DECL_ATTRIBUTES (e->callee->decl));
484c0a68be4Smrg 
485c0a68be4Smrg       /* Function is not calling proper target clone.  */
486c0a68be4Smrg       if (!attribute_list_equal (attr_target, attr_target2))
487c0a68be4Smrg 	{
488c0a68be4Smrg 	  while (fv2->prev != NULL)
489c0a68be4Smrg 	    fv2 = fv2->prev;
490c0a68be4Smrg 
491c0a68be4Smrg 	  /* Try to find a clone with equal target attribute.  */
492c0a68be4Smrg 	  for (; fv2 != NULL; fv2 = fv2->next)
493c0a68be4Smrg 	    {
494c0a68be4Smrg 	      cgraph_node *callee = fv2->this_node;
495c0a68be4Smrg 	      attr_target2 = lookup_attribute ("target",
496c0a68be4Smrg 					       DECL_ATTRIBUTES (callee->decl));
497c0a68be4Smrg 	      if (attribute_list_equal (attr_target, attr_target2))
498c0a68be4Smrg 		{
499c0a68be4Smrg 		  e->redirect_callee (callee);
500*8feb0f0bSmrg 		  cgraph_edge::redirect_call_stmt_to_callee (e);
501c0a68be4Smrg 		  break;
502c0a68be4Smrg 		}
503c0a68be4Smrg 	    }
504c0a68be4Smrg 	}
505c0a68be4Smrg     }
506c0a68be4Smrg }
507c0a68be4Smrg 
5081debfc3dSmrg static unsigned int
ipa_target_clone(void)5091debfc3dSmrg ipa_target_clone (void)
5101debfc3dSmrg {
5111debfc3dSmrg   struct cgraph_node *node;
512a2dc1f3fSmrg   auto_vec<cgraph_node *> to_dispatch;
5131debfc3dSmrg 
5141debfc3dSmrg   FOR_EACH_FUNCTION (node)
515a2dc1f3fSmrg     if (expand_target_clones (node, node->definition))
516a2dc1f3fSmrg       to_dispatch.safe_push (node);
5171debfc3dSmrg 
518a2dc1f3fSmrg   for (unsigned i = 0; i < to_dispatch.length (); i++)
519a2dc1f3fSmrg     create_dispatcher_calls (to_dispatch[i]);
5201debfc3dSmrg 
521c0a68be4Smrg   FOR_EACH_FUNCTION (node)
522c0a68be4Smrg     redirect_to_specific_clone (node);
523c0a68be4Smrg 
5241debfc3dSmrg   return 0;
5251debfc3dSmrg }
5261debfc3dSmrg 
5271debfc3dSmrg namespace {
5281debfc3dSmrg 
5291debfc3dSmrg const pass_data pass_data_target_clone =
5301debfc3dSmrg {
5311debfc3dSmrg   SIMPLE_IPA_PASS,		/* type */
5321debfc3dSmrg   "targetclone",		/* name */
5331debfc3dSmrg   OPTGROUP_NONE,		/* optinfo_flags */
5341debfc3dSmrg   TV_NONE,			/* tv_id */
5351debfc3dSmrg   ( PROP_ssa | PROP_cfg ),	/* properties_required */
5361debfc3dSmrg   0,				/* properties_provided */
5371debfc3dSmrg   0,				/* properties_destroyed */
5381debfc3dSmrg   0,				/* todo_flags_start */
5391debfc3dSmrg   TODO_update_ssa		/* todo_flags_finish */
5401debfc3dSmrg };
5411debfc3dSmrg 
5421debfc3dSmrg class pass_target_clone : public simple_ipa_opt_pass
5431debfc3dSmrg {
5441debfc3dSmrg public:
pass_target_clone(gcc::context * ctxt)5451debfc3dSmrg   pass_target_clone (gcc::context *ctxt)
5461debfc3dSmrg     : simple_ipa_opt_pass (pass_data_target_clone, ctxt)
5471debfc3dSmrg   {}
5481debfc3dSmrg 
5491debfc3dSmrg   /* opt_pass methods: */
5501debfc3dSmrg   virtual bool gate (function *);
execute(function *)5511debfc3dSmrg   virtual unsigned int execute (function *) { return ipa_target_clone (); }
5521debfc3dSmrg };
5531debfc3dSmrg 
5541debfc3dSmrg bool
gate(function *)5551debfc3dSmrg pass_target_clone::gate (function *)
5561debfc3dSmrg {
5571debfc3dSmrg   return true;
5581debfc3dSmrg }
5591debfc3dSmrg 
5601debfc3dSmrg } // anon namespace
5611debfc3dSmrg 
5621debfc3dSmrg simple_ipa_opt_pass *
make_pass_target_clone(gcc::context * ctxt)5631debfc3dSmrg make_pass_target_clone (gcc::context *ctxt)
5641debfc3dSmrg {
5651debfc3dSmrg   return new pass_target_clone (ctxt);
5661debfc3dSmrg }
567