]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/multiple_target.cc
aarch64: Avoid using mismatched ZERO ZA sizes
[thirdparty/gcc.git] / gcc / multiple_target.cc
CommitLineData
3b1661a9
ES
1/* Pass for parsing functions with multiple target attributes.
2
3 Contributed by Evgeny Stupachenko <evstupac@gmail.com>
4
a945c346 5 Copyright (C) 2015-2024 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
aaa587d4
ML
69 if (!targetm.has_ifunc_p ())
70 {
71 error_at (DECL_SOURCE_LOCATION (node->decl),
a164374e 72 "the call requires %<ifunc%>, which is not"
aaa587d4
ML
73 " supported by this target");
74 return;
75 }
76 else if (!targetm.get_function_versions_dispatcher)
77 {
78 error_at (DECL_SOURCE_LOCATION (node->decl),
79 "target does not support function version dispatcher");
80 return;
81 }
82
83 tree idecl = targetm.get_function_versions_dispatcher (node->decl);
84 if (!idecl)
85 {
86 error_at (DECL_SOURCE_LOCATION (node->decl),
646cf252 87 "default %<target_clones%> attribute was not set");
aaa587d4
ML
88 return;
89 }
90
91 cgraph_node *inode = cgraph_node::get (idecl);
92 gcc_assert (inode);
93 tree resolver_decl = targetm.generate_version_dispatcher_body (inode);
94
95 /* Update aliases. */
96 inode->alias = true;
97 inode->alias_target = resolver_decl;
98 if (!inode->analyzed)
99 inode->resolve_alias (cgraph_node::get (resolver_decl));
100
871cc215 101 auto_vec<cgraph_edge *> edges_to_redirect;
27814aed
JJ
102 /* We need to capture the references by value rather than just pointers to them
103 and remove them right away, as removing them later would invalidate what
104 some other reference pointers point to. */
105 auto_vec<ipa_ref> references_to_redirect;
871cc215 106
27814aed
JJ
107 while (node->iterate_referring (0, ref))
108 {
109 references_to_redirect.safe_push (*ref);
110 ref->remove_reference ();
111 }
3b1661a9
ES
112
113 /* We need to remember NEXT_CALLER as it could be modified in the loop. */
871cc215
ML
114 for (cgraph_edge *e = node->callers; e ; e = e->next_caller)
115 edges_to_redirect.safe_push (e);
3b1661a9 116
871cc215
ML
117 if (!edges_to_redirect.is_empty () || !references_to_redirect.is_empty ())
118 {
871cc215
ML
119 /* Redirect edges. */
120 unsigned i;
121 cgraph_edge *e;
122 FOR_EACH_VEC_ELT (edges_to_redirect, i, e)
123 {
124 e->redirect_callee (inode);
27c5a177 125 cgraph_edge::redirect_call_stmt_to_callee (e);
871cc215
ML
126 }
127
128 /* Redirect references. */
129 FOR_EACH_VEC_ELT (references_to_redirect, i, ref)
130 {
131 if (ref->use == IPA_REF_ADDR)
132 {
133 struct walk_stmt_info wi;
134 memset (&wi, 0, sizeof (wi));
135 wi.info = (void *)node->function_version ();
136
137 if (dyn_cast<varpool_node *> (ref->referring))
138 {
139 hash_set<tree> visited_nodes;
140 walk_tree (&DECL_INITIAL (ref->referring->decl),
141 replace_function_decl, &wi, &visited_nodes);
142 }
143 else
144 {
145 gimple_stmt_iterator it = gsi_for_stmt (ref->stmt);
146 if (ref->referring->decl != resolver_decl)
147 walk_gimple_stmt (&it, NULL, replace_function_decl, &wi);
148 }
d5aabfc9
ML
149
150 symtab_node *source = ref->referring;
d5aabfc9 151 source->create_reference (inode, IPA_REF_ADDR);
871cc215 152 }
a9a98049
ML
153 else if (ref->use == IPA_REF_ALIAS)
154 {
155 symtab_node *source = ref->referring;
a9a98049 156 source->create_reference (inode, IPA_REF_ALIAS);
d04295d2
ML
157 if (inode->get_comdat_group ())
158 source->add_to_same_comdat_group (inode);
a9a98049 159 }
871cc215
ML
160 else
161 gcc_unreachable ();
162 }
3b1661a9 163 }
871cc215 164
bfc9250e
ML
165 tree fname = clone_function_name (node->decl, "default");
166 symtab->change_decl_assembler_name (node->decl, fname);
646cf252 167
91292490
ML
168 if (node->definition)
169 {
170 /* FIXME: copy of cgraph_node::make_local that should be cleaned up
171 in next stage1. */
172 node->make_decl_local ();
173 node->set_section (NULL);
174 node->set_comdat_group (NULL);
175 node->externally_visible = false;
176 node->forced_by_abi = false;
91292490
ML
177
178 DECL_ARTIFICIAL (node->decl) = 1;
179 node->force_output = true;
180 }
3b1661a9
ES
181}
182
3b1661a9
ES
183/* Create string with attributes separated by comma.
184 Return number of attributes. */
185
186static int
187get_attr_str (tree arglist, char *attr_str)
188{
189 tree arg;
190 size_t str_len_sum = 0;
191 int argnum = 0;
192
193 for (arg = arglist; arg; arg = TREE_CHAIN (arg))
194 {
195 const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
3b1661a9 196 size_t len = strlen (str);
5e7c7761
JJ
197 for (const char *p = strchr (str, ','); p; p = strchr (p + 1, ','))
198 argnum++;
3b1661a9
ES
199 memcpy (attr_str + str_len_sum, str, len);
200 attr_str[str_len_sum + len] = TREE_CHAIN (arg) ? ',' : '\0';
201 str_len_sum += len + 1;
202 argnum++;
203 }
204 return argnum;
205}
206
207/* Return number of attributes separated by comma and put them into ARGS.
a164374e
ML
208 If there is no DEFAULT attribute return -1.
209 If there is an empty string in attribute return -2.
210 If there are multiple DEFAULT attributes return -3.
211 */
3b1661a9
ES
212
213static int
646cf252 214separate_attrs (char *attr_str, char **attrs, int attrnum)
3b1661a9
ES
215{
216 int i = 0;
646cf252 217 int default_count = 0;
3b1661a9 218
5e7c7761
JJ
219 for (char *attr = strtok (attr_str, ",");
220 attr != NULL; attr = strtok (NULL, ","))
3b1661a9
ES
221 {
222 if (strcmp (attr, "default") == 0)
223 {
646cf252 224 default_count++;
3b1661a9
ES
225 continue;
226 }
5e7c7761 227 attrs[i++] = attr;
3b1661a9 228 }
646cf252 229 if (default_count == 0)
3b1661a9 230 return -1;
a164374e
ML
231 else if (default_count > 1)
232 return -3;
646cf252
ML
233 else if (i + default_count < attrnum)
234 return -2;
235
3b1661a9
ES
236 return i;
237}
238
239/* Return true if symbol is valid in assembler name. */
240
241static bool
242is_valid_asm_symbol (char c)
243{
244 if ('a' <= c && c <= 'z')
245 return true;
246 if ('A' <= c && c <= 'Z')
247 return true;
248 if ('0' <= c && c <= '9')
249 return true;
250 if (c == '_')
251 return true;
252 return false;
253}
254
255/* Replace all not valid assembler symbols with '_'. */
256
257static void
258create_new_asm_name (char *old_asm_name, char *new_asm_name)
259{
260 int i;
261 int old_name_len = strlen (old_asm_name);
262
263 /* Replace all not valid assembler symbols with '_'. */
264 for (i = 0; i < old_name_len; i++)
265 if (!is_valid_asm_symbol (old_asm_name[i]))
266 new_asm_name[i] = '_';
267 else
268 new_asm_name[i] = old_asm_name[i];
269 new_asm_name[old_name_len] = '\0';
270}
271
272/* Creates target clone of NODE. */
273
274static cgraph_node *
5928bc2e
ML
275create_target_clone (cgraph_node *node, bool definition, char *name,
276 tree attributes)
3b1661a9
ES
277{
278 cgraph_node *new_node;
279
280 if (definition)
281 {
bfc9250e
ML
282 new_node
283 = node->create_version_clone_with_body (vNULL, NULL, NULL, NULL, NULL,
284 name, attributes, false);
5928bc2e
ML
285 if (new_node == NULL)
286 return NULL;
3b1661a9
ES
287 new_node->force_output = true;
288 }
289 else
290 {
291 tree new_decl = copy_node (node->decl);
292 new_node = cgraph_node::get_create (new_decl);
5928bc2e 293 DECL_ATTRIBUTES (new_decl) = attributes;
f9477999 294 /* Generate a new name for the new version. */
bfc9250e
ML
295 tree fname = clone_function_name (node->decl, name);
296 symtab->change_decl_assembler_name (new_node->decl, fname);
3b1661a9
ES
297 }
298 return new_node;
299}
300
301/* If the function in NODE has multiple target attributes
302 create the appropriate clone for each valid target attribute. */
303
304static bool
5e7c7761 305expand_target_clones (struct cgraph_node *node, bool definition)
3b1661a9
ES
306{
307 int i;
308 /* Parsing target attributes separated by comma. */
309 tree attr_target = lookup_attribute ("target_clones",
310 DECL_ATTRIBUTES (node->decl));
311 /* No targets specified. */
312 if (!attr_target)
313 return false;
314
315 tree arglist = TREE_VALUE (attr_target);
6df0c8d4 316 int attr_len = get_target_clone_attr_len (arglist);
3b1661a9
ES
317
318 /* No need to clone for 1 target attribute. */
319 if (attr_len == -1)
320 {
321 warning_at (DECL_SOURCE_LOCATION (node->decl),
a164374e 322 0, "single %<target_clones%> attribute is ignored");
3b1661a9
ES
323 return false;
324 }
325
0f1de8d0 326 if (node->definition
a61b003f 327 && (node->alias || !tree_versionable_function_p (node->decl)))
0f1de8d0 328 {
097f82ec 329 auto_diagnostic_group d;
0f1de8d0
JJ
330 error_at (DECL_SOURCE_LOCATION (node->decl),
331 "clones for %<target_clones%> attribute cannot be created");
332 const char *reason = NULL;
333 if (lookup_attribute ("noclone", DECL_ATTRIBUTES (node->decl)))
334 reason = G_("function %q+F can never be copied "
335 "because it has %<noclone%> attribute");
a61b003f
ML
336 else if (node->alias)
337 reason
338 = "%<target_clones%> cannot be combined with %<alias%> attribute";
0f1de8d0
JJ
339 else
340 reason = copy_forbidden (DECL_STRUCT_FUNCTION (node->decl));
341 if (reason)
342 inform (DECL_SOURCE_LOCATION (node->decl), reason, node->decl);
343 return false;
344 }
345
3b1661a9
ES
346 char *attr_str = XNEWVEC (char, attr_len);
347 int attrnum = get_attr_str (arglist, attr_str);
348 char **attrs = XNEWVEC (char *, attrnum);
349
646cf252 350 attrnum = separate_attrs (attr_str, attrs, attrnum);
a164374e 351 switch (attrnum)
3b1661a9 352 {
a164374e 353 case -1:
3b1661a9 354 error_at (DECL_SOURCE_LOCATION (node->decl),
a164374e
ML
355 "%<default%> target was not set");
356 break;
357 case -2:
646cf252
ML
358 error_at (DECL_SOURCE_LOCATION (node->decl),
359 "an empty string cannot be in %<target_clones%> attribute");
a164374e
ML
360 break;
361 case -3:
362 error_at (DECL_SOURCE_LOCATION (node->decl),
363 "multiple %<default%> targets were set");
364 break;
365 default:
366 break;
367 }
368
369 if (attrnum < 0)
370 {
646cf252
ML
371 XDELETEVEC (attrs);
372 XDELETEVEC (attr_str);
373 return false;
374 }
3b1661a9 375
79891c4c
AC
376 const char *new_attr_name = (TARGET_HAS_FMV_TARGET_ATTRIBUTE
377 ? "target" : "target_version");
3b1661a9
ES
378 cgraph_function_version_info *decl1_v = NULL;
379 cgraph_function_version_info *decl2_v = NULL;
380 cgraph_function_version_info *before = NULL;
381 cgraph_function_version_info *after = NULL;
382 decl1_v = node->function_version ();
383 if (decl1_v == NULL)
384 decl1_v = node->insert_new_function_version ();
385 before = decl1_v;
386 DECL_FUNCTION_VERSIONED (node->decl) = 1;
387
388 for (i = 0; i < attrnum; i++)
389 {
390 char *attr = attrs[i];
3b1661a9 391
3b1661a9 392 /* Create new target clone. */
79891c4c 393 tree attributes = make_attribute (new_attr_name, attr,
5928bc2e
ML
394 DECL_ATTRIBUTES (node->decl));
395
07b0096e
BRF
396 char *suffix = XNEWVEC (char, strlen (attr) + 1);
397 create_new_asm_name (attr, suffix);
5928bc2e
ML
398 cgraph_node *new_node = create_target_clone (node, definition, suffix,
399 attributes);
07b0096e 400 XDELETEVEC (suffix);
5928bc2e 401 if (new_node == NULL)
07b0096e
BRF
402 {
403 XDELETEVEC (attrs);
404 XDELETEVEC (attr_str);
405 return false;
406 }
87f94429 407 new_node->local = false;
3b1661a9
ES
408
409 decl2_v = new_node->function_version ();
410 if (decl2_v != NULL)
411 continue;
412 decl2_v = new_node->insert_new_function_version ();
413
414 /* Chain decl2_v and decl1_v. All semantically identical versions
415 will be chained together. */
416 after = decl2_v;
417 while (before->next != NULL)
418 before = before->next;
419 while (after->prev != NULL)
420 after = after->prev;
421
422 before->next = after;
423 after->prev = before;
424 DECL_FUNCTION_VERSIONED (new_node->decl) = 1;
425 }
426
3b1661a9
ES
427 XDELETEVEC (attrs);
428 XDELETEVEC (attr_str);
5e7c7761
JJ
429
430 /* Setting new attribute to initial function. */
79891c4c 431 tree attributes = make_attribute (new_attr_name, "default",
5e7c7761
JJ
432 DECL_ATTRIBUTES (node->decl));
433 DECL_ATTRIBUTES (node->decl) = attributes;
87f94429 434 node->local = false;
5928bc2e 435 return true;
3b1661a9
ES
436}
437
b8ce8129
ML
438/* When NODE is a target clone, consider all callees and redirect
439 to a clone with equal target attributes. That prevents multiple
440 multi-versioning dispatches and a call-chain can be optimized. */
441
442static void
443redirect_to_specific_clone (cgraph_node *node)
444{
445 cgraph_function_version_info *fv = node->function_version ();
446 if (fv == NULL)
447 return;
448
449 tree attr_target = lookup_attribute ("target", DECL_ATTRIBUTES (node->decl));
450 if (attr_target == NULL_TREE)
451 return;
452
453 /* We need to remember NEXT_CALLER as it could be modified in the loop. */
454 for (cgraph_edge *e = node->callees; e ; e = e->next_callee)
455 {
456 cgraph_function_version_info *fv2 = e->callee->function_version ();
457 if (!fv2)
458 continue;
459
460 tree attr_target2 = lookup_attribute ("target",
461 DECL_ATTRIBUTES (e->callee->decl));
462
463 /* Function is not calling proper target clone. */
00e90d3d
YY
464 if (attr_target2 == NULL_TREE
465 || !attribute_value_equal (attr_target, attr_target2))
b8ce8129
ML
466 {
467 while (fv2->prev != NULL)
468 fv2 = fv2->prev;
469
470 /* Try to find a clone with equal target attribute. */
471 for (; fv2 != NULL; fv2 = fv2->next)
472 {
473 cgraph_node *callee = fv2->this_node;
474 attr_target2 = lookup_attribute ("target",
475 DECL_ATTRIBUTES (callee->decl));
00e90d3d
YY
476 if (attr_target2 != NULL_TREE
477 && attribute_value_equal (attr_target, attr_target2))
b8ce8129
ML
478 {
479 e->redirect_callee (callee);
27c5a177 480 cgraph_edge::redirect_call_stmt_to_callee (e);
b8ce8129
ML
481 break;
482 }
483 }
484 }
485 }
486}
487
3b1661a9
ES
488static unsigned int
489ipa_target_clone (void)
490{
491 struct cgraph_node *node;
646cf252 492 auto_vec<cgraph_node *> to_dispatch;
3b1661a9 493
3b1661a9 494 FOR_EACH_FUNCTION (node)
646cf252
ML
495 if (expand_target_clones (node, node->definition))
496 to_dispatch.safe_push (node);
46a2ab58 497
646cf252
ML
498 for (unsigned i = 0; i < to_dispatch.length (); i++)
499 create_dispatcher_calls (to_dispatch[i]);
46a2ab58 500
b8ce8129
ML
501 FOR_EACH_FUNCTION (node)
502 redirect_to_specific_clone (node);
503
3b1661a9
ES
504 return 0;
505}
506
507namespace {
508
509const pass_data pass_data_target_clone =
510{
511 SIMPLE_IPA_PASS, /* type */
512 "targetclone", /* name */
513 OPTGROUP_NONE, /* optinfo_flags */
514 TV_NONE, /* tv_id */
515 ( PROP_ssa | PROP_cfg ), /* properties_required */
516 0, /* properties_provided */
517 0, /* properties_destroyed */
518 0, /* todo_flags_start */
46a2ab58 519 TODO_update_ssa /* todo_flags_finish */
3b1661a9
ES
520};
521
522class pass_target_clone : public simple_ipa_opt_pass
523{
524public:
525 pass_target_clone (gcc::context *ctxt)
526 : simple_ipa_opt_pass (pass_data_target_clone, ctxt)
527 {}
528
529 /* opt_pass methods: */
725793af
DM
530 bool gate (function *) final override;
531 unsigned int execute (function *) final override
532 {
533 return ipa_target_clone ();
534 }
3b1661a9
ES
535};
536
537bool
538pass_target_clone::gate (function *)
539{
5894a817
RB
540 /* If there were any errors avoid pass property verification errors. */
541 return !seen_error ();
3b1661a9
ES
542}
543
544} // anon namespace
545
546simple_ipa_opt_pass *
547make_pass_target_clone (gcc::context *ctxt)
548{
549 return new pass_target_clone (ctxt);
550}