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