xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/multiple_target.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /* Pass for parsing functions with multiple target attributes.
2 
3    Contributed by Evgeny Stupachenko <evstupac@gmail.com>
4 
5    Copyright (C) 2015-2019 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
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
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_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   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
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
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.
240    If there is an empty string in attribute return -2.
241    If there are multiple DEFAULT attributes return -3.
242    */
243 
244 static int
245 separate_attrs (char *attr_str, char **attrs, int attrnum)
246 {
247   int i = 0;
248   int default_count = 0;
249 
250   for (char *attr = strtok (attr_str, ",");
251        attr != NULL; attr = strtok (NULL, ","))
252     {
253       if (strcmp (attr, "default") == 0)
254 	{
255 	  default_count++;
256 	  continue;
257 	}
258       attrs[i++] = attr;
259     }
260   if (default_count == 0)
261     return -1;
262   else if (default_count > 1)
263     return -3;
264   else if (i + default_count < attrnum)
265     return -2;
266 
267   return i;
268 }
269 
270 /*  Return true if symbol is valid in assembler name.  */
271 
272 static bool
273 is_valid_asm_symbol (char c)
274 {
275   if ('a' <= c && c <= 'z')
276     return true;
277   if ('A' <= c && c <= 'Z')
278     return true;
279   if ('0' <= c && c <= '9')
280     return true;
281   if (c == '_')
282     return true;
283   return false;
284 }
285 
286 /*  Replace all not valid assembler symbols with '_'.  */
287 
288 static void
289 create_new_asm_name (char *old_asm_name, char *new_asm_name)
290 {
291   int i;
292   int old_name_len = strlen (old_asm_name);
293 
294   /* Replace all not valid assembler symbols with '_'.  */
295   for (i = 0; i < old_name_len; i++)
296     if (!is_valid_asm_symbol (old_asm_name[i]))
297       new_asm_name[i] = '_';
298     else
299       new_asm_name[i] = old_asm_name[i];
300   new_asm_name[old_name_len] = '\0';
301 }
302 
303 /*  Creates target clone of NODE.  */
304 
305 static cgraph_node *
306 create_target_clone (cgraph_node *node, bool definition, char *name,
307 		     tree attributes)
308 {
309   cgraph_node *new_node;
310 
311   if (definition)
312     {
313       new_node = node->create_version_clone_with_body (vNULL, NULL,
314     						       NULL, false,
315 						       NULL, NULL,
316 						       name, attributes);
317       if (new_node == NULL)
318 	return NULL;
319       new_node->force_output = true;
320     }
321   else
322     {
323       tree new_decl = copy_node (node->decl);
324       new_node = cgraph_node::get_create (new_decl);
325       DECL_ATTRIBUTES (new_decl) = attributes;
326       /* Generate a new name for the new version.  */
327       symtab->change_decl_assembler_name (new_node->decl,
328 					  clone_function_name_numbered (
329 					      node->decl, name));
330     }
331   return new_node;
332 }
333 
334 /* If the function in NODE has multiple target attributes
335    create the appropriate clone for each valid target attribute.  */
336 
337 static bool
338 expand_target_clones (struct cgraph_node *node, bool definition)
339 {
340   int i;
341   /* Parsing target attributes separated by comma.  */
342   tree attr_target = lookup_attribute ("target_clones",
343 				       DECL_ATTRIBUTES (node->decl));
344   /* No targets specified.  */
345   if (!attr_target)
346     return false;
347 
348   tree arglist = TREE_VALUE (attr_target);
349   int attr_len = get_attr_len (arglist);
350 
351   /* No need to clone for 1 target attribute.  */
352   if (attr_len == -1)
353     {
354       warning_at (DECL_SOURCE_LOCATION (node->decl),
355 		  0, "single %<target_clones%> attribute is ignored");
356       return false;
357     }
358 
359   if (node->definition
360       && !tree_versionable_function_p (node->decl))
361     {
362       auto_diagnostic_group d;
363       error_at (DECL_SOURCE_LOCATION (node->decl),
364 		"clones for %<target_clones%> attribute cannot be created");
365       const char *reason = NULL;
366       if (lookup_attribute ("noclone", DECL_ATTRIBUTES (node->decl)))
367 	reason = G_("function %q+F can never be copied "
368 		    "because it has %<noclone%> attribute");
369       else
370 	reason = copy_forbidden (DECL_STRUCT_FUNCTION (node->decl));
371       if (reason)
372 	inform (DECL_SOURCE_LOCATION (node->decl), reason, node->decl);
373       return false;
374     }
375 
376   char *attr_str = XNEWVEC (char, attr_len);
377   int attrnum = get_attr_str (arglist, attr_str);
378   char **attrs = XNEWVEC (char *, attrnum);
379 
380   attrnum = separate_attrs (attr_str, attrs, attrnum);
381   switch (attrnum)
382     {
383     case -1:
384       error_at (DECL_SOURCE_LOCATION (node->decl),
385 		"%<default%> target was not set");
386       break;
387     case -2:
388       error_at (DECL_SOURCE_LOCATION (node->decl),
389 		"an empty string cannot be in %<target_clones%> attribute");
390       break;
391     case -3:
392       error_at (DECL_SOURCE_LOCATION (node->decl),
393 		"multiple %<default%> targets were set");
394       break;
395     default:
396       break;
397     }
398 
399   if (attrnum < 0)
400     {
401       XDELETEVEC (attrs);
402       XDELETEVEC (attr_str);
403       return false;
404     }
405 
406   cgraph_function_version_info *decl1_v = NULL;
407   cgraph_function_version_info *decl2_v = NULL;
408   cgraph_function_version_info *before = NULL;
409   cgraph_function_version_info *after = NULL;
410   decl1_v = node->function_version ();
411   if (decl1_v == NULL)
412     decl1_v = node->insert_new_function_version ();
413   before = decl1_v;
414   DECL_FUNCTION_VERSIONED (node->decl) = 1;
415 
416   for (i = 0; i < attrnum; i++)
417     {
418       char *attr = attrs[i];
419       char *suffix = XNEWVEC (char, strlen (attr) + 1);
420 
421       create_new_asm_name (attr, suffix);
422       /* Create new target clone.  */
423       tree attributes = make_attribute ("target", attr,
424 					DECL_ATTRIBUTES (node->decl));
425 
426       cgraph_node *new_node = create_target_clone (node, definition, suffix,
427 						   attributes);
428       if (new_node == NULL)
429 	return false;
430       new_node->local.local = false;
431       XDELETEVEC (suffix);
432 
433       decl2_v = new_node->function_version ();
434       if (decl2_v != NULL)
435         continue;
436       decl2_v = new_node->insert_new_function_version ();
437 
438       /* Chain decl2_v and decl1_v.  All semantically identical versions
439 	 will be chained together.  */
440       after = decl2_v;
441       while (before->next != NULL)
442 	before = before->next;
443       while (after->prev != NULL)
444 	after = after->prev;
445 
446       before->next = after;
447       after->prev = before;
448       DECL_FUNCTION_VERSIONED (new_node->decl) = 1;
449     }
450 
451   XDELETEVEC (attrs);
452   XDELETEVEC (attr_str);
453 
454   /* Setting new attribute to initial function.  */
455   tree attributes = make_attribute ("target", "default",
456 				    DECL_ATTRIBUTES (node->decl));
457   DECL_ATTRIBUTES (node->decl) = attributes;
458   node->local.local = false;
459   return true;
460 }
461 
462 /* When NODE is a target clone, consider all callees and redirect
463    to a clone with equal target attributes.  That prevents multiple
464    multi-versioning dispatches and a call-chain can be optimized.  */
465 
466 static void
467 redirect_to_specific_clone (cgraph_node *node)
468 {
469   cgraph_function_version_info *fv = node->function_version ();
470   if (fv == NULL)
471     return;
472 
473   tree attr_target = lookup_attribute ("target", DECL_ATTRIBUTES (node->decl));
474   if (attr_target == NULL_TREE)
475     return;
476 
477   /* We need to remember NEXT_CALLER as it could be modified in the loop.  */
478   for (cgraph_edge *e = node->callees; e ; e = e->next_callee)
479     {
480       cgraph_function_version_info *fv2 = e->callee->function_version ();
481       if (!fv2)
482 	continue;
483 
484       tree attr_target2 = lookup_attribute ("target",
485 					    DECL_ATTRIBUTES (e->callee->decl));
486 
487       /* Function is not calling proper target clone.  */
488       if (!attribute_list_equal (attr_target, attr_target2))
489 	{
490 	  while (fv2->prev != NULL)
491 	    fv2 = fv2->prev;
492 
493 	  /* Try to find a clone with equal target attribute.  */
494 	  for (; fv2 != NULL; fv2 = fv2->next)
495 	    {
496 	      cgraph_node *callee = fv2->this_node;
497 	      attr_target2 = lookup_attribute ("target",
498 					       DECL_ATTRIBUTES (callee->decl));
499 	      if (attribute_list_equal (attr_target, attr_target2))
500 		{
501 		  e->redirect_callee (callee);
502 		  e->redirect_call_stmt_to_callee ();
503 		  break;
504 		}
505 	    }
506 	}
507     }
508 }
509 
510 static unsigned int
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:
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 *);
553   virtual unsigned int execute (function *) { return ipa_target_clone (); }
554 };
555 
556 bool
557 pass_target_clone::gate (function *)
558 {
559   return true;
560 }
561 
562 } // anon namespace
563 
564 simple_ipa_opt_pass *
565 make_pass_target_clone (gcc::context *ctxt)
566 {
567   return new pass_target_clone (ctxt);
568 }
569