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