1 /* Pass for parsing functions with multiple target attributes.
3 Contributed by Evgeny Stupachenko <evstupac@gmail.com>
5 Copyright (C) 2015-2021 Free Software Foundation, Inc.
7 This file is part of GCC.
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
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
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/>. */
25 #include "coretypes.h"
28 #include "stringpool.h"
30 #include "diagnostic-core.h"
31 #include "gimple-ssa.h"
33 #include "tree-pass.h"
36 #include "pretty-print.h"
37 #include "gimple-iterator.h"
38 #include "gimple-walk.h"
39 #include "tree-inline.h"
42 /* Walker callback that replaces all FUNCTION_DECL of a function that's
43 going to be versioned. */
46 replace_function_decl (tree
*op
, int *walk_subtrees
, void *data
)
48 struct walk_stmt_info
*wi
= (struct walk_stmt_info
*) data
;
49 cgraph_function_version_info
*info
= (cgraph_function_version_info
*)wi
->info
;
51 if (TREE_CODE (*op
) == FUNCTION_DECL
52 && info
->this_node
->decl
== *op
)
54 *op
= info
->dispatcher_resolver
;
61 /* If the call in NODE has multiple target attribute with multiple fields,
62 replace it with dispatcher call and create dispatcher (once). */
65 create_dispatcher_calls (struct cgraph_node
*node
)
69 if (!DECL_FUNCTION_VERSIONED (node
->decl
)
70 || !is_function_default_version (node
->decl
))
73 if (!targetm
.has_ifunc_p ())
75 error_at (DECL_SOURCE_LOCATION (node
->decl
),
76 "the call requires %<ifunc%>, which is not"
77 " supported by this target");
80 else if (!targetm
.get_function_versions_dispatcher
)
82 error_at (DECL_SOURCE_LOCATION (node
->decl
),
83 "target does not support function version dispatcher");
87 tree idecl
= targetm
.get_function_versions_dispatcher (node
->decl
);
90 error_at (DECL_SOURCE_LOCATION (node
->decl
),
91 "default %<target_clones%> attribute was not set");
95 cgraph_node
*inode
= cgraph_node::get (idecl
);
97 tree resolver_decl
= targetm
.generate_version_dispatcher_body (inode
);
101 inode
->alias_target
= resolver_decl
;
102 if (!inode
->analyzed
)
103 inode
->resolve_alias (cgraph_node::get (resolver_decl
));
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
;
111 while (node
->iterate_referring (0, ref
))
113 references_to_redirect
.safe_push (*ref
);
114 ref
->remove_reference ();
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
);
121 if (!edges_to_redirect
.is_empty () || !references_to_redirect
.is_empty ())
123 /* Redirect edges. */
126 FOR_EACH_VEC_ELT (edges_to_redirect
, i
, e
)
128 e
->redirect_callee (inode
);
129 cgraph_edge::redirect_call_stmt_to_callee (e
);
132 /* Redirect references. */
133 FOR_EACH_VEC_ELT (references_to_redirect
, i
, ref
)
135 if (ref
->use
== IPA_REF_ADDR
)
137 struct walk_stmt_info wi
;
138 memset (&wi
, 0, sizeof (wi
));
139 wi
.info
= (void *)node
->function_version ();
141 if (dyn_cast
<varpool_node
*> (ref
->referring
))
143 hash_set
<tree
> visited_nodes
;
144 walk_tree (&DECL_INITIAL (ref
->referring
->decl
),
145 replace_function_decl
, &wi
, &visited_nodes
);
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
);
154 symtab_node
*source
= ref
->referring
;
155 source
->create_reference (inode
, IPA_REF_ADDR
);
157 else if (ref
->use
== IPA_REF_ALIAS
)
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
);
169 symtab
->change_decl_assembler_name (node
->decl
,
170 clone_function_name_numbered (
171 node
->decl
, "default"));
173 /* FIXME: copy of cgraph_node::make_local that should be cleaned up
175 node
->make_decl_local ();
176 node
->set_section (NULL
);
177 node
->set_comdat_group (NULL
);
178 node
->externally_visible
= false;
179 node
->forced_by_abi
= false;
180 node
->set_section (NULL
);
182 DECL_ARTIFICIAL (node
->decl
) = 1;
183 node
->force_output
= true;
186 /* Return length of attribute names string,
187 if arglist chain > 1, -1 otherwise. */
190 get_attr_len (tree arglist
)
196 for (arg
= arglist
; arg
; arg
= TREE_CHAIN (arg
))
198 const char *str
= TREE_STRING_POINTER (TREE_VALUE (arg
));
199 size_t len
= strlen (str
);
200 str_len_sum
+= len
+ 1;
201 for (const char *p
= strchr (str
, ','); p
; p
= strchr (p
+ 1, ','))
210 /* Create string with attributes separated by comma.
211 Return number of attributes. */
214 get_attr_str (tree arglist
, char *attr_str
)
217 size_t str_len_sum
= 0;
220 for (arg
= arglist
; arg
; arg
= TREE_CHAIN (arg
))
222 const char *str
= TREE_STRING_POINTER (TREE_VALUE (arg
));
223 size_t len
= strlen (str
);
224 for (const char *p
= strchr (str
, ','); p
; p
= strchr (p
+ 1, ','))
226 memcpy (attr_str
+ str_len_sum
, str
, len
);
227 attr_str
[str_len_sum
+ len
] = TREE_CHAIN (arg
) ? ',' : '\0';
228 str_len_sum
+= len
+ 1;
234 /* Return number of attributes separated by comma and put them into ARGS.
235 If there is no DEFAULT attribute return -1.
236 If there is an empty string in attribute return -2.
237 If there are multiple DEFAULT attributes return -3.
241 separate_attrs (char *attr_str
, char **attrs
, int attrnum
)
244 int default_count
= 0;
246 for (char *attr
= strtok (attr_str
, ",");
247 attr
!= NULL
; attr
= strtok (NULL
, ","))
249 if (strcmp (attr
, "default") == 0)
256 if (default_count
== 0)
258 else if (default_count
> 1)
260 else if (i
+ default_count
< attrnum
)
266 /* Return true if symbol is valid in assembler name. */
269 is_valid_asm_symbol (char c
)
271 if ('a' <= c
&& c
<= 'z')
273 if ('A' <= c
&& c
<= 'Z')
275 if ('0' <= c
&& c
<= '9')
282 /* Replace all not valid assembler symbols with '_'. */
285 create_new_asm_name (char *old_asm_name
, char *new_asm_name
)
288 int old_name_len
= strlen (old_asm_name
);
290 /* Replace all not valid assembler symbols with '_'. */
291 for (i
= 0; i
< old_name_len
; i
++)
292 if (!is_valid_asm_symbol (old_asm_name
[i
]))
293 new_asm_name
[i
] = '_';
295 new_asm_name
[i
] = old_asm_name
[i
];
296 new_asm_name
[old_name_len
] = '\0';
299 /* Creates target clone of NODE. */
302 create_target_clone (cgraph_node
*node
, bool definition
, char *name
,
305 cgraph_node
*new_node
;
309 new_node
= node
->create_version_clone_with_body (vNULL
, NULL
,
311 NULL
, name
, attributes
);
312 if (new_node
== NULL
)
314 new_node
->force_output
= true;
318 tree new_decl
= copy_node (node
->decl
);
319 new_node
= cgraph_node::get_create (new_decl
);
320 DECL_ATTRIBUTES (new_decl
) = attributes
;
321 /* Generate a new name for the new version. */
322 symtab
->change_decl_assembler_name (new_node
->decl
,
323 clone_function_name_numbered (
329 /* If the function in NODE has multiple target attributes
330 create the appropriate clone for each valid target attribute. */
333 expand_target_clones (struct cgraph_node
*node
, bool definition
)
336 /* Parsing target attributes separated by comma. */
337 tree attr_target
= lookup_attribute ("target_clones",
338 DECL_ATTRIBUTES (node
->decl
));
339 /* No targets specified. */
343 tree arglist
= TREE_VALUE (attr_target
);
344 int attr_len
= get_attr_len (arglist
);
346 /* No need to clone for 1 target attribute. */
349 warning_at (DECL_SOURCE_LOCATION (node
->decl
),
350 0, "single %<target_clones%> attribute is ignored");
355 && (node
->alias
|| !tree_versionable_function_p (node
->decl
)))
357 auto_diagnostic_group d
;
358 error_at (DECL_SOURCE_LOCATION (node
->decl
),
359 "clones for %<target_clones%> attribute cannot be created");
360 const char *reason
= NULL
;
361 if (lookup_attribute ("noclone", DECL_ATTRIBUTES (node
->decl
)))
362 reason
= G_("function %q+F can never be copied "
363 "because it has %<noclone%> attribute");
364 else if (node
->alias
)
366 = "%<target_clones%> cannot be combined with %<alias%> attribute";
368 reason
= copy_forbidden (DECL_STRUCT_FUNCTION (node
->decl
));
370 inform (DECL_SOURCE_LOCATION (node
->decl
), reason
, node
->decl
);
374 char *attr_str
= XNEWVEC (char, attr_len
);
375 int attrnum
= get_attr_str (arglist
, attr_str
);
376 char **attrs
= XNEWVEC (char *, attrnum
);
378 attrnum
= separate_attrs (attr_str
, attrs
, attrnum
);
382 error_at (DECL_SOURCE_LOCATION (node
->decl
),
383 "%<default%> target was not set");
386 error_at (DECL_SOURCE_LOCATION (node
->decl
),
387 "an empty string cannot be in %<target_clones%> attribute");
390 error_at (DECL_SOURCE_LOCATION (node
->decl
),
391 "multiple %<default%> targets were set");
400 XDELETEVEC (attr_str
);
404 cgraph_function_version_info
*decl1_v
= NULL
;
405 cgraph_function_version_info
*decl2_v
= NULL
;
406 cgraph_function_version_info
*before
= NULL
;
407 cgraph_function_version_info
*after
= NULL
;
408 decl1_v
= node
->function_version ();
410 decl1_v
= node
->insert_new_function_version ();
412 DECL_FUNCTION_VERSIONED (node
->decl
) = 1;
414 for (i
= 0; i
< attrnum
; i
++)
416 char *attr
= attrs
[i
];
417 char *suffix
= XNEWVEC (char, strlen (attr
) + 1);
419 create_new_asm_name (attr
, suffix
);
420 /* Create new target clone. */
421 tree attributes
= make_attribute ("target", attr
,
422 DECL_ATTRIBUTES (node
->decl
));
424 cgraph_node
*new_node
= create_target_clone (node
, definition
, suffix
,
426 if (new_node
== NULL
)
428 new_node
->local
= false;
431 decl2_v
= new_node
->function_version ();
434 decl2_v
= new_node
->insert_new_function_version ();
436 /* Chain decl2_v and decl1_v. All semantically identical versions
437 will be chained together. */
439 while (before
->next
!= NULL
)
440 before
= before
->next
;
441 while (after
->prev
!= NULL
)
444 before
->next
= after
;
445 after
->prev
= before
;
446 DECL_FUNCTION_VERSIONED (new_node
->decl
) = 1;
450 XDELETEVEC (attr_str
);
452 /* Setting new attribute to initial function. */
453 tree attributes
= make_attribute ("target", "default",
454 DECL_ATTRIBUTES (node
->decl
));
455 DECL_ATTRIBUTES (node
->decl
) = attributes
;
460 /* When NODE is a target clone, consider all callees and redirect
461 to a clone with equal target attributes. That prevents multiple
462 multi-versioning dispatches and a call-chain can be optimized. */
465 redirect_to_specific_clone (cgraph_node
*node
)
467 cgraph_function_version_info
*fv
= node
->function_version ();
471 tree attr_target
= lookup_attribute ("target", DECL_ATTRIBUTES (node
->decl
));
472 if (attr_target
== NULL_TREE
)
475 /* We need to remember NEXT_CALLER as it could be modified in the loop. */
476 for (cgraph_edge
*e
= node
->callees
; e
; e
= e
->next_callee
)
478 cgraph_function_version_info
*fv2
= e
->callee
->function_version ();
482 tree attr_target2
= lookup_attribute ("target",
483 DECL_ATTRIBUTES (e
->callee
->decl
));
485 /* Function is not calling proper target clone. */
486 if (attr_target2
== NULL_TREE
487 || !attribute_value_equal (attr_target
, attr_target2
))
489 while (fv2
->prev
!= NULL
)
492 /* Try to find a clone with equal target attribute. */
493 for (; fv2
!= NULL
; fv2
= fv2
->next
)
495 cgraph_node
*callee
= fv2
->this_node
;
496 attr_target2
= lookup_attribute ("target",
497 DECL_ATTRIBUTES (callee
->decl
));
498 if (attr_target2
!= NULL_TREE
499 && attribute_value_equal (attr_target
, attr_target2
))
501 e
->redirect_callee (callee
);
502 cgraph_edge::redirect_call_stmt_to_callee (e
);
511 ipa_target_clone (void)
513 struct cgraph_node
*node
;
514 auto_vec
<cgraph_node
*> to_dispatch
;
516 FOR_EACH_FUNCTION (node
)
517 if (expand_target_clones (node
, node
->definition
))
518 to_dispatch
.safe_push (node
);
520 for (unsigned i
= 0; i
< to_dispatch
.length (); i
++)
521 create_dispatcher_calls (to_dispatch
[i
]);
523 FOR_EACH_FUNCTION (node
)
524 redirect_to_specific_clone (node
);
531 const pass_data pass_data_target_clone
=
533 SIMPLE_IPA_PASS
, /* type */
534 "targetclone", /* name */
535 OPTGROUP_NONE
, /* optinfo_flags */
537 ( PROP_ssa
| PROP_cfg
), /* properties_required */
538 0, /* properties_provided */
539 0, /* properties_destroyed */
540 0, /* todo_flags_start */
541 TODO_update_ssa
/* todo_flags_finish */
544 class pass_target_clone
: public simple_ipa_opt_pass
547 pass_target_clone (gcc::context
*ctxt
)
548 : simple_ipa_opt_pass (pass_data_target_clone
, ctxt
)
551 /* opt_pass methods: */
552 virtual bool gate (function
*);
553 virtual unsigned int execute (function
*) { return ipa_target_clone (); }
557 pass_target_clone::gate (function
*)
564 simple_ipa_opt_pass
*
565 make_pass_target_clone (gcc::context
*ctxt
)
567 return new pass_target_clone (ctxt
);