1 /* Pass for parsing functions with multiple target attributes.
2 
3    Contributed by Evgeny Stupachenko <evstupac@gmail.com>
4 
5    Copyright (C) 2015-2018 Free Software Foundation, Inc.
6 
7 This file is part of GCC.
8 
9 GCC is free software; you can redistribute it and/or modify it under
10 the terms of the GNU General Public License as published by the Free
11 Software Foundation; either version 3, or (at your option) any later
12 version.
13 
14 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
17 for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with GCC; see the file COPYING3.  If not see
21 <http://www.gnu.org/licenses/>.  */
22 
23 #include "config.h"
24 #include "system.h"
25 #include "coretypes.h"
26 #include "backend.h"
27 #include "tree.h"
28 #include "stringpool.h"
29 #include "gimple.h"
30 #include "diagnostic-core.h"
31 #include "gimple-ssa.h"
32 #include "cgraph.h"
33 #include "tree-pass.h"
34 #include "target.h"
35 #include "attribs.h"
36 #include "pretty-print.h"
37 #include "gimple-iterator.h"
38 #include "gimple-walk.h"
39 #include "tree-inline.h"
40 #include "intl.h"
41 
42 /* Walker callback that replaces all FUNCTION_DECL of a function that's
43    going to be versioned.  */
44 
45 static tree
replace_function_decl(tree * op,int * walk_subtrees,void * data)46 replace_function_decl (tree *op, int *walk_subtrees, void *data)
47 {
48   struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
49   cgraph_function_version_info *info = (cgraph_function_version_info *)wi->info;
50 
51   if (TREE_CODE (*op) == FUNCTION_DECL
52       && info->this_node->decl == *op)
53     {
54       *op = info->dispatcher_resolver;
55       *walk_subtrees = 0;
56     }
57 
58   return NULL;
59 }
60 
61 /* If the call in NODE has multiple target attribute with multiple fields,
62    replace it with dispatcher call and create dispatcher (once).  */
63 
64 static void
create_dispatcher_calls(struct cgraph_node * node)65 create_dispatcher_calls (struct cgraph_node *node)
66 {
67   ipa_ref *ref;
68 
69   if (!DECL_FUNCTION_VERSIONED (node->decl)
70       || !is_function_default_version (node->decl))
71     return;
72 
73   if (!targetm.has_ifunc_p ())
74     {
75       error_at (DECL_SOURCE_LOCATION (node->decl),
76 		"the call requires ifunc, which is not"
77 		" supported by this target");
78       return;
79     }
80   else if (!targetm.get_function_versions_dispatcher)
81     {
82       error_at (DECL_SOURCE_LOCATION (node->decl),
83 		"target does not support function version dispatcher");
84       return;
85     }
86 
87   tree idecl = targetm.get_function_versions_dispatcher (node->decl);
88   if (!idecl)
89     {
90       error_at (DECL_SOURCE_LOCATION (node->decl),
91 		"default %<target_clones%> attribute was not set");
92       return;
93     }
94 
95   cgraph_node *inode = cgraph_node::get (idecl);
96   gcc_assert (inode);
97   tree resolver_decl = targetm.generate_version_dispatcher_body (inode);
98 
99   /* Update aliases.  */
100   inode->alias = true;
101   inode->alias_target = resolver_decl;
102   if (!inode->analyzed)
103     inode->resolve_alias (cgraph_node::get (resolver_decl));
104 
105   auto_vec<cgraph_edge *> edges_to_redirect;
106   /* We need to capture the references by value rather than just pointers to them
107      and remove them right away, as removing them later would invalidate what
108      some other reference pointers point to.  */
109   auto_vec<ipa_ref> references_to_redirect;
110 
111   while (node->iterate_referring (0, ref))
112     {
113       references_to_redirect.safe_push (*ref);
114       ref->remove_reference ();
115     }
116 
117   /* We need to remember NEXT_CALLER as it could be modified in the loop.  */
118   for (cgraph_edge *e = node->callers; e ; e = e->next_caller)
119     edges_to_redirect.safe_push (e);
120 
121   if (!edges_to_redirect.is_empty () || !references_to_redirect.is_empty ())
122     {
123       /* Redirect edges.  */
124       unsigned i;
125       cgraph_edge *e;
126       FOR_EACH_VEC_ELT (edges_to_redirect, i, e)
127 	{
128 	  e->redirect_callee (inode);
129 	  e->redirect_call_stmt_to_callee ();
130 	}
131 
132       /* Redirect references.  */
133       FOR_EACH_VEC_ELT (references_to_redirect, i, ref)
134 	{
135 	  if (ref->use == IPA_REF_ADDR)
136 	    {
137 	      struct walk_stmt_info wi;
138 	      memset (&wi, 0, sizeof (wi));
139 	      wi.info = (void *)node->function_version ();
140 
141 	      if (dyn_cast<varpool_node *> (ref->referring))
142 		{
143 		  hash_set<tree> visited_nodes;
144 		  walk_tree (&DECL_INITIAL (ref->referring->decl),
145 			     replace_function_decl, &wi, &visited_nodes);
146 		}
147 	      else
148 		{
149 		  gimple_stmt_iterator it = gsi_for_stmt (ref->stmt);
150 		  if (ref->referring->decl != resolver_decl)
151 		    walk_gimple_stmt (&it, NULL, replace_function_decl, &wi);
152 		}
153 
154 	      symtab_node *source = ref->referring;
155 	      source->create_reference (inode, IPA_REF_ADDR);
156 	    }
157 	  else if (ref->use == IPA_REF_ALIAS)
158 	    {
159 	      symtab_node *source = ref->referring;
160 	      source->create_reference (inode, IPA_REF_ALIAS);
161 	      if (inode->get_comdat_group ())
162 		source->add_to_same_comdat_group (inode);
163 	    }
164 	  else
165 	    gcc_unreachable ();
166 	}
167     }
168 
169   symtab->change_decl_assembler_name (node->decl,
170 				      clone_function_name (node->decl,
171 							   "default"));
172 
173   /* FIXME: copy of cgraph_node::make_local that should be cleaned up
174 	    in next stage1.  */
175   node->make_decl_local ();
176   node->set_section (NULL);
177   node->set_comdat_group (NULL);
178   node->externally_visible = false;
179   node->forced_by_abi = false;
180   node->set_section (NULL);
181   node->unique_name = ((node->resolution == LDPR_PREVAILING_DEF_IRONLY
182 			|| node->resolution == LDPR_PREVAILING_DEF_IRONLY_EXP)
183 		       && !flag_incremental_link);
184   node->resolution = LDPR_PREVAILING_DEF_IRONLY;
185 
186   DECL_ARTIFICIAL (node->decl) = 1;
187   node->force_output = true;
188 }
189 
190 /* Return length of attribute names string,
191    if arglist chain > 1, -1 otherwise.  */
192 
193 static int
get_attr_len(tree arglist)194 get_attr_len (tree arglist)
195 {
196   tree arg;
197   int str_len_sum = 0;
198   int argnum = 0;
199 
200   for (arg = arglist; arg; arg = TREE_CHAIN (arg))
201     {
202       const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
203       size_t len = strlen (str);
204       str_len_sum += len + 1;
205       for (const char *p = strchr (str, ','); p; p = strchr (p + 1, ','))
206 	argnum++;
207       argnum++;
208     }
209   if (argnum <= 1)
210     return -1;
211   return str_len_sum;
212 }
213 
214 /* Create string with attributes separated by comma.
215    Return number of attributes.  */
216 
217 static int
get_attr_str(tree arglist,char * attr_str)218 get_attr_str (tree arglist, char *attr_str)
219 {
220   tree arg;
221   size_t str_len_sum = 0;
222   int argnum = 0;
223 
224   for (arg = arglist; arg; arg = TREE_CHAIN (arg))
225     {
226       const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
227       size_t len = strlen (str);
228       for (const char *p = strchr (str, ','); p; p = strchr (p + 1, ','))
229 	argnum++;
230       memcpy (attr_str + str_len_sum, str, len);
231       attr_str[str_len_sum + len] = TREE_CHAIN (arg) ? ',' : '\0';
232       str_len_sum += len + 1;
233       argnum++;
234     }
235   return argnum;
236 }
237 
238 /* Return number of attributes separated by comma and put them into ARGS.
239    If there is no DEFAULT attribute return -1.  If there is an empty
240    string in attribute return -2.  */
241 
242 static int
separate_attrs(char * attr_str,char ** attrs,int attrnum)243 separate_attrs (char *attr_str, char **attrs, int attrnum)
244 {
245   int i = 0;
246   int default_count = 0;
247 
248   for (char *attr = strtok (attr_str, ",");
249        attr != NULL; attr = strtok (NULL, ","))
250     {
251       if (strcmp (attr, "default") == 0)
252 	{
253 	  default_count++;
254 	  continue;
255 	}
256       attrs[i++] = attr;
257     }
258   if (default_count == 0)
259     return -1;
260   else if (i + default_count < attrnum)
261     return -2;
262 
263   return i;
264 }
265 
266 /*  Return true if symbol is valid in assembler name.  */
267 
268 static bool
is_valid_asm_symbol(char c)269 is_valid_asm_symbol (char c)
270 {
271   if ('a' <= c && c <= 'z')
272     return true;
273   if ('A' <= c && c <= 'Z')
274     return true;
275   if ('0' <= c && c <= '9')
276     return true;
277   if (c == '_')
278     return true;
279   return false;
280 }
281 
282 /*  Replace all not valid assembler symbols with '_'.  */
283 
284 static void
create_new_asm_name(char * old_asm_name,char * new_asm_name)285 create_new_asm_name (char *old_asm_name, char *new_asm_name)
286 {
287   int i;
288   int old_name_len = strlen (old_asm_name);
289 
290   /* Replace all not valid assembler symbols with '_'.  */
291   for (i = 0; i < old_name_len; i++)
292     if (!is_valid_asm_symbol (old_asm_name[i]))
293       new_asm_name[i] = '_';
294     else
295       new_asm_name[i] = old_asm_name[i];
296   new_asm_name[old_name_len] = '\0';
297 }
298 
299 /*  Creates target clone of NODE.  */
300 
301 static cgraph_node *
create_target_clone(cgraph_node * node,bool definition,char * name,tree attributes)302 create_target_clone (cgraph_node *node, bool definition, char *name,
303 		     tree attributes)
304 {
305   cgraph_node *new_node;
306 
307   if (definition)
308     {
309       new_node = node->create_version_clone_with_body (vNULL, NULL,
310     						       NULL, false,
311 						       NULL, NULL,
312 						       name, attributes);
313       if (new_node == NULL)
314 	return NULL;
315       new_node->force_output = true;
316     }
317   else
318     {
319       tree new_decl = copy_node (node->decl);
320       new_node = cgraph_node::get_create (new_decl);
321       DECL_ATTRIBUTES (new_decl) = attributes;
322       /* Generate a new name for the new version.  */
323       symtab->change_decl_assembler_name (new_node->decl,
324 					  clone_function_name (node->decl,
325 							       name));
326     }
327   return new_node;
328 }
329 
330 /* If the function in NODE has multiple target attributes
331    create the appropriate clone for each valid target attribute.  */
332 
333 static bool
expand_target_clones(struct cgraph_node * node,bool definition)334 expand_target_clones (struct cgraph_node *node, bool definition)
335 {
336   int i;
337   /* Parsing target attributes separated by comma.  */
338   tree attr_target = lookup_attribute ("target_clones",
339 				       DECL_ATTRIBUTES (node->decl));
340   /* No targets specified.  */
341   if (!attr_target)
342     return false;
343 
344   tree arglist = TREE_VALUE (attr_target);
345   int attr_len = get_attr_len (arglist);
346 
347   /* No need to clone for 1 target attribute.  */
348   if (attr_len == -1)
349     {
350       warning_at (DECL_SOURCE_LOCATION (node->decl),
351 		  0,
352 		  "single %<target_clones%> attribute is ignored");
353       return false;
354     }
355 
356   if (node->definition
357       && !tree_versionable_function_p (node->decl))
358     {
359       error_at (DECL_SOURCE_LOCATION (node->decl),
360 		"clones for %<target_clones%> attribute cannot be created");
361       const char *reason = NULL;
362       if (lookup_attribute ("noclone", DECL_ATTRIBUTES (node->decl)))
363 	reason = G_("function %q+F can never be copied "
364 		    "because it has %<noclone%> attribute");
365       else
366 	reason = copy_forbidden (DECL_STRUCT_FUNCTION (node->decl));
367       if (reason)
368 	inform (DECL_SOURCE_LOCATION (node->decl), reason, node->decl);
369       return false;
370     }
371 
372   char *attr_str = XNEWVEC (char, attr_len);
373   int attrnum = get_attr_str (arglist, attr_str);
374   char **attrs = XNEWVEC (char *, attrnum);
375 
376   attrnum = separate_attrs (attr_str, attrs, attrnum);
377   if (attrnum == -1)
378     {
379       error_at (DECL_SOURCE_LOCATION (node->decl),
380 		"default target was not set");
381       XDELETEVEC (attrs);
382       XDELETEVEC (attr_str);
383       return false;
384     }
385   else if (attrnum == -2)
386     {
387       error_at (DECL_SOURCE_LOCATION (node->decl),
388 		"an empty string cannot be in %<target_clones%> attribute");
389       XDELETEVEC (attrs);
390       XDELETEVEC (attr_str);
391       return false;
392     }
393 
394   cgraph_function_version_info *decl1_v = NULL;
395   cgraph_function_version_info *decl2_v = NULL;
396   cgraph_function_version_info *before = NULL;
397   cgraph_function_version_info *after = NULL;
398   decl1_v = node->function_version ();
399   if (decl1_v == NULL)
400     decl1_v = node->insert_new_function_version ();
401   before = decl1_v;
402   DECL_FUNCTION_VERSIONED (node->decl) = 1;
403 
404   for (i = 0; i < attrnum; i++)
405     {
406       char *attr = attrs[i];
407       char *suffix = XNEWVEC (char, strlen (attr) + 1);
408 
409       create_new_asm_name (attr, suffix);
410       /* Create new target clone.  */
411       tree attributes = make_attribute ("target", attr,
412 					DECL_ATTRIBUTES (node->decl));
413 
414       cgraph_node *new_node = create_target_clone (node, definition, suffix,
415 						   attributes);
416       if (new_node == NULL)
417 	return false;
418       new_node->local.local = false;
419       XDELETEVEC (suffix);
420 
421       decl2_v = new_node->function_version ();
422       if (decl2_v != NULL)
423         continue;
424       decl2_v = new_node->insert_new_function_version ();
425 
426       /* Chain decl2_v and decl1_v.  All semantically identical versions
427 	 will be chained together.  */
428       after = decl2_v;
429       while (before->next != NULL)
430 	before = before->next;
431       while (after->prev != NULL)
432 	after = after->prev;
433 
434       before->next = after;
435       after->prev = before;
436       DECL_FUNCTION_VERSIONED (new_node->decl) = 1;
437     }
438 
439   XDELETEVEC (attrs);
440   XDELETEVEC (attr_str);
441 
442   /* Setting new attribute to initial function.  */
443   tree attributes = make_attribute ("target", "default",
444 				    DECL_ATTRIBUTES (node->decl));
445   DECL_ATTRIBUTES (node->decl) = attributes;
446   node->local.local = false;
447   return true;
448 }
449 
450 static unsigned int
ipa_target_clone(void)451 ipa_target_clone (void)
452 {
453   struct cgraph_node *node;
454   auto_vec<cgraph_node *> to_dispatch;
455 
456   FOR_EACH_FUNCTION (node)
457     if (expand_target_clones (node, node->definition))
458       to_dispatch.safe_push (node);
459 
460   for (unsigned i = 0; i < to_dispatch.length (); i++)
461     create_dispatcher_calls (to_dispatch[i]);
462 
463   return 0;
464 }
465 
466 namespace {
467 
468 const pass_data pass_data_target_clone =
469 {
470   SIMPLE_IPA_PASS,		/* type */
471   "targetclone",		/* name */
472   OPTGROUP_NONE,		/* optinfo_flags */
473   TV_NONE,			/* tv_id */
474   ( PROP_ssa | PROP_cfg ),	/* properties_required */
475   0,				/* properties_provided */
476   0,				/* properties_destroyed */
477   0,				/* todo_flags_start */
478   TODO_update_ssa		/* todo_flags_finish */
479 };
480 
481 class pass_target_clone : public simple_ipa_opt_pass
482 {
483 public:
pass_target_clone(gcc::context * ctxt)484   pass_target_clone (gcc::context *ctxt)
485     : simple_ipa_opt_pass (pass_data_target_clone, ctxt)
486   {}
487 
488   /* opt_pass methods: */
489   virtual bool gate (function *);
execute(function *)490   virtual unsigned int execute (function *) { return ipa_target_clone (); }
491 };
492 
493 bool
gate(function *)494 pass_target_clone::gate (function *)
495 {
496   return true;
497 }
498 
499 } // anon namespace
500 
501 simple_ipa_opt_pass *
make_pass_target_clone(gcc::context * ctxt)502 make_pass_target_clone (gcc::context *ctxt)
503 {
504   return new pass_target_clone (ctxt);
505 }
506