]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/multiple_target.c
[Ada] Improved support for aspect alignment in CCG
[thirdparty/gcc.git] / gcc / multiple_target.c
CommitLineData
3b1661a9
ES
1/* Pass for parsing functions with multiple target attributes.
2
3 Contributed by Evgeny Stupachenko <evstupac@gmail.com>
4
8d9254fc 5 Copyright (C) 2015-2020 Free Software Foundation, Inc.
3b1661a9
ES
6
7This file is part of GCC.
8
9GCC is free software; you can redistribute it and/or modify it under
10the terms of the GNU General Public License as published by the Free
11Software Foundation; either version 3, or (at your option) any later
12version.
13
14GCC is distributed in the hope that it will be useful, but WITHOUT ANY
15WARRANTY; without even the implied warranty of MERCHANTABILITY or
16FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17for more details.
18
19You should have received a copy of the GNU General Public License
20along 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"
871cc215
ML
37#include "gimple-iterator.h"
38#include "gimple-walk.h"
0f1de8d0
JJ
39#include "tree-inline.h"
40#include "intl.h"
871cc215
ML
41
42/* Walker callback that replaces all FUNCTION_DECL of a function that's
43 going to be versioned. */
44
45static tree
46replace_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}
3b1661a9
ES
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
64static void
65create_dispatcher_calls (struct cgraph_node *node)
66{
871cc215
ML
67 ipa_ref *ref;
68
9761349c
MM
69 if (!DECL_FUNCTION_VERSIONED (node->decl)
70 || !is_function_default_version (node->decl))
871cc215
ML
71 return;
72
aaa587d4
ML
73 if (!targetm.has_ifunc_p ())
74 {
75 error_at (DECL_SOURCE_LOCATION (node->decl),
a164374e 76 "the call requires %<ifunc%>, which is not"
aaa587d4
ML
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),
646cf252 91 "default %<target_clones%> attribute was not set");
aaa587d4
ML
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
871cc215 105 auto_vec<cgraph_edge *> edges_to_redirect;
27814aed
JJ
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;
871cc215 110
27814aed
JJ
111 while (node->iterate_referring (0, ref))
112 {
113 references_to_redirect.safe_push (*ref);
114 ref->remove_reference ();
115 }
3b1661a9
ES
116
117 /* We need to remember NEXT_CALLER as it could be modified in the loop. */
871cc215
ML
118 for (cgraph_edge *e = node->callers; e ; e = e->next_caller)
119 edges_to_redirect.safe_push (e);
3b1661a9 120
871cc215
ML
121 if (!edges_to_redirect.is_empty () || !references_to_redirect.is_empty ())
122 {
871cc215
ML
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);
27c5a177 129 cgraph_edge::redirect_call_stmt_to_callee (e);
871cc215
ML
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 }
d5aabfc9
ML
153
154 symtab_node *source = ref->referring;
d5aabfc9 155 source->create_reference (inode, IPA_REF_ADDR);
871cc215 156 }
a9a98049
ML
157 else if (ref->use == IPA_REF_ALIAS)
158 {
159 symtab_node *source = ref->referring;
a9a98049 160 source->create_reference (inode, IPA_REF_ALIAS);
d04295d2
ML
161 if (inode->get_comdat_group ())
162 source->add_to_same_comdat_group (inode);
a9a98049 163 }
871cc215
ML
164 else
165 gcc_unreachable ();
166 }
3b1661a9 167 }
871cc215
ML
168
169 symtab->change_decl_assembler_name (node->decl,
7958186b
MP
170 clone_function_name_numbered (
171 node->decl, "default"));
646cf252
ML
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);
646cf252
ML
181
182 DECL_ARTIFICIAL (node->decl) = 1;
183 node->force_output = true;
3b1661a9
ES
184}
185
186/* Return length of attribute names string,
187 if arglist chain > 1, -1 otherwise. */
188
189static int
190get_attr_len (tree arglist)
191{
192 tree arg;
193 int str_len_sum = 0;
5e7c7761 194 int argnum = 0;
3b1661a9
ES
195
196 for (arg = arglist; arg; arg = TREE_CHAIN (arg))
197 {
3b1661a9 198 const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
5e7c7761 199 size_t len = strlen (str);
3b1661a9 200 str_len_sum += len + 1;
5e7c7761 201 for (const char *p = strchr (str, ','); p; p = strchr (p + 1, ','))
3b1661a9 202 argnum++;
5e7c7761 203 argnum++;
3b1661a9 204 }
5e7c7761 205 if (argnum <= 1)
3b1661a9
ES
206 return -1;
207 return str_len_sum;
208}
209
210/* Create string with attributes separated by comma.
211 Return number of attributes. */
212
213static int
214get_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));
3b1661a9 223 size_t len = strlen (str);
5e7c7761
JJ
224 for (const char *p = strchr (str, ','); p; p = strchr (p + 1, ','))
225 argnum++;
3b1661a9
ES
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.
a164374e
ML
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 */
3b1661a9
ES
239
240static int
646cf252 241separate_attrs (char *attr_str, char **attrs, int attrnum)
3b1661a9
ES
242{
243 int i = 0;
646cf252 244 int default_count = 0;
3b1661a9 245
5e7c7761
JJ
246 for (char *attr = strtok (attr_str, ",");
247 attr != NULL; attr = strtok (NULL, ","))
3b1661a9
ES
248 {
249 if (strcmp (attr, "default") == 0)
250 {
646cf252 251 default_count++;
3b1661a9
ES
252 continue;
253 }
5e7c7761 254 attrs[i++] = attr;
3b1661a9 255 }
646cf252 256 if (default_count == 0)
3b1661a9 257 return -1;
a164374e
ML
258 else if (default_count > 1)
259 return -3;
646cf252
ML
260 else if (i + default_count < attrnum)
261 return -2;
262
3b1661a9
ES
263 return i;
264}
265
266/* Return true if symbol is valid in assembler name. */
267
268static bool
269is_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
284static void
285create_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
301static cgraph_node *
5928bc2e
ML
302create_target_clone (cgraph_node *node, bool definition, char *name,
303 tree attributes)
3b1661a9
ES
304{
305 cgraph_node *new_node;
306
307 if (definition)
308 {
309 new_node = node->create_version_clone_with_body (vNULL, NULL,
ff6686d2
MJ
310 NULL, NULL,
311 NULL, name, attributes);
5928bc2e
ML
312 if (new_node == NULL)
313 return NULL;
3b1661a9
ES
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);
5928bc2e 320 DECL_ATTRIBUTES (new_decl) = attributes;
f9477999
ES
321 /* Generate a new name for the new version. */
322 symtab->change_decl_assembler_name (new_node->decl,
7958186b
MP
323 clone_function_name_numbered (
324 node->decl, name));
3b1661a9
ES
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
332static bool
5e7c7761 333expand_target_clones (struct cgraph_node *node, bool definition)
3b1661a9
ES
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),
a164374e 350 0, "single %<target_clones%> attribute is ignored");
3b1661a9
ES
351 return false;
352 }
353
0f1de8d0 354 if (node->definition
a61b003f 355 && (node->alias || !tree_versionable_function_p (node->decl)))
0f1de8d0 356 {
097f82ec 357 auto_diagnostic_group d;
0f1de8d0
JJ
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");
a61b003f
ML
364 else if (node->alias)
365 reason
366 = "%<target_clones%> cannot be combined with %<alias%> attribute";
0f1de8d0
JJ
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
3b1661a9
ES
374 char *attr_str = XNEWVEC (char, attr_len);
375 int attrnum = get_attr_str (arglist, attr_str);
376 char **attrs = XNEWVEC (char *, attrnum);
377
646cf252 378 attrnum = separate_attrs (attr_str, attrs, attrnum);
a164374e 379 switch (attrnum)
3b1661a9 380 {
a164374e 381 case -1:
3b1661a9 382 error_at (DECL_SOURCE_LOCATION (node->decl),
a164374e
ML
383 "%<default%> target was not set");
384 break;
385 case -2:
646cf252
ML
386 error_at (DECL_SOURCE_LOCATION (node->decl),
387 "an empty string cannot be in %<target_clones%> attribute");
a164374e
ML
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 {
646cf252
ML
399 XDELETEVEC (attrs);
400 XDELETEVEC (attr_str);
401 return false;
402 }
3b1661a9
ES
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. */
3b1661a9 421 tree attributes = make_attribute ("target", attr,
5928bc2e
ML
422 DECL_ATTRIBUTES (node->decl));
423
424 cgraph_node *new_node = create_target_clone (node, definition, suffix,
425 attributes);
426 if (new_node == NULL)
f7486ecb 427 return false;
87f94429 428 new_node->local = false;
5928bc2e 429 XDELETEVEC (suffix);
3b1661a9
ES
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
3b1661a9
ES
449 XDELETEVEC (attrs);
450 XDELETEVEC (attr_str);
5e7c7761
JJ
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;
87f94429 456 node->local = false;
5928bc2e 457 return true;
3b1661a9
ES
458}
459
b8ce8129
ML
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
464static void
465redirect_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 (!attribute_list_equal (attr_target, attr_target2))
487 {
488 while (fv2->prev != NULL)
489 fv2 = fv2->prev;
490
491 /* Try to find a clone with equal target attribute. */
492 for (; fv2 != NULL; fv2 = fv2->next)
493 {
494 cgraph_node *callee = fv2->this_node;
495 attr_target2 = lookup_attribute ("target",
496 DECL_ATTRIBUTES (callee->decl));
497 if (attribute_list_equal (attr_target, attr_target2))
498 {
499 e->redirect_callee (callee);
27c5a177 500 cgraph_edge::redirect_call_stmt_to_callee (e);
b8ce8129
ML
501 break;
502 }
503 }
504 }
505 }
506}
507
3b1661a9
ES
508static unsigned int
509ipa_target_clone (void)
510{
511 struct cgraph_node *node;
646cf252 512 auto_vec<cgraph_node *> to_dispatch;
3b1661a9 513
3b1661a9 514 FOR_EACH_FUNCTION (node)
646cf252
ML
515 if (expand_target_clones (node, node->definition))
516 to_dispatch.safe_push (node);
46a2ab58 517
646cf252
ML
518 for (unsigned i = 0; i < to_dispatch.length (); i++)
519 create_dispatcher_calls (to_dispatch[i]);
46a2ab58 520
b8ce8129
ML
521 FOR_EACH_FUNCTION (node)
522 redirect_to_specific_clone (node);
523
3b1661a9
ES
524 return 0;
525}
526
527namespace {
528
529const pass_data pass_data_target_clone =
530{
531 SIMPLE_IPA_PASS, /* type */
532 "targetclone", /* name */
533 OPTGROUP_NONE, /* optinfo_flags */
534 TV_NONE, /* tv_id */
535 ( PROP_ssa | PROP_cfg ), /* properties_required */
536 0, /* properties_provided */
537 0, /* properties_destroyed */
538 0, /* todo_flags_start */
46a2ab58 539 TODO_update_ssa /* todo_flags_finish */
3b1661a9
ES
540};
541
542class pass_target_clone : public simple_ipa_opt_pass
543{
544public:
545 pass_target_clone (gcc::context *ctxt)
546 : simple_ipa_opt_pass (pass_data_target_clone, ctxt)
547 {}
548
549 /* opt_pass methods: */
550 virtual bool gate (function *);
551 virtual unsigned int execute (function *) { return ipa_target_clone (); }
552};
553
554bool
555pass_target_clone::gate (function *)
556{
557 return true;
558}
559
560} // anon namespace
561
562simple_ipa_opt_pass *
563make_pass_target_clone (gcc::context *ctxt)
564{
565 return new pass_target_clone (ctxt);
566}