1 /* Pass for parsing functions with multiple target attributes.
2
3 Contributed by Evgeny Stupachenko <evstupac@gmail.com>
4
5 Copyright (C) 2015-2022 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
replace_function_decl(tree * op,int * walk_subtrees,void * data)46 replace_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
64 static void
create_dispatcher_calls(struct cgraph_node * node)65 create_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 (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 (cgraph_node::get (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 (0, ref))
112 {
113 references_to_redirect.safe_push (*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 (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 (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 (&wi, 0, sizeof (wi));
139 wi.info = (void *)node->function_version ();
140
141 if (dyn_cast<varpool_node *> (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 (inode, IPA_REF_ADDR);
156 }
157 else if (ref->use == IPA_REF_ALIAS)
158 {
159 symtab_node *source = ref->referring;
160 source->create_reference (inode, IPA_REF_ALIAS);
161 if (inode->get_comdat_group ())
162 source->add_to_same_comdat_group (inode);
163 }
164 else
165 gcc_unreachable ();
166 }
167 }
168
169 tree fname = clone_function_name (node->decl, "default");
170 symtab->change_decl_assembler_name (node->decl, 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 node->set_section (NULL);
182
183 DECL_ARTIFICIAL (node->decl) = 1;
184 node->force_output = true;
185 }
186 }
187
188 /* Create string with attributes separated by comma.
189 Return number of attributes. */
190
191 static int
get_attr_str(tree arglist,char * attr_str)192 get_attr_str (tree arglist, char *attr_str)
193 {
194 tree arg;
195 size_t str_len_sum = 0;
196 int argnum = 0;
197
198 for (arg = arglist; arg; arg = TREE_CHAIN (arg))
199 {
200 const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
201 size_t len = strlen (str);
202 for (const char *p = strchr (str, ','); p; p = strchr (p + 1, ','))
203 argnum++;
204 memcpy (attr_str + str_len_sum, str, len);
205 attr_str[str_len_sum + len] = TREE_CHAIN (arg) ? ',' : '\0';
206 str_len_sum += len + 1;
207 argnum++;
208 }
209 return argnum;
210 }
211
212 /* Return number of attributes separated by comma and put them into ARGS.
213 If there is no DEFAULT attribute return -1.
214 If there is an empty string in attribute return -2.
215 If there are multiple DEFAULT attributes return -3.
216 */
217
218 static int
separate_attrs(char * attr_str,char ** attrs,int attrnum)219 separate_attrs (char *attr_str, char **attrs, int attrnum)
220 {
221 int i = 0;
222 int default_count = 0;
223
224 for (char *attr = strtok (attr_str, ",");
225 attr != NULL; attr = strtok (NULL, ","))
226 {
227 if (strcmp (attr, "default") == 0)
228 {
229 default_count++;
230 continue;
231 }
232 attrs[i++] = attr;
233 }
234 if (default_count == 0)
235 return -1;
236 else if (default_count > 1)
237 return -3;
238 else if (i + default_count < attrnum)
239 return -2;
240
241 return i;
242 }
243
244 /* Return true if symbol is valid in assembler name. */
245
246 static bool
is_valid_asm_symbol(char c)247 is_valid_asm_symbol (char c)
248 {
249 if ('a' <= c && c <= 'z')
250 return true;
251 if ('A' <= c && c <= 'Z')
252 return true;
253 if ('0' <= c && c <= '9')
254 return true;
255 if (c == '_')
256 return true;
257 return false;
258 }
259
260 /* Replace all not valid assembler symbols with '_'. */
261
262 static void
create_new_asm_name(char * old_asm_name,char * new_asm_name)263 create_new_asm_name (char *old_asm_name, char *new_asm_name)
264 {
265 int i;
266 int old_name_len = strlen (old_asm_name);
267
268 /* Replace all not valid assembler symbols with '_'. */
269 for (i = 0; i < old_name_len; i++)
270 if (!is_valid_asm_symbol (old_asm_name[i]))
271 new_asm_name[i] = '_';
272 else
273 new_asm_name[i] = old_asm_name[i];
274 new_asm_name[old_name_len] = '\0';
275 }
276
277 /* Creates target clone of NODE. */
278
279 static cgraph_node *
create_target_clone(cgraph_node * node,bool definition,char * name,tree attributes)280 create_target_clone (cgraph_node *node, bool definition, char *name,
281 tree attributes)
282 {
283 cgraph_node *new_node;
284
285 if (definition)
286 {
287 new_node
288 = node->create_version_clone_with_body (vNULL, NULL, NULL, NULL, NULL,
289 name, attributes, false);
290 if (new_node == NULL)
291 return NULL;
292 new_node->force_output = true;
293 }
294 else
295 {
296 tree new_decl = copy_node (node->decl);
297 new_node = cgraph_node::get_create (new_decl);
298 DECL_ATTRIBUTES (new_decl) = attributes;
299 /* Generate a new name for the new version. */
300 tree fname = clone_function_name (node->decl, name);
301 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
expand_target_clones(struct cgraph_node * node,bool definition)310 expand_target_clones (struct cgraph_node *node, bool definition)
311 {
312 int i;
313 /* Parsing target attributes separated by comma. */
314 tree attr_target = lookup_attribute ("target_clones",
315 DECL_ATTRIBUTES (node->decl));
316 /* No targets specified. */
317 if (!attr_target)
318 return false;
319
320 tree arglist = TREE_VALUE (attr_target);
321 int attr_len = get_target_clone_attr_len (arglist);
322
323 /* No need to clone for 1 target attribute. */
324 if (attr_len == -1)
325 {
326 warning_at (DECL_SOURCE_LOCATION (node->decl),
327 0, "single %<target_clones%> attribute is ignored");
328 return false;
329 }
330
331 if (node->definition
332 && (node->alias || !tree_versionable_function_p (node->decl)))
333 {
334 auto_diagnostic_group d;
335 error_at (DECL_SOURCE_LOCATION (node->decl),
336 "clones for %<target_clones%> attribute cannot be created");
337 const char *reason = NULL;
338 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 else if (node->alias)
342 reason
343 = "%<target_clones%> cannot be combined with %<alias%> attribute";
344 else
345 reason = copy_forbidden (DECL_STRUCT_FUNCTION (node->decl));
346 if (reason)
347 inform (DECL_SOURCE_LOCATION (node->decl), reason, node->decl);
348 return false;
349 }
350
351 char *attr_str = XNEWVEC (char, attr_len);
352 int attrnum = get_attr_str (arglist, attr_str);
353 char **attrs = XNEWVEC (char *, attrnum);
354
355 attrnum = separate_attrs (attr_str, attrs, attrnum);
356 switch (attrnum)
357 {
358 case -1:
359 error_at (DECL_SOURCE_LOCATION (node->decl),
360 "%<default%> target was not set");
361 break;
362 case -2:
363 error_at (DECL_SOURCE_LOCATION (node->decl),
364 "an empty string cannot be in %<target_clones%> attribute");
365 break;
366 case -3:
367 error_at (DECL_SOURCE_LOCATION (node->decl),
368 "multiple %<default%> targets were set");
369 break;
370 default:
371 break;
372 }
373
374 if (attrnum < 0)
375 {
376 XDELETEVEC (attrs);
377 XDELETEVEC (attr_str);
378 return false;
379 }
380
381 cgraph_function_version_info *decl1_v = NULL;
382 cgraph_function_version_info *decl2_v = NULL;
383 cgraph_function_version_info *before = NULL;
384 cgraph_function_version_info *after = NULL;
385 decl1_v = node->function_version ();
386 if (decl1_v == NULL)
387 decl1_v = node->insert_new_function_version ();
388 before = decl1_v;
389 DECL_FUNCTION_VERSIONED (node->decl) = 1;
390
391 for (i = 0; i < attrnum; i++)
392 {
393 char *attr = attrs[i];
394 char *suffix = XNEWVEC (char, strlen (attr) + 1);
395
396 create_new_asm_name (attr, suffix);
397 /* Create new target clone. */
398 tree attributes = make_attribute ("target", attr,
399 DECL_ATTRIBUTES (node->decl));
400
401 cgraph_node *new_node = create_target_clone (node, definition, suffix,
402 attributes);
403 if (new_node == NULL)
404 return false;
405 new_node->local = false;
406 XDELETEVEC (suffix);
407
408 decl2_v = new_node->function_version ();
409 if (decl2_v != NULL)
410 continue;
411 decl2_v = new_node->insert_new_function_version ();
412
413 /* Chain decl2_v and decl1_v. All semantically identical versions
414 will be chained together. */
415 after = decl2_v;
416 while (before->next != NULL)
417 before = before->next;
418 while (after->prev != NULL)
419 after = after->prev;
420
421 before->next = after;
422 after->prev = before;
423 DECL_FUNCTION_VERSIONED (new_node->decl) = 1;
424 }
425
426 XDELETEVEC (attrs);
427 XDELETEVEC (attr_str);
428
429 /* Setting new attribute to initial function. */
430 tree attributes = make_attribute ("target", "default",
431 DECL_ATTRIBUTES (node->decl));
432 DECL_ATTRIBUTES (node->decl) = attributes;
433 node->local = false;
434 return true;
435 }
436
437 /* When NODE is a target clone, consider all callees and redirect
438 to a clone with equal target attributes. That prevents multiple
439 multi-versioning dispatches and a call-chain can be optimized. */
440
441 static void
redirect_to_specific_clone(cgraph_node * node)442 redirect_to_specific_clone (cgraph_node *node)
443 {
444 cgraph_function_version_info *fv = node->function_version ();
445 if (fv == NULL)
446 return;
447
448 tree attr_target = lookup_attribute ("target", DECL_ATTRIBUTES (node->decl));
449 if (attr_target == NULL_TREE)
450 return;
451
452 /* We need to remember NEXT_CALLER as it could be modified in the loop. */
453 for (cgraph_edge *e = node->callees; e ; e = e->next_callee)
454 {
455 cgraph_function_version_info *fv2 = e->callee->function_version ();
456 if (!fv2)
457 continue;
458
459 tree attr_target2 = lookup_attribute ("target",
460 DECL_ATTRIBUTES (e->callee->decl));
461
462 /* Function is not calling proper target clone. */
463 if (attr_target2 == NULL_TREE
464 || !attribute_value_equal (attr_target, attr_target2))
465 {
466 while (fv2->prev != NULL)
467 fv2 = fv2->prev;
468
469 /* Try to find a clone with equal target attribute. */
470 for (; fv2 != NULL; fv2 = fv2->next)
471 {
472 cgraph_node *callee = fv2->this_node;
473 attr_target2 = lookup_attribute ("target",
474 DECL_ATTRIBUTES (callee->decl));
475 if (attr_target2 != NULL_TREE
476 && attribute_value_equal (attr_target, attr_target2))
477 {
478 e->redirect_callee (callee);
479 cgraph_edge::redirect_call_stmt_to_callee (e);
480 break;
481 }
482 }
483 }
484 }
485 }
486
487 static unsigned int
ipa_target_clone(void)488 ipa_target_clone (void)
489 {
490 struct cgraph_node *node;
491 auto_vec<cgraph_node *> to_dispatch;
492
493 FOR_EACH_FUNCTION (node)
494 if (expand_target_clones (node, node->definition))
495 to_dispatch.safe_push (node);
496
497 for (unsigned i = 0; i < to_dispatch.length (); i++)
498 create_dispatcher_calls (to_dispatch[i]);
499
500 FOR_EACH_FUNCTION (node)
501 redirect_to_specific_clone (node);
502
503 return 0;
504 }
505
506 namespace {
507
508 const pass_data pass_data_target_clone =
509 {
510 SIMPLE_IPA_PASS, /* type */
511 "targetclone", /* name */
512 OPTGROUP_NONE, /* optinfo_flags */
513 TV_NONE, /* tv_id */
514 ( PROP_ssa | PROP_cfg ), /* properties_required */
515 0, /* properties_provided */
516 0, /* properties_destroyed */
517 0, /* todo_flags_start */
518 TODO_update_ssa /* todo_flags_finish */
519 };
520
521 class pass_target_clone : public simple_ipa_opt_pass
522 {
523 public:
pass_target_clone(gcc::context * ctxt)524 pass_target_clone (gcc::context *ctxt)
525 : simple_ipa_opt_pass (pass_data_target_clone, ctxt)
526 {}
527
528 /* opt_pass methods: */
529 virtual bool gate (function *);
execute(function *)530 virtual unsigned int execute (function *) { return ipa_target_clone (); }
531 };
532
533 bool
gate(function *)534 pass_target_clone::gate (function *)
535 {
536 return true;
537 }
538
539 } // anon namespace
540
541 simple_ipa_opt_pass *
make_pass_target_clone(gcc::context * ctxt)542 make_pass_target_clone (gcc::context *ctxt)
543 {
544 return new pass_target_clone (ctxt);
545 }
546