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