1 /* Pass for parsing functions with multiple target attributes.
2 
3    Contributed by Evgeny Stupachenko <evstupac@gmail.com>
4 
5    Copyright (C) 2015-2021 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 	  cgraph_edge::redirect_call_stmt_to_callee (e);
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_numbered (
171 					  node->decl, "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 
182   DECL_ARTIFICIAL (node->decl) = 1;
183   node->force_output = true;
184 }
185 
186 /* Return length of attribute names string,
187    if arglist chain > 1, -1 otherwise.  */
188 
189 static int
get_attr_len(tree arglist)190 get_attr_len (tree arglist)
191 {
192   tree arg;
193   int str_len_sum = 0;
194   int argnum = 0;
195 
196   for (arg = arglist; arg; arg = TREE_CHAIN (arg))
197     {
198       const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
199       size_t len = strlen (str);
200       str_len_sum += len + 1;
201       for (const char *p = strchr (str, ','); p; p = strchr (p + 1, ','))
202 	argnum++;
203       argnum++;
204     }
205   if (argnum <= 1)
206     return -1;
207   return str_len_sum;
208 }
209 
210 /* Create string with attributes separated by comma.
211    Return number of attributes.  */
212 
213 static int
get_attr_str(tree arglist,char * attr_str)214 get_attr_str (tree arglist, char *attr_str)
215 {
216   tree arg;
217   size_t str_len_sum = 0;
218   int argnum = 0;
219 
220   for (arg = arglist; arg; arg = TREE_CHAIN (arg))
221     {
222       const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
223       size_t len = strlen (str);
224       for (const char *p = strchr (str, ','); p; p = strchr (p + 1, ','))
225 	argnum++;
226       memcpy (attr_str + str_len_sum, str, len);
227       attr_str[str_len_sum + len] = TREE_CHAIN (arg) ? ',' : '\0';
228       str_len_sum += len + 1;
229       argnum++;
230     }
231   return argnum;
232 }
233 
234 /* Return number of attributes separated by comma and put them into ARGS.
235    If there is no DEFAULT attribute return -1.
236    If there is an empty string in attribute return -2.
237    If there are multiple DEFAULT attributes return -3.
238    */
239 
240 static int
separate_attrs(char * attr_str,char ** attrs,int attrnum)241 separate_attrs (char *attr_str, char **attrs, int attrnum)
242 {
243   int i = 0;
244   int default_count = 0;
245 
246   for (char *attr = strtok (attr_str, ",");
247        attr != NULL; attr = strtok (NULL, ","))
248     {
249       if (strcmp (attr, "default") == 0)
250 	{
251 	  default_count++;
252 	  continue;
253 	}
254       attrs[i++] = attr;
255     }
256   if (default_count == 0)
257     return -1;
258   else if (default_count > 1)
259     return -3;
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, NULL,
311 						       NULL, name, attributes);
312       if (new_node == NULL)
313 	return NULL;
314       new_node->force_output = true;
315     }
316   else
317     {
318       tree new_decl = copy_node (node->decl);
319       new_node = cgraph_node::get_create (new_decl);
320       DECL_ATTRIBUTES (new_decl) = attributes;
321       /* Generate a new name for the new version.  */
322       symtab->change_decl_assembler_name (new_node->decl,
323 					  clone_function_name_numbered (
324 					      node->decl, name));
325     }
326   return new_node;
327 }
328 
329 /* If the function in NODE has multiple target attributes
330    create the appropriate clone for each valid target attribute.  */
331 
332 static bool
expand_target_clones(struct cgraph_node * node,bool definition)333 expand_target_clones (struct cgraph_node *node, bool definition)
334 {
335   int i;
336   /* Parsing target attributes separated by comma.  */
337   tree attr_target = lookup_attribute ("target_clones",
338 				       DECL_ATTRIBUTES (node->decl));
339   /* No targets specified.  */
340   if (!attr_target)
341     return false;
342 
343   tree arglist = TREE_VALUE (attr_target);
344   int attr_len = get_attr_len (arglist);
345 
346   /* No need to clone for 1 target attribute.  */
347   if (attr_len == -1)
348     {
349       warning_at (DECL_SOURCE_LOCATION (node->decl),
350 		  0, "single %<target_clones%> attribute is ignored");
351       return false;
352     }
353 
354   if (node->definition
355       && (node->alias || !tree_versionable_function_p (node->decl)))
356     {
357       auto_diagnostic_group d;
358       error_at (DECL_SOURCE_LOCATION (node->decl),
359 		"clones for %<target_clones%> attribute cannot be created");
360       const char *reason = NULL;
361       if (lookup_attribute ("noclone", DECL_ATTRIBUTES (node->decl)))
362 	reason = G_("function %q+F can never be copied "
363 		    "because it has %<noclone%> attribute");
364       else if (node->alias)
365 	reason
366 	  = "%<target_clones%> cannot be combined with %<alias%> attribute";
367       else
368 	reason = copy_forbidden (DECL_STRUCT_FUNCTION (node->decl));
369       if (reason)
370 	inform (DECL_SOURCE_LOCATION (node->decl), reason, node->decl);
371       return false;
372     }
373 
374   char *attr_str = XNEWVEC (char, attr_len);
375   int attrnum = get_attr_str (arglist, attr_str);
376   char **attrs = XNEWVEC (char *, attrnum);
377 
378   attrnum = separate_attrs (attr_str, attrs, attrnum);
379   switch (attrnum)
380     {
381     case -1:
382       error_at (DECL_SOURCE_LOCATION (node->decl),
383 		"%<default%> target was not set");
384       break;
385     case -2:
386       error_at (DECL_SOURCE_LOCATION (node->decl),
387 		"an empty string cannot be in %<target_clones%> attribute");
388       break;
389     case -3:
390       error_at (DECL_SOURCE_LOCATION (node->decl),
391 		"multiple %<default%> targets were set");
392       break;
393     default:
394       break;
395     }
396 
397   if (attrnum < 0)
398     {
399       XDELETEVEC (attrs);
400       XDELETEVEC (attr_str);
401       return false;
402     }
403 
404   cgraph_function_version_info *decl1_v = NULL;
405   cgraph_function_version_info *decl2_v = NULL;
406   cgraph_function_version_info *before = NULL;
407   cgraph_function_version_info *after = NULL;
408   decl1_v = node->function_version ();
409   if (decl1_v == NULL)
410     decl1_v = node->insert_new_function_version ();
411   before = decl1_v;
412   DECL_FUNCTION_VERSIONED (node->decl) = 1;
413 
414   for (i = 0; i < attrnum; i++)
415     {
416       char *attr = attrs[i];
417       char *suffix = XNEWVEC (char, strlen (attr) + 1);
418 
419       create_new_asm_name (attr, suffix);
420       /* Create new target clone.  */
421       tree attributes = make_attribute ("target", attr,
422 					DECL_ATTRIBUTES (node->decl));
423 
424       cgraph_node *new_node = create_target_clone (node, definition, suffix,
425 						   attributes);
426       if (new_node == NULL)
427 	return false;
428       new_node->local = false;
429       XDELETEVEC (suffix);
430 
431       decl2_v = new_node->function_version ();
432       if (decl2_v != NULL)
433         continue;
434       decl2_v = new_node->insert_new_function_version ();
435 
436       /* Chain decl2_v and decl1_v.  All semantically identical versions
437 	 will be chained together.  */
438       after = decl2_v;
439       while (before->next != NULL)
440 	before = before->next;
441       while (after->prev != NULL)
442 	after = after->prev;
443 
444       before->next = after;
445       after->prev = before;
446       DECL_FUNCTION_VERSIONED (new_node->decl) = 1;
447     }
448 
449   XDELETEVEC (attrs);
450   XDELETEVEC (attr_str);
451 
452   /* Setting new attribute to initial function.  */
453   tree attributes = make_attribute ("target", "default",
454 				    DECL_ATTRIBUTES (node->decl));
455   DECL_ATTRIBUTES (node->decl) = attributes;
456   node->local = false;
457   return true;
458 }
459 
460 /* When NODE is a target clone, consider all callees and redirect
461    to a clone with equal target attributes.  That prevents multiple
462    multi-versioning dispatches and a call-chain can be optimized.  */
463 
464 static void
redirect_to_specific_clone(cgraph_node * node)465 redirect_to_specific_clone (cgraph_node *node)
466 {
467   cgraph_function_version_info *fv = node->function_version ();
468   if (fv == NULL)
469     return;
470 
471   tree attr_target = lookup_attribute ("target", DECL_ATTRIBUTES (node->decl));
472   if (attr_target == NULL_TREE)
473     return;
474 
475   /* We need to remember NEXT_CALLER as it could be modified in the loop.  */
476   for (cgraph_edge *e = node->callees; e ; e = e->next_callee)
477     {
478       cgraph_function_version_info *fv2 = e->callee->function_version ();
479       if (!fv2)
480 	continue;
481 
482       tree attr_target2 = lookup_attribute ("target",
483 					    DECL_ATTRIBUTES (e->callee->decl));
484 
485       /* Function is not calling proper target clone.  */
486       if (attr_target2 == NULL_TREE
487 	  || !attribute_value_equal (attr_target, attr_target2))
488 	{
489 	  while (fv2->prev != NULL)
490 	    fv2 = fv2->prev;
491 
492 	  /* Try to find a clone with equal target attribute.  */
493 	  for (; fv2 != NULL; fv2 = fv2->next)
494 	    {
495 	      cgraph_node *callee = fv2->this_node;
496 	      attr_target2 = lookup_attribute ("target",
497 					       DECL_ATTRIBUTES (callee->decl));
498 	      if (attr_target2 != NULL_TREE
499 		  && attribute_value_equal (attr_target, attr_target2))
500 		{
501 		  e->redirect_callee (callee);
502 		  cgraph_edge::redirect_call_stmt_to_callee (e);
503 		  break;
504 		}
505 	    }
506 	}
507     }
508 }
509 
510 static unsigned int
ipa_target_clone(void)511 ipa_target_clone (void)
512 {
513   struct cgraph_node *node;
514   auto_vec<cgraph_node *> to_dispatch;
515 
516   FOR_EACH_FUNCTION (node)
517     if (expand_target_clones (node, node->definition))
518       to_dispatch.safe_push (node);
519 
520   for (unsigned i = 0; i < to_dispatch.length (); i++)
521     create_dispatcher_calls (to_dispatch[i]);
522 
523   FOR_EACH_FUNCTION (node)
524     redirect_to_specific_clone (node);
525 
526   return 0;
527 }
528 
529 namespace {
530 
531 const pass_data pass_data_target_clone =
532 {
533   SIMPLE_IPA_PASS,		/* type */
534   "targetclone",		/* name */
535   OPTGROUP_NONE,		/* optinfo_flags */
536   TV_NONE,			/* tv_id */
537   ( PROP_ssa | PROP_cfg ),	/* properties_required */
538   0,				/* properties_provided */
539   0,				/* properties_destroyed */
540   0,				/* todo_flags_start */
541   TODO_update_ssa		/* todo_flags_finish */
542 };
543 
544 class pass_target_clone : public simple_ipa_opt_pass
545 {
546 public:
pass_target_clone(gcc::context * ctxt)547   pass_target_clone (gcc::context *ctxt)
548     : simple_ipa_opt_pass (pass_data_target_clone, ctxt)
549   {}
550 
551   /* opt_pass methods: */
552   virtual bool gate (function *);
execute(function *)553   virtual unsigned int execute (function *) { return ipa_target_clone (); }
554 };
555 
556 bool
gate(function *)557 pass_target_clone::gate (function *)
558 {
559   return true;
560 }
561 
562 } // anon namespace
563 
564 simple_ipa_opt_pass *
make_pass_target_clone(gcc::context * ctxt)565 make_pass_target_clone (gcc::context *ctxt)
566 {
567   return new pass_target_clone (ctxt);
568 }
569