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
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"
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
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}
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{
67 ipa_ref *ref;
68
69 if (!targetm.has_ifunc_p ())
70 {
71 error_at (DECL_SOURCE_LOCATION (node->decl),
72 "the call requires %<ifunc%>, which is not"
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),
87 "default %<target_clones%> attribute was not set");
88 return;
89 }
90
91 cgraph_node *inode = cgraph_node::get (decl: 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 (target: cgraph_node::get (decl: resolver_decl));
100
101 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 auto_vec<ipa_ref> references_to_redirect;
106
107 while (node->iterate_referring (i: 0, ref))
108 {
109 references_to_redirect.safe_push (obj: *ref);
110 ref->remove_reference ();
111 }
112
113 /* We need to remember NEXT_CALLER as it could be modified in the loop. */
114 for (cgraph_edge *e = node->callers; e ; e = e->next_caller)
115 edges_to_redirect.safe_push (obj: e);
116
117 if (!edges_to_redirect.is_empty () || !references_to_redirect.is_empty ())
118 {
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 (n: inode);
125 cgraph_edge::redirect_call_stmt_to_callee (e);
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 (s: &wi, c: 0, n: sizeof (wi));
135 wi.info = (void *)node->function_version ();
136
137 if (dyn_cast<varpool_node *> (p: 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 }
149
150 symtab_node *source = ref->referring;
151 source->create_reference (referred_node: inode, use_type: IPA_REF_ADDR);
152 }
153 else if (ref->use == IPA_REF_ALIAS)
154 {
155 symtab_node *source = ref->referring;
156 source->create_reference (referred_node: inode, use_type: IPA_REF_ALIAS);
157 if (inode->get_comdat_group ())
158 source->add_to_same_comdat_group (old_node: inode);
159 }
160 else
161 gcc_unreachable ();
162 }
163 }
164
165 tree fname = clone_function_name (decl: node->decl, suffix: "default");
166 symtab->change_decl_assembler_name (decl: node->decl, name: fname);
167
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;
177
178 DECL_ARTIFICIAL (node->decl) = 1;
179 node->force_output = true;
180 }
181}
182
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));
196 size_t len = strlen (s: str);
197 for (const char *p = strchr (s: str, c: ','); p; p = strchr (s: p + 1, c: ','))
198 argnum++;
199 memcpy (dest: attr_str + str_len_sum, src: str, n: 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.
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
213static int
214separate_attrs (char *attr_str, char **attrs, int attrnum)
215{
216 int i = 0;
217 int default_count = 0;
218
219 for (char *attr = strtok (s: attr_str, delim: ",");
220 attr != NULL; attr = strtok (NULL, delim: ","))
221 {
222 if (strcmp (s1: attr, s2: "default") == 0)
223 {
224 default_count++;
225 continue;
226 }
227 attrs[i++] = attr;
228 }
229 if (default_count == 0)
230 return -1;
231 else if (default_count > 1)
232 return -3;
233 else if (i + default_count < attrnum)
234 return -2;
235
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 (s: 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 (c: 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 *
275create_target_clone (cgraph_node *node, bool definition, char *name,
276 tree attributes)
277{
278 cgraph_node *new_node;
279
280 if (definition)
281 {
282 new_node
283 = node->create_version_clone_with_body (redirect_callers: vNULL, NULL, NULL, NULL, NULL,
284 clone_name: name, target_attributes: attributes, version_decl: false);
285 if (new_node == NULL)
286 return NULL;
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);
293 DECL_ATTRIBUTES (new_decl) = attributes;
294 /* Generate a new name for the new version. */
295 tree fname = clone_function_name (decl: node->decl, suffix: name);
296 symtab->change_decl_assembler_name (decl: new_node->decl, name: 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
304static bool
305expand_target_clones (struct cgraph_node *node, bool definition)
306{
307 int i;
308 /* Parsing target attributes separated by comma. */
309 tree attr_target = lookup_attribute (attr_name: "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);
316 int attr_len = get_target_clone_attr_len (arglist);
317
318 /* No need to clone for 1 target attribute. */
319 if (attr_len == -1)
320 {
321 warning_at (DECL_SOURCE_LOCATION (node->decl),
322 0, "single %<target_clones%> attribute is ignored");
323 return false;
324 }
325
326 if (node->definition
327 && (node->alias || !tree_versionable_function_p (node->decl)))
328 {
329 auto_diagnostic_group d;
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 (attr_name: "noclone", DECL_ATTRIBUTES (node->decl)))
334 reason = G_("function %q+F can never be copied "
335 "because it has %<noclone%> attribute");
336 else if (node->alias)
337 reason
338 = "%<target_clones%> cannot be combined with %<alias%> attribute";
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
346 char *attr_str = XNEWVEC (char, attr_len);
347 int attrnum = get_attr_str (arglist, attr_str);
348 char **attrs = XNEWVEC (char *, attrnum);
349
350 attrnum = separate_attrs (attr_str, attrs, attrnum);
351 switch (attrnum)
352 {
353 case -1:
354 error_at (DECL_SOURCE_LOCATION (node->decl),
355 "%<default%> target was not set");
356 break;
357 case -2:
358 error_at (DECL_SOURCE_LOCATION (node->decl),
359 "an empty string cannot be in %<target_clones%> attribute");
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 {
371 XDELETEVEC (attrs);
372 XDELETEVEC (attr_str);
373 return false;
374 }
375
376 const char *new_attr_name = (TARGET_HAS_FMV_TARGET_ATTRIBUTE
377 ? "target" : "target_version");
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];
391
392 /* Create new target clone. */
393 tree attributes = make_attribute (new_attr_name, attr,
394 DECL_ATTRIBUTES (node->decl));
395
396 char *suffix = XNEWVEC (char, strlen (attr) + 1);
397 create_new_asm_name (old_asm_name: attr, new_asm_name: suffix);
398 cgraph_node *new_node = create_target_clone (node, definition, name: suffix,
399 attributes);
400 XDELETEVEC (suffix);
401 if (new_node == NULL)
402 {
403 XDELETEVEC (attrs);
404 XDELETEVEC (attr_str);
405 return false;
406 }
407 new_node->local = false;
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
427 XDELETEVEC (attrs);
428 XDELETEVEC (attr_str);
429
430 /* Setting new attribute to initial function. */
431 tree attributes = make_attribute (new_attr_name, "default",
432 DECL_ATTRIBUTES (node->decl));
433 DECL_ATTRIBUTES (node->decl) = attributes;
434 node->local = false;
435 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
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 (attr_name: "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 (attr_name: "target",
461 DECL_ATTRIBUTES (e->callee->decl));
462
463 /* Function is not calling proper target clone. */
464 if (attr_target2 == NULL_TREE
465 || !attribute_value_equal (attr_target, attr_target2))
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 (attr_name: "target",
475 DECL_ATTRIBUTES (callee->decl));
476 if (attr_target2 != NULL_TREE
477 && attribute_value_equal (attr_target, attr_target2))
478 {
479 e->redirect_callee (n: callee);
480 cgraph_edge::redirect_call_stmt_to_callee (e);
481 break;
482 }
483 }
484 }
485 }
486}
487
488static unsigned int
489ipa_target_clone (void)
490{
491 struct cgraph_node *node;
492 auto_vec<cgraph_node *> to_dispatch;
493
494 FOR_EACH_FUNCTION (node)
495 if (expand_target_clones (node, definition: node->definition))
496 to_dispatch.safe_push (obj: node);
497
498 for (unsigned i = 0; i < to_dispatch.length (); i++)
499 create_dispatcher_calls (node: to_dispatch[i]);
500
501 FOR_EACH_FUNCTION (node)
502 redirect_to_specific_clone (node);
503
504 return 0;
505}
506
507namespace {
508
509const pass_data pass_data_target_clone =
510{
511 .type: SIMPLE_IPA_PASS, /* type */
512 .name: "targetclone", /* name */
513 .optinfo_flags: OPTGROUP_NONE, /* optinfo_flags */
514 .tv_id: TV_NONE, /* tv_id */
515 .properties_required: ( PROP_ssa | PROP_cfg ), /* properties_required */
516 .properties_provided: 0, /* properties_provided */
517 .properties_destroyed: 0, /* properties_destroyed */
518 .todo_flags_start: 0, /* todo_flags_start */
519 TODO_update_ssa /* todo_flags_finish */
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: */
530 bool gate (function *) final override;
531 unsigned int execute (function *) final override
532 {
533 return ipa_target_clone ();
534 }
535};
536
537bool
538pass_target_clone::gate (function *)
539{
540 /* If there were any errors avoid pass property verification errors. */
541 return !seen_error ();
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}
551

source code of gcc/multiple_target.cc