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