1 /* Pass for parsing functions with multiple target attributes.
3 Contributed by Evgeny Stupachenko <evstupac@gmail.com>
5 Copyright (C) 2015-2024 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 (!targetm
.has_ifunc_p ())
71 error_at (DECL_SOURCE_LOCATION (node
->decl
),
72 "the call requires %<ifunc%>, which is not"
73 " supported by this target");
76 else if (!targetm
.get_function_versions_dispatcher
)
78 error_at (DECL_SOURCE_LOCATION (node
->decl
),
79 "target does not support function version dispatcher");
83 tree idecl
= targetm
.get_function_versions_dispatcher (node
->decl
);
86 error_at (DECL_SOURCE_LOCATION (node
->decl
),
87 "default %<target_clones%> attribute was not set");
91 cgraph_node
*inode
= cgraph_node::get (idecl
);
93 tree resolver_decl
= targetm
.generate_version_dispatcher_body (inode
);
97 inode
->alias_target
= resolver_decl
;
99 inode
->resolve_alias (cgraph_node::get (resolver_decl
));
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
;
107 while (node
->iterate_referring (0, ref
))
109 references_to_redirect
.safe_push (*ref
);
110 ref
->remove_reference ();
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 (e
);
117 if (!edges_to_redirect
.is_empty () || !references_to_redirect
.is_empty ())
119 /* Redirect edges. */
122 FOR_EACH_VEC_ELT (edges_to_redirect
, i
, e
)
124 e
->redirect_callee (inode
);
125 cgraph_edge::redirect_call_stmt_to_callee (e
);
128 /* Redirect references. */
129 FOR_EACH_VEC_ELT (references_to_redirect
, i
, ref
)
131 if (ref
->use
== IPA_REF_ADDR
)
133 struct walk_stmt_info wi
;
134 memset (&wi
, 0, sizeof (wi
));
135 wi
.info
= (void *)node
->function_version ();
137 if (dyn_cast
<varpool_node
*> (ref
->referring
))
139 hash_set
<tree
> visited_nodes
;
140 walk_tree (&DECL_INITIAL (ref
->referring
->decl
),
141 replace_function_decl
, &wi
, &visited_nodes
);
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
);
150 symtab_node
*source
= ref
->referring
;
151 source
->create_reference (inode
, IPA_REF_ADDR
);
153 else if (ref
->use
== IPA_REF_ALIAS
)
155 symtab_node
*source
= ref
->referring
;
156 source
->create_reference (inode
, IPA_REF_ALIAS
);
157 if (inode
->get_comdat_group ())
158 source
->add_to_same_comdat_group (inode
);
165 tree fname
= clone_function_name (node
->decl
, "default");
166 symtab
->change_decl_assembler_name (node
->decl
, fname
);
168 if (node
->definition
)
170 /* FIXME: copy of cgraph_node::make_local that should be cleaned up
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;
178 DECL_ARTIFICIAL (node
->decl
) = 1;
179 node
->force_output
= true;
183 /* Create string with attributes separated by comma.
184 Return number of attributes. */
187 get_attr_str (tree arglist
, char *attr_str
)
190 size_t str_len_sum
= 0;
193 for (arg
= arglist
; arg
; arg
= TREE_CHAIN (arg
))
195 const char *str
= TREE_STRING_POINTER (TREE_VALUE (arg
));
196 size_t len
= strlen (str
);
197 for (const char *p
= strchr (str
, ','); p
; p
= strchr (p
+ 1, ','))
199 memcpy (attr_str
+ str_len_sum
, str
, len
);
200 attr_str
[str_len_sum
+ len
] = TREE_CHAIN (arg
) ? ',' : '\0';
201 str_len_sum
+= len
+ 1;
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.
214 separate_attrs (char *attr_str
, char **attrs
, int attrnum
)
217 int default_count
= 0;
219 for (char *attr
= strtok (attr_str
, ",");
220 attr
!= NULL
; attr
= strtok (NULL
, ","))
222 if (strcmp (attr
, "default") == 0)
229 if (default_count
== 0)
231 else if (default_count
> 1)
233 else if (i
+ default_count
< attrnum
)
239 /* Return true if symbol is valid in assembler name. */
242 is_valid_asm_symbol (char c
)
244 if ('a' <= c
&& c
<= 'z')
246 if ('A' <= c
&& c
<= 'Z')
248 if ('0' <= c
&& c
<= '9')
255 /* Replace all not valid assembler symbols with '_'. */
258 create_new_asm_name (char *old_asm_name
, char *new_asm_name
)
261 int old_name_len
= strlen (old_asm_name
);
263 /* Replace all not valid assembler symbols with '_'. */
264 for (i
= 0; i
< old_name_len
; i
++)
265 if (!is_valid_asm_symbol (old_asm_name
[i
]))
266 new_asm_name
[i
] = '_';
268 new_asm_name
[i
] = old_asm_name
[i
];
269 new_asm_name
[old_name_len
] = '\0';
272 /* Creates target clone of NODE. */
275 create_target_clone (cgraph_node
*node
, bool definition
, char *name
,
278 cgraph_node
*new_node
;
283 = node
->create_version_clone_with_body (vNULL
, NULL
, NULL
, NULL
, NULL
,
284 name
, attributes
, false);
285 if (new_node
== NULL
)
287 new_node
->force_output
= true;
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 (node
->decl
, name
);
296 symtab
->change_decl_assembler_name (new_node
->decl
, fname
);
301 /* If the function in NODE has multiple target attributes
302 create the appropriate clone for each valid target attribute. */
305 expand_target_clones (struct cgraph_node
*node
, bool definition
)
308 /* Parsing target attributes separated by comma. */
309 tree attr_target
= lookup_attribute ("target_clones",
310 DECL_ATTRIBUTES (node
->decl
));
311 /* No targets specified. */
315 tree arglist
= TREE_VALUE (attr_target
);
316 int attr_len
= get_target_clone_attr_len (arglist
);
318 /* No need to clone for 1 target attribute. */
321 warning_at (DECL_SOURCE_LOCATION (node
->decl
),
322 0, "single %<target_clones%> attribute is ignored");
327 && (node
->alias
|| !tree_versionable_function_p (node
->decl
)))
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 ("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
)
338 = "%<target_clones%> cannot be combined with %<alias%> attribute";
340 reason
= copy_forbidden (DECL_STRUCT_FUNCTION (node
->decl
));
342 inform (DECL_SOURCE_LOCATION (node
->decl
), reason
, node
->decl
);
346 char *attr_str
= XNEWVEC (char, attr_len
);
347 int attrnum
= get_attr_str (arglist
, attr_str
);
348 char **attrs
= XNEWVEC (char *, attrnum
);
350 attrnum
= separate_attrs (attr_str
, attrs
, attrnum
);
354 error_at (DECL_SOURCE_LOCATION (node
->decl
),
355 "%<default%> target was not set");
358 error_at (DECL_SOURCE_LOCATION (node
->decl
),
359 "an empty string cannot be in %<target_clones%> attribute");
362 error_at (DECL_SOURCE_LOCATION (node
->decl
),
363 "multiple %<default%> targets were set");
372 XDELETEVEC (attr_str
);
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 ();
384 decl1_v
= node
->insert_new_function_version ();
386 DECL_FUNCTION_VERSIONED (node
->decl
) = 1;
388 for (i
= 0; i
< attrnum
; i
++)
390 char *attr
= attrs
[i
];
392 /* Create new target clone. */
393 tree attributes
= make_attribute (new_attr_name
, attr
,
394 DECL_ATTRIBUTES (node
->decl
));
396 char *suffix
= XNEWVEC (char, strlen (attr
) + 1);
397 create_new_asm_name (attr
, suffix
);
398 cgraph_node
*new_node
= create_target_clone (node
, definition
, suffix
,
401 if (new_node
== NULL
)
404 XDELETEVEC (attr_str
);
407 new_node
->local
= false;
409 decl2_v
= new_node
->function_version ();
412 decl2_v
= new_node
->insert_new_function_version ();
414 /* Chain decl2_v and decl1_v. All semantically identical versions
415 will be chained together. */
417 while (before
->next
!= NULL
)
418 before
= before
->next
;
419 while (after
->prev
!= NULL
)
422 before
->next
= after
;
423 after
->prev
= before
;
424 DECL_FUNCTION_VERSIONED (new_node
->decl
) = 1;
428 XDELETEVEC (attr_str
);
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
;
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. */
443 redirect_to_specific_clone (cgraph_node
*node
)
445 cgraph_function_version_info
*fv
= node
->function_version ();
449 tree attr_target
= lookup_attribute ("target", DECL_ATTRIBUTES (node
->decl
));
450 if (attr_target
== NULL_TREE
)
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
)
456 cgraph_function_version_info
*fv2
= e
->callee
->function_version ();
460 tree attr_target2
= lookup_attribute ("target",
461 DECL_ATTRIBUTES (e
->callee
->decl
));
463 /* Function is not calling proper target clone. */
464 if (attr_target2
== NULL_TREE
465 || !attribute_value_equal (attr_target
, attr_target2
))
467 while (fv2
->prev
!= NULL
)
470 /* Try to find a clone with equal target attribute. */
471 for (; fv2
!= NULL
; fv2
= fv2
->next
)
473 cgraph_node
*callee
= fv2
->this_node
;
474 attr_target2
= lookup_attribute ("target",
475 DECL_ATTRIBUTES (callee
->decl
));
476 if (attr_target2
!= NULL_TREE
477 && attribute_value_equal (attr_target
, attr_target2
))
479 e
->redirect_callee (callee
);
480 cgraph_edge::redirect_call_stmt_to_callee (e
);
489 ipa_target_clone (void)
491 struct cgraph_node
*node
;
492 auto_vec
<cgraph_node
*> to_dispatch
;
494 FOR_EACH_FUNCTION (node
)
495 if (expand_target_clones (node
, node
->definition
))
496 to_dispatch
.safe_push (node
);
498 for (unsigned i
= 0; i
< to_dispatch
.length (); i
++)
499 create_dispatcher_calls (to_dispatch
[i
]);
501 FOR_EACH_FUNCTION (node
)
502 redirect_to_specific_clone (node
);
509 const pass_data pass_data_target_clone
=
511 SIMPLE_IPA_PASS
, /* type */
512 "targetclone", /* name */
513 OPTGROUP_NONE
, /* optinfo_flags */
515 ( PROP_ssa
| PROP_cfg
), /* properties_required */
516 0, /* properties_provided */
517 0, /* properties_destroyed */
518 0, /* todo_flags_start */
519 TODO_update_ssa
/* todo_flags_finish */
522 class pass_target_clone
: public simple_ipa_opt_pass
525 pass_target_clone (gcc::context
*ctxt
)
526 : simple_ipa_opt_pass (pass_data_target_clone
, ctxt
)
529 /* opt_pass methods: */
530 bool gate (function
*) final override
;
531 unsigned int execute (function
*) final override
533 return ipa_target_clone ();
538 pass_target_clone::gate (function
*)
540 /* If there were any errors avoid pass property verification errors. */
541 return !seen_error ();
546 simple_ipa_opt_pass
*
547 make_pass_target_clone (gcc::context
*ctxt
)
549 return new pass_target_clone (ctxt
);