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

source code of gcc/multiple_target.cc