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