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