hppa: Fix LO_SUM DLTIND14R address support in PRINT_OPERAND_ADDRESS
[official-gcc.git] / gcc / multiple_target.cc
blob1fdd279da04a7acc5e8c50f528139f19cadcd5ff
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
12 version.
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.
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/>. */
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"
42 /* Walker callback that replaces all FUNCTION_DECL of a function that's
43 going to be versioned. */
45 static tree
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;
55 *walk_subtrees = 0;
58 return NULL;
61 /* If the call in NODE has multiple target attribute with multiple fields,
62 replace it with dispatcher call and create dispatcher (once). */
64 static void
65 create_dispatcher_calls (struct cgraph_node *node)
67 ipa_ref *ref;
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");
74 return;
76 else if (!targetm.get_function_versions_dispatcher)
78 error_at (DECL_SOURCE_LOCATION (node->decl),
79 "target does not support function version dispatcher");
80 return;
83 tree idecl = targetm.get_function_versions_dispatcher (node->decl);
84 if (!idecl)
86 error_at (DECL_SOURCE_LOCATION (node->decl),
87 "default %<target_clones%> attribute was not set");
88 return;
91 cgraph_node *inode = cgraph_node::get (idecl);
92 gcc_assert (inode);
93 tree resolver_decl = targetm.generate_version_dispatcher_body (inode);
95 /* Update aliases. */
96 inode->alias = true;
97 inode->alias_target = resolver_decl;
98 if (!inode->analyzed)
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. */
120 unsigned i;
121 cgraph_edge *e;
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);
143 else
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);
160 else
161 gcc_unreachable ();
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
171 in next stage1. */
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. */
186 static int
187 get_attr_str (tree arglist, char *attr_str)
189 tree arg;
190 size_t str_len_sum = 0;
191 int argnum = 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, ','))
198 argnum++;
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;
202 argnum++;
204 return argnum;
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.
213 static int
214 separate_attrs (char *attr_str, char **attrs, int attrnum)
216 int i = 0;
217 int default_count = 0;
219 for (char *attr = strtok (attr_str, ",");
220 attr != NULL; attr = strtok (NULL, ","))
222 if (strcmp (attr, "default") == 0)
224 default_count++;
225 continue;
227 attrs[i++] = attr;
229 if (default_count == 0)
230 return -1;
231 else if (default_count > 1)
232 return -3;
233 else if (i + default_count < attrnum)
234 return -2;
236 return i;
239 /* Return true if symbol is valid in assembler name. */
241 static bool
242 is_valid_asm_symbol (char c)
244 if ('a' <= c && c <= 'z')
245 return true;
246 if ('A' <= c && c <= 'Z')
247 return true;
248 if ('0' <= c && c <= '9')
249 return true;
250 if (c == '_')
251 return true;
252 return false;
255 /* Replace all not valid assembler symbols with '_'. */
257 static void
258 create_new_asm_name (char *old_asm_name, char *new_asm_name)
260 int i;
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] = '_';
267 else
268 new_asm_name[i] = old_asm_name[i];
269 new_asm_name[old_name_len] = '\0';
272 /* Creates target clone of NODE. */
274 static cgraph_node *
275 create_target_clone (cgraph_node *node, bool definition, char *name,
276 tree attributes)
278 cgraph_node *new_node;
280 if (definition)
282 new_node
283 = node->create_version_clone_with_body (vNULL, NULL, NULL, NULL, NULL,
284 name, attributes, false);
285 if (new_node == NULL)
286 return NULL;
287 new_node->force_output = true;
289 else
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);
298 return new_node;
301 /* If the function in NODE has multiple target attributes
302 create the appropriate clone for each valid target attribute. */
304 static bool
305 expand_target_clones (struct cgraph_node *node, bool definition)
307 int i;
308 /* Parsing target attributes separated by comma. */
309 tree attr_target = lookup_attribute ("target_clones",
310 DECL_ATTRIBUTES (node->decl));
311 /* No targets specified. */
312 if (!attr_target)
313 return false;
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. */
319 if (attr_len == -1)
321 warning_at (DECL_SOURCE_LOCATION (node->decl),
322 0, "single %<target_clones%> attribute is ignored");
323 return false;
326 if (node->definition
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)
337 reason
338 = "%<target_clones%> cannot be combined with %<alias%> attribute";
339 else
340 reason = copy_forbidden (DECL_STRUCT_FUNCTION (node->decl));
341 if (reason)
342 inform (DECL_SOURCE_LOCATION (node->decl), reason, node->decl);
343 return false;
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);
351 switch (attrnum)
353 case -1:
354 error_at (DECL_SOURCE_LOCATION (node->decl),
355 "%<default%> target was not set");
356 break;
357 case -2:
358 error_at (DECL_SOURCE_LOCATION (node->decl),
359 "an empty string cannot be in %<target_clones%> attribute");
360 break;
361 case -3:
362 error_at (DECL_SOURCE_LOCATION (node->decl),
363 "multiple %<default%> targets were set");
364 break;
365 default:
366 break;
369 if (attrnum < 0)
371 XDELETEVEC (attrs);
372 XDELETEVEC (attr_str);
373 return false;
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 ();
383 if (decl1_v == NULL)
384 decl1_v = node->insert_new_function_version ();
385 before = decl1_v;
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,
399 attributes);
400 XDELETEVEC (suffix);
401 if (new_node == NULL)
403 XDELETEVEC (attrs);
404 XDELETEVEC (attr_str);
405 return false;
407 new_node->local = false;
409 decl2_v = new_node->function_version ();
410 if (decl2_v != NULL)
411 continue;
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. */
416 after = decl2_v;
417 while (before->next != NULL)
418 before = before->next;
419 while (after->prev != NULL)
420 after = after->prev;
422 before->next = after;
423 after->prev = before;
424 DECL_FUNCTION_VERSIONED (new_node->decl) = 1;
427 XDELETEVEC (attrs);
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;
434 node->local = false;
435 return true;
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. */
442 static void
443 redirect_to_specific_clone (cgraph_node *node)
445 cgraph_function_version_info *fv = node->function_version ();
446 if (fv == NULL)
447 return;
449 tree attr_target = lookup_attribute ("target", DECL_ATTRIBUTES (node->decl));
450 if (attr_target == NULL_TREE)
451 return;
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 ();
457 if (!fv2)
458 continue;
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)
468 fv2 = fv2->prev;
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);
481 break;
488 static unsigned int
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);
504 return 0;
507 namespace {
509 const pass_data pass_data_target_clone =
511 SIMPLE_IPA_PASS, /* type */
512 "targetclone", /* name */
513 OPTGROUP_NONE, /* optinfo_flags */
514 TV_NONE, /* tv_id */
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
524 public:
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 ();
537 bool
538 pass_target_clone::gate (function *)
540 /* If there were any errors avoid pass property verification errors. */
541 return !seen_error ();
544 } // anon namespace
546 simple_ipa_opt_pass *
547 make_pass_target_clone (gcc::context *ctxt)
549 return new pass_target_clone (ctxt);