11debfc3dSmrg /* Pass for parsing functions with multiple target attributes.
21debfc3dSmrg
31debfc3dSmrg Contributed by Evgeny Stupachenko <evstupac@gmail.com>
41debfc3dSmrg
5*8feb0f0bSmrg Copyright (C) 2015-2020 Free Software Foundation, Inc.
61debfc3dSmrg
71debfc3dSmrg This file is part of GCC.
81debfc3dSmrg
91debfc3dSmrg GCC is free software; you can redistribute it and/or modify it under
101debfc3dSmrg the terms of the GNU General Public License as published by the Free
111debfc3dSmrg Software Foundation; either version 3, or (at your option) any later
121debfc3dSmrg version.
131debfc3dSmrg
141debfc3dSmrg GCC is distributed in the hope that it will be useful, but WITHOUT ANY
151debfc3dSmrg WARRANTY; without even the implied warranty of MERCHANTABILITY or
161debfc3dSmrg FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
171debfc3dSmrg for more details.
181debfc3dSmrg
191debfc3dSmrg You should have received a copy of the GNU General Public License
201debfc3dSmrg along with GCC; see the file COPYING3. If not see
211debfc3dSmrg <http://www.gnu.org/licenses/>. */
221debfc3dSmrg
231debfc3dSmrg #include "config.h"
241debfc3dSmrg #include "system.h"
251debfc3dSmrg #include "coretypes.h"
261debfc3dSmrg #include "backend.h"
271debfc3dSmrg #include "tree.h"
281debfc3dSmrg #include "stringpool.h"
291debfc3dSmrg #include "gimple.h"
301debfc3dSmrg #include "diagnostic-core.h"
311debfc3dSmrg #include "gimple-ssa.h"
321debfc3dSmrg #include "cgraph.h"
331debfc3dSmrg #include "tree-pass.h"
341debfc3dSmrg #include "target.h"
351debfc3dSmrg #include "attribs.h"
361debfc3dSmrg #include "pretty-print.h"
37a2dc1f3fSmrg #include "gimple-iterator.h"
38a2dc1f3fSmrg #include "gimple-walk.h"
39a2dc1f3fSmrg #include "tree-inline.h"
40a2dc1f3fSmrg #include "intl.h"
41a2dc1f3fSmrg
42a2dc1f3fSmrg /* Walker callback that replaces all FUNCTION_DECL of a function that's
43a2dc1f3fSmrg going to be versioned. */
44a2dc1f3fSmrg
45a2dc1f3fSmrg static tree
replace_function_decl(tree * op,int * walk_subtrees,void * data)46a2dc1f3fSmrg replace_function_decl (tree *op, int *walk_subtrees, void *data)
47a2dc1f3fSmrg {
48a2dc1f3fSmrg struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
49a2dc1f3fSmrg cgraph_function_version_info *info = (cgraph_function_version_info *)wi->info;
50a2dc1f3fSmrg
51a2dc1f3fSmrg if (TREE_CODE (*op) == FUNCTION_DECL
52a2dc1f3fSmrg && info->this_node->decl == *op)
53a2dc1f3fSmrg {
54a2dc1f3fSmrg *op = info->dispatcher_resolver;
55a2dc1f3fSmrg *walk_subtrees = 0;
56a2dc1f3fSmrg }
57a2dc1f3fSmrg
58a2dc1f3fSmrg return NULL;
59a2dc1f3fSmrg }
601debfc3dSmrg
611debfc3dSmrg /* If the call in NODE has multiple target attribute with multiple fields,
621debfc3dSmrg replace it with dispatcher call and create dispatcher (once). */
631debfc3dSmrg
641debfc3dSmrg static void
create_dispatcher_calls(struct cgraph_node * node)651debfc3dSmrg create_dispatcher_calls (struct cgraph_node *node)
661debfc3dSmrg {
67a2dc1f3fSmrg ipa_ref *ref;
681debfc3dSmrg
69a2dc1f3fSmrg if (!DECL_FUNCTION_VERSIONED (node->decl)
70a2dc1f3fSmrg || !is_function_default_version (node->decl))
71a2dc1f3fSmrg return;
721debfc3dSmrg
731debfc3dSmrg if (!targetm.has_ifunc_p ())
741debfc3dSmrg {
75a2dc1f3fSmrg error_at (DECL_SOURCE_LOCATION (node->decl),
76c0a68be4Smrg "the call requires %<ifunc%>, which is not"
771debfc3dSmrg " supported by this target");
78a2dc1f3fSmrg return;
791debfc3dSmrg }
801debfc3dSmrg else if (!targetm.get_function_versions_dispatcher)
811debfc3dSmrg {
82a2dc1f3fSmrg error_at (DECL_SOURCE_LOCATION (node->decl),
831debfc3dSmrg "target does not support function version dispatcher");
84a2dc1f3fSmrg return;
851debfc3dSmrg }
861debfc3dSmrg
87a2dc1f3fSmrg tree idecl = targetm.get_function_versions_dispatcher (node->decl);
881debfc3dSmrg if (!idecl)
891debfc3dSmrg {
90a2dc1f3fSmrg error_at (DECL_SOURCE_LOCATION (node->decl),
91a2dc1f3fSmrg "default %<target_clones%> attribute was not set");
92a2dc1f3fSmrg return;
931debfc3dSmrg }
94a2dc1f3fSmrg
95a2dc1f3fSmrg cgraph_node *inode = cgraph_node::get (idecl);
961debfc3dSmrg gcc_assert (inode);
97a2dc1f3fSmrg tree resolver_decl = targetm.generate_version_dispatcher_body (inode);
981debfc3dSmrg
991debfc3dSmrg /* Update aliases. */
1001debfc3dSmrg inode->alias = true;
1011debfc3dSmrg inode->alias_target = resolver_decl;
1021debfc3dSmrg if (!inode->analyzed)
1031debfc3dSmrg inode->resolve_alias (cgraph_node::get (resolver_decl));
1041debfc3dSmrg
105a2dc1f3fSmrg auto_vec<cgraph_edge *> edges_to_redirect;
106a2dc1f3fSmrg /* We need to capture the references by value rather than just pointers to them
107a2dc1f3fSmrg and remove them right away, as removing them later would invalidate what
108a2dc1f3fSmrg some other reference pointers point to. */
109a2dc1f3fSmrg auto_vec<ipa_ref> references_to_redirect;
110a2dc1f3fSmrg
111a2dc1f3fSmrg while (node->iterate_referring (0, ref))
112a2dc1f3fSmrg {
113a2dc1f3fSmrg references_to_redirect.safe_push (*ref);
114a2dc1f3fSmrg ref->remove_reference ();
115a2dc1f3fSmrg }
116a2dc1f3fSmrg
117a2dc1f3fSmrg /* We need to remember NEXT_CALLER as it could be modified in the loop. */
118a2dc1f3fSmrg for (cgraph_edge *e = node->callers; e ; e = e->next_caller)
119a2dc1f3fSmrg edges_to_redirect.safe_push (e);
120a2dc1f3fSmrg
121a2dc1f3fSmrg if (!edges_to_redirect.is_empty () || !references_to_redirect.is_empty ())
122a2dc1f3fSmrg {
123a2dc1f3fSmrg /* Redirect edges. */
124a2dc1f3fSmrg unsigned i;
125a2dc1f3fSmrg cgraph_edge *e;
126a2dc1f3fSmrg FOR_EACH_VEC_ELT (edges_to_redirect, i, e)
127a2dc1f3fSmrg {
1281debfc3dSmrg e->redirect_callee (inode);
129*8feb0f0bSmrg cgraph_edge::redirect_call_stmt_to_callee (e);
1301debfc3dSmrg }
131a2dc1f3fSmrg
132a2dc1f3fSmrg /* Redirect references. */
133a2dc1f3fSmrg FOR_EACH_VEC_ELT (references_to_redirect, i, ref)
134a2dc1f3fSmrg {
135a2dc1f3fSmrg if (ref->use == IPA_REF_ADDR)
136a2dc1f3fSmrg {
137a2dc1f3fSmrg struct walk_stmt_info wi;
138a2dc1f3fSmrg memset (&wi, 0, sizeof (wi));
139a2dc1f3fSmrg wi.info = (void *)node->function_version ();
140a2dc1f3fSmrg
141a2dc1f3fSmrg if (dyn_cast<varpool_node *> (ref->referring))
142a2dc1f3fSmrg {
143a2dc1f3fSmrg hash_set<tree> visited_nodes;
144a2dc1f3fSmrg walk_tree (&DECL_INITIAL (ref->referring->decl),
145a2dc1f3fSmrg replace_function_decl, &wi, &visited_nodes);
146a2dc1f3fSmrg }
147a2dc1f3fSmrg else
148a2dc1f3fSmrg {
149a2dc1f3fSmrg gimple_stmt_iterator it = gsi_for_stmt (ref->stmt);
150a2dc1f3fSmrg if (ref->referring->decl != resolver_decl)
151a2dc1f3fSmrg walk_gimple_stmt (&it, NULL, replace_function_decl, &wi);
152a2dc1f3fSmrg }
153a2dc1f3fSmrg
154a2dc1f3fSmrg symtab_node *source = ref->referring;
155a2dc1f3fSmrg source->create_reference (inode, IPA_REF_ADDR);
156a2dc1f3fSmrg }
157a2dc1f3fSmrg else if (ref->use == IPA_REF_ALIAS)
158a2dc1f3fSmrg {
159a2dc1f3fSmrg symtab_node *source = ref->referring;
160a2dc1f3fSmrg source->create_reference (inode, IPA_REF_ALIAS);
161a2dc1f3fSmrg if (inode->get_comdat_group ())
162a2dc1f3fSmrg source->add_to_same_comdat_group (inode);
163a2dc1f3fSmrg }
164a2dc1f3fSmrg else
165a2dc1f3fSmrg gcc_unreachable ();
166a2dc1f3fSmrg }
167a2dc1f3fSmrg }
168a2dc1f3fSmrg
169a2dc1f3fSmrg symtab->change_decl_assembler_name (node->decl,
170c0a68be4Smrg clone_function_name_numbered (
171c0a68be4Smrg node->decl, "default"));
172a2dc1f3fSmrg
173a2dc1f3fSmrg /* FIXME: copy of cgraph_node::make_local that should be cleaned up
174a2dc1f3fSmrg in next stage1. */
175a2dc1f3fSmrg node->make_decl_local ();
176a2dc1f3fSmrg node->set_section (NULL);
177a2dc1f3fSmrg node->set_comdat_group (NULL);
178a2dc1f3fSmrg node->externally_visible = false;
179a2dc1f3fSmrg node->forced_by_abi = false;
180a2dc1f3fSmrg node->set_section (NULL);
181a2dc1f3fSmrg
182a2dc1f3fSmrg DECL_ARTIFICIAL (node->decl) = 1;
183a2dc1f3fSmrg node->force_output = true;
1841debfc3dSmrg }
1851debfc3dSmrg
1861debfc3dSmrg /* Return length of attribute names string,
1871debfc3dSmrg if arglist chain > 1, -1 otherwise. */
1881debfc3dSmrg
1891debfc3dSmrg static int
get_attr_len(tree arglist)1901debfc3dSmrg get_attr_len (tree arglist)
1911debfc3dSmrg {
1921debfc3dSmrg tree arg;
1931debfc3dSmrg int str_len_sum = 0;
1941debfc3dSmrg int argnum = 0;
1951debfc3dSmrg
1961debfc3dSmrg for (arg = arglist; arg; arg = TREE_CHAIN (arg))
1971debfc3dSmrg {
1981debfc3dSmrg const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
1991debfc3dSmrg size_t len = strlen (str);
2001debfc3dSmrg str_len_sum += len + 1;
2011debfc3dSmrg for (const char *p = strchr (str, ','); p; p = strchr (p + 1, ','))
2021debfc3dSmrg argnum++;
2031debfc3dSmrg argnum++;
2041debfc3dSmrg }
2051debfc3dSmrg if (argnum <= 1)
2061debfc3dSmrg return -1;
2071debfc3dSmrg return str_len_sum;
2081debfc3dSmrg }
2091debfc3dSmrg
2101debfc3dSmrg /* Create string with attributes separated by comma.
2111debfc3dSmrg Return number of attributes. */
2121debfc3dSmrg
2131debfc3dSmrg static int
get_attr_str(tree arglist,char * attr_str)2141debfc3dSmrg get_attr_str (tree arglist, char *attr_str)
2151debfc3dSmrg {
2161debfc3dSmrg tree arg;
2171debfc3dSmrg size_t str_len_sum = 0;
2181debfc3dSmrg int argnum = 0;
2191debfc3dSmrg
2201debfc3dSmrg for (arg = arglist; arg; arg = TREE_CHAIN (arg))
2211debfc3dSmrg {
2221debfc3dSmrg const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
2231debfc3dSmrg size_t len = strlen (str);
2241debfc3dSmrg for (const char *p = strchr (str, ','); p; p = strchr (p + 1, ','))
2251debfc3dSmrg argnum++;
2261debfc3dSmrg memcpy (attr_str + str_len_sum, str, len);
2271debfc3dSmrg attr_str[str_len_sum + len] = TREE_CHAIN (arg) ? ',' : '\0';
2281debfc3dSmrg str_len_sum += len + 1;
2291debfc3dSmrg argnum++;
2301debfc3dSmrg }
2311debfc3dSmrg return argnum;
2321debfc3dSmrg }
2331debfc3dSmrg
2341debfc3dSmrg /* Return number of attributes separated by comma and put them into ARGS.
235c0a68be4Smrg If there is no DEFAULT attribute return -1.
236c0a68be4Smrg If there is an empty string in attribute return -2.
237c0a68be4Smrg If there are multiple DEFAULT attributes return -3.
238c0a68be4Smrg */
2391debfc3dSmrg
2401debfc3dSmrg static int
separate_attrs(char * attr_str,char ** attrs,int attrnum)241a2dc1f3fSmrg separate_attrs (char *attr_str, char **attrs, int attrnum)
2421debfc3dSmrg {
2431debfc3dSmrg int i = 0;
244a2dc1f3fSmrg int default_count = 0;
2451debfc3dSmrg
2461debfc3dSmrg for (char *attr = strtok (attr_str, ",");
2471debfc3dSmrg attr != NULL; attr = strtok (NULL, ","))
2481debfc3dSmrg {
2491debfc3dSmrg if (strcmp (attr, "default") == 0)
2501debfc3dSmrg {
251a2dc1f3fSmrg default_count++;
2521debfc3dSmrg continue;
2531debfc3dSmrg }
2541debfc3dSmrg attrs[i++] = attr;
2551debfc3dSmrg }
256a2dc1f3fSmrg if (default_count == 0)
2571debfc3dSmrg return -1;
258c0a68be4Smrg else if (default_count > 1)
259c0a68be4Smrg return -3;
260a2dc1f3fSmrg else if (i + default_count < attrnum)
261a2dc1f3fSmrg return -2;
262a2dc1f3fSmrg
2631debfc3dSmrg return i;
2641debfc3dSmrg }
2651debfc3dSmrg
2661debfc3dSmrg /* Return true if symbol is valid in assembler name. */
2671debfc3dSmrg
2681debfc3dSmrg static bool
is_valid_asm_symbol(char c)2691debfc3dSmrg is_valid_asm_symbol (char c)
2701debfc3dSmrg {
2711debfc3dSmrg if ('a' <= c && c <= 'z')
2721debfc3dSmrg return true;
2731debfc3dSmrg if ('A' <= c && c <= 'Z')
2741debfc3dSmrg return true;
2751debfc3dSmrg if ('0' <= c && c <= '9')
2761debfc3dSmrg return true;
2771debfc3dSmrg if (c == '_')
2781debfc3dSmrg return true;
2791debfc3dSmrg return false;
2801debfc3dSmrg }
2811debfc3dSmrg
2821debfc3dSmrg /* Replace all not valid assembler symbols with '_'. */
2831debfc3dSmrg
2841debfc3dSmrg static void
create_new_asm_name(char * old_asm_name,char * new_asm_name)2851debfc3dSmrg create_new_asm_name (char *old_asm_name, char *new_asm_name)
2861debfc3dSmrg {
2871debfc3dSmrg int i;
2881debfc3dSmrg int old_name_len = strlen (old_asm_name);
2891debfc3dSmrg
2901debfc3dSmrg /* Replace all not valid assembler symbols with '_'. */
2911debfc3dSmrg for (i = 0; i < old_name_len; i++)
2921debfc3dSmrg if (!is_valid_asm_symbol (old_asm_name[i]))
2931debfc3dSmrg new_asm_name[i] = '_';
2941debfc3dSmrg else
2951debfc3dSmrg new_asm_name[i] = old_asm_name[i];
2961debfc3dSmrg new_asm_name[old_name_len] = '\0';
2971debfc3dSmrg }
2981debfc3dSmrg
2991debfc3dSmrg /* Creates target clone of NODE. */
3001debfc3dSmrg
3011debfc3dSmrg static cgraph_node *
create_target_clone(cgraph_node * node,bool definition,char * name,tree attributes)302a2dc1f3fSmrg create_target_clone (cgraph_node *node, bool definition, char *name,
303a2dc1f3fSmrg tree attributes)
3041debfc3dSmrg {
3051debfc3dSmrg cgraph_node *new_node;
3061debfc3dSmrg
3071debfc3dSmrg if (definition)
3081debfc3dSmrg {
3091debfc3dSmrg new_node = node->create_version_clone_with_body (vNULL, NULL,
3101debfc3dSmrg NULL, NULL,
311*8feb0f0bSmrg NULL, name, attributes);
312a2dc1f3fSmrg if (new_node == NULL)
313a2dc1f3fSmrg return NULL;
3141debfc3dSmrg new_node->force_output = true;
3151debfc3dSmrg }
3161debfc3dSmrg else
3171debfc3dSmrg {
3181debfc3dSmrg tree new_decl = copy_node (node->decl);
3191debfc3dSmrg new_node = cgraph_node::get_create (new_decl);
320a2dc1f3fSmrg DECL_ATTRIBUTES (new_decl) = attributes;
3211debfc3dSmrg /* Generate a new name for the new version. */
3221debfc3dSmrg symtab->change_decl_assembler_name (new_node->decl,
323c0a68be4Smrg clone_function_name_numbered (
324c0a68be4Smrg node->decl, name));
3251debfc3dSmrg }
3261debfc3dSmrg return new_node;
3271debfc3dSmrg }
3281debfc3dSmrg
3291debfc3dSmrg /* If the function in NODE has multiple target attributes
3301debfc3dSmrg create the appropriate clone for each valid target attribute. */
3311debfc3dSmrg
3321debfc3dSmrg static bool
expand_target_clones(struct cgraph_node * node,bool definition)3331debfc3dSmrg expand_target_clones (struct cgraph_node *node, bool definition)
3341debfc3dSmrg {
3351debfc3dSmrg int i;
3361debfc3dSmrg /* Parsing target attributes separated by comma. */
3371debfc3dSmrg tree attr_target = lookup_attribute ("target_clones",
3381debfc3dSmrg DECL_ATTRIBUTES (node->decl));
3391debfc3dSmrg /* No targets specified. */
3401debfc3dSmrg if (!attr_target)
3411debfc3dSmrg return false;
3421debfc3dSmrg
3431debfc3dSmrg tree arglist = TREE_VALUE (attr_target);
3441debfc3dSmrg int attr_len = get_attr_len (arglist);
3451debfc3dSmrg
3461debfc3dSmrg /* No need to clone for 1 target attribute. */
3471debfc3dSmrg if (attr_len == -1)
3481debfc3dSmrg {
3491debfc3dSmrg warning_at (DECL_SOURCE_LOCATION (node->decl),
350c0a68be4Smrg 0, "single %<target_clones%> attribute is ignored");
351a2dc1f3fSmrg return false;
352a2dc1f3fSmrg }
353a2dc1f3fSmrg
354a2dc1f3fSmrg if (node->definition
355*8feb0f0bSmrg && (node->alias || !tree_versionable_function_p (node->decl)))
356a2dc1f3fSmrg {
357c0a68be4Smrg auto_diagnostic_group d;
358a2dc1f3fSmrg error_at (DECL_SOURCE_LOCATION (node->decl),
359a2dc1f3fSmrg "clones for %<target_clones%> attribute cannot be created");
360a2dc1f3fSmrg const char *reason = NULL;
361a2dc1f3fSmrg if (lookup_attribute ("noclone", DECL_ATTRIBUTES (node->decl)))
362a2dc1f3fSmrg reason = G_("function %q+F can never be copied "
363a2dc1f3fSmrg "because it has %<noclone%> attribute");
364*8feb0f0bSmrg else if (node->alias)
365*8feb0f0bSmrg reason
366*8feb0f0bSmrg = "%<target_clones%> cannot be combined with %<alias%> attribute";
367a2dc1f3fSmrg else
368a2dc1f3fSmrg reason = copy_forbidden (DECL_STRUCT_FUNCTION (node->decl));
369a2dc1f3fSmrg if (reason)
370a2dc1f3fSmrg inform (DECL_SOURCE_LOCATION (node->decl), reason, node->decl);
3711debfc3dSmrg return false;
3721debfc3dSmrg }
3731debfc3dSmrg
3741debfc3dSmrg char *attr_str = XNEWVEC (char, attr_len);
3751debfc3dSmrg int attrnum = get_attr_str (arglist, attr_str);
3761debfc3dSmrg char **attrs = XNEWVEC (char *, attrnum);
3771debfc3dSmrg
378a2dc1f3fSmrg attrnum = separate_attrs (attr_str, attrs, attrnum);
379c0a68be4Smrg switch (attrnum)
3801debfc3dSmrg {
381c0a68be4Smrg case -1:
3821debfc3dSmrg error_at (DECL_SOURCE_LOCATION (node->decl),
383c0a68be4Smrg "%<default%> target was not set");
384c0a68be4Smrg break;
385c0a68be4Smrg case -2:
386a2dc1f3fSmrg error_at (DECL_SOURCE_LOCATION (node->decl),
387a2dc1f3fSmrg "an empty string cannot be in %<target_clones%> attribute");
388c0a68be4Smrg break;
389c0a68be4Smrg case -3:
390c0a68be4Smrg error_at (DECL_SOURCE_LOCATION (node->decl),
391c0a68be4Smrg "multiple %<default%> targets were set");
392c0a68be4Smrg break;
393c0a68be4Smrg default:
394c0a68be4Smrg break;
395c0a68be4Smrg }
396c0a68be4Smrg
397c0a68be4Smrg if (attrnum < 0)
398c0a68be4Smrg {
399a2dc1f3fSmrg XDELETEVEC (attrs);
400a2dc1f3fSmrg XDELETEVEC (attr_str);
401a2dc1f3fSmrg return false;
402a2dc1f3fSmrg }
4031debfc3dSmrg
4041debfc3dSmrg cgraph_function_version_info *decl1_v = NULL;
4051debfc3dSmrg cgraph_function_version_info *decl2_v = NULL;
4061debfc3dSmrg cgraph_function_version_info *before = NULL;
4071debfc3dSmrg cgraph_function_version_info *after = NULL;
4081debfc3dSmrg decl1_v = node->function_version ();
4091debfc3dSmrg if (decl1_v == NULL)
4101debfc3dSmrg decl1_v = node->insert_new_function_version ();
4111debfc3dSmrg before = decl1_v;
4121debfc3dSmrg DECL_FUNCTION_VERSIONED (node->decl) = 1;
4131debfc3dSmrg
4141debfc3dSmrg for (i = 0; i < attrnum; i++)
4151debfc3dSmrg {
4161debfc3dSmrg char *attr = attrs[i];
4171debfc3dSmrg char *suffix = XNEWVEC (char, strlen (attr) + 1);
4181debfc3dSmrg
4191debfc3dSmrg create_new_asm_name (attr, suffix);
4201debfc3dSmrg /* Create new target clone. */
421a2dc1f3fSmrg tree attributes = make_attribute ("target", attr,
422a2dc1f3fSmrg DECL_ATTRIBUTES (node->decl));
423a2dc1f3fSmrg
424a2dc1f3fSmrg cgraph_node *new_node = create_target_clone (node, definition, suffix,
425a2dc1f3fSmrg attributes);
426a2dc1f3fSmrg if (new_node == NULL)
427a2dc1f3fSmrg return false;
428*8feb0f0bSmrg new_node->local = false;
4291debfc3dSmrg XDELETEVEC (suffix);
4301debfc3dSmrg
4311debfc3dSmrg decl2_v = new_node->function_version ();
4321debfc3dSmrg if (decl2_v != NULL)
4331debfc3dSmrg continue;
4341debfc3dSmrg decl2_v = new_node->insert_new_function_version ();
4351debfc3dSmrg
4361debfc3dSmrg /* Chain decl2_v and decl1_v. All semantically identical versions
4371debfc3dSmrg will be chained together. */
4381debfc3dSmrg after = decl2_v;
4391debfc3dSmrg while (before->next != NULL)
4401debfc3dSmrg before = before->next;
4411debfc3dSmrg while (after->prev != NULL)
4421debfc3dSmrg after = after->prev;
4431debfc3dSmrg
4441debfc3dSmrg before->next = after;
4451debfc3dSmrg after->prev = before;
4461debfc3dSmrg DECL_FUNCTION_VERSIONED (new_node->decl) = 1;
4471debfc3dSmrg }
4481debfc3dSmrg
4491debfc3dSmrg XDELETEVEC (attrs);
4501debfc3dSmrg XDELETEVEC (attr_str);
4511debfc3dSmrg
4521debfc3dSmrg /* Setting new attribute to initial function. */
4531debfc3dSmrg tree attributes = make_attribute ("target", "default",
4541debfc3dSmrg DECL_ATTRIBUTES (node->decl));
4551debfc3dSmrg DECL_ATTRIBUTES (node->decl) = attributes;
456*8feb0f0bSmrg node->local = false;
457a2dc1f3fSmrg return true;
4581debfc3dSmrg }
4591debfc3dSmrg
460c0a68be4Smrg /* When NODE is a target clone, consider all callees and redirect
461c0a68be4Smrg to a clone with equal target attributes. That prevents multiple
462c0a68be4Smrg multi-versioning dispatches and a call-chain can be optimized. */
463c0a68be4Smrg
464c0a68be4Smrg static void
redirect_to_specific_clone(cgraph_node * node)465c0a68be4Smrg redirect_to_specific_clone (cgraph_node *node)
466c0a68be4Smrg {
467c0a68be4Smrg cgraph_function_version_info *fv = node->function_version ();
468c0a68be4Smrg if (fv == NULL)
469c0a68be4Smrg return;
470c0a68be4Smrg
471c0a68be4Smrg tree attr_target = lookup_attribute ("target", DECL_ATTRIBUTES (node->decl));
472c0a68be4Smrg if (attr_target == NULL_TREE)
473c0a68be4Smrg return;
474c0a68be4Smrg
475c0a68be4Smrg /* We need to remember NEXT_CALLER as it could be modified in the loop. */
476c0a68be4Smrg for (cgraph_edge *e = node->callees; e ; e = e->next_callee)
477c0a68be4Smrg {
478c0a68be4Smrg cgraph_function_version_info *fv2 = e->callee->function_version ();
479c0a68be4Smrg if (!fv2)
480c0a68be4Smrg continue;
481c0a68be4Smrg
482c0a68be4Smrg tree attr_target2 = lookup_attribute ("target",
483c0a68be4Smrg DECL_ATTRIBUTES (e->callee->decl));
484c0a68be4Smrg
485c0a68be4Smrg /* Function is not calling proper target clone. */
486c0a68be4Smrg if (!attribute_list_equal (attr_target, attr_target2))
487c0a68be4Smrg {
488c0a68be4Smrg while (fv2->prev != NULL)
489c0a68be4Smrg fv2 = fv2->prev;
490c0a68be4Smrg
491c0a68be4Smrg /* Try to find a clone with equal target attribute. */
492c0a68be4Smrg for (; fv2 != NULL; fv2 = fv2->next)
493c0a68be4Smrg {
494c0a68be4Smrg cgraph_node *callee = fv2->this_node;
495c0a68be4Smrg attr_target2 = lookup_attribute ("target",
496c0a68be4Smrg DECL_ATTRIBUTES (callee->decl));
497c0a68be4Smrg if (attribute_list_equal (attr_target, attr_target2))
498c0a68be4Smrg {
499c0a68be4Smrg e->redirect_callee (callee);
500*8feb0f0bSmrg cgraph_edge::redirect_call_stmt_to_callee (e);
501c0a68be4Smrg break;
502c0a68be4Smrg }
503c0a68be4Smrg }
504c0a68be4Smrg }
505c0a68be4Smrg }
506c0a68be4Smrg }
507c0a68be4Smrg
5081debfc3dSmrg static unsigned int
ipa_target_clone(void)5091debfc3dSmrg ipa_target_clone (void)
5101debfc3dSmrg {
5111debfc3dSmrg struct cgraph_node *node;
512a2dc1f3fSmrg auto_vec<cgraph_node *> to_dispatch;
5131debfc3dSmrg
5141debfc3dSmrg FOR_EACH_FUNCTION (node)
515a2dc1f3fSmrg if (expand_target_clones (node, node->definition))
516a2dc1f3fSmrg to_dispatch.safe_push (node);
5171debfc3dSmrg
518a2dc1f3fSmrg for (unsigned i = 0; i < to_dispatch.length (); i++)
519a2dc1f3fSmrg create_dispatcher_calls (to_dispatch[i]);
5201debfc3dSmrg
521c0a68be4Smrg FOR_EACH_FUNCTION (node)
522c0a68be4Smrg redirect_to_specific_clone (node);
523c0a68be4Smrg
5241debfc3dSmrg return 0;
5251debfc3dSmrg }
5261debfc3dSmrg
5271debfc3dSmrg namespace {
5281debfc3dSmrg
5291debfc3dSmrg const pass_data pass_data_target_clone =
5301debfc3dSmrg {
5311debfc3dSmrg SIMPLE_IPA_PASS, /* type */
5321debfc3dSmrg "targetclone", /* name */
5331debfc3dSmrg OPTGROUP_NONE, /* optinfo_flags */
5341debfc3dSmrg TV_NONE, /* tv_id */
5351debfc3dSmrg ( PROP_ssa | PROP_cfg ), /* properties_required */
5361debfc3dSmrg 0, /* properties_provided */
5371debfc3dSmrg 0, /* properties_destroyed */
5381debfc3dSmrg 0, /* todo_flags_start */
5391debfc3dSmrg TODO_update_ssa /* todo_flags_finish */
5401debfc3dSmrg };
5411debfc3dSmrg
5421debfc3dSmrg class pass_target_clone : public simple_ipa_opt_pass
5431debfc3dSmrg {
5441debfc3dSmrg public:
pass_target_clone(gcc::context * ctxt)5451debfc3dSmrg pass_target_clone (gcc::context *ctxt)
5461debfc3dSmrg : simple_ipa_opt_pass (pass_data_target_clone, ctxt)
5471debfc3dSmrg {}
5481debfc3dSmrg
5491debfc3dSmrg /* opt_pass methods: */
5501debfc3dSmrg virtual bool gate (function *);
execute(function *)5511debfc3dSmrg virtual unsigned int execute (function *) { return ipa_target_clone (); }
5521debfc3dSmrg };
5531debfc3dSmrg
5541debfc3dSmrg bool
gate(function *)5551debfc3dSmrg pass_target_clone::gate (function *)
5561debfc3dSmrg {
5571debfc3dSmrg return true;
5581debfc3dSmrg }
5591debfc3dSmrg
5601debfc3dSmrg } // anon namespace
5611debfc3dSmrg
5621debfc3dSmrg simple_ipa_opt_pass *
make_pass_target_clone(gcc::context * ctxt)5631debfc3dSmrg make_pass_target_clone (gcc::context *ctxt)
5641debfc3dSmrg {
5651debfc3dSmrg return new pass_target_clone (ctxt);
5661debfc3dSmrg }
567