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