Line data Source code
1 : /* Pass for parsing functions with multiple target attributes.
2 :
3 : Contributed by Evgeny Stupachenko <evstupac@gmail.com>
4 :
5 : Copyright (C) 2015-2026 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
46 127 : replace_function_decl (tree *op, int *walk_subtrees, void *data)
47 : {
48 127 : struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
49 127 : cgraph_function_version_info *info = (cgraph_function_version_info *)wi->info;
50 :
51 127 : if (TREE_CODE (*op) == FUNCTION_DECL
52 50 : && info->this_node->decl == *op)
53 : {
54 26 : *op = info->dispatcher_resolver;
55 26 : *walk_subtrees = 0;
56 : }
57 :
58 127 : return NULL;
59 : }
60 :
61 : /* In target FMV attributes, if the call in NODE has multiple target attribute
62 : with multiple fields, replace it with calls to the dispatched symbol and
63 : create the dispatcher body (once).
64 :
65 : In target_version semantics, if it is a lone annotated default, then
66 : the dispatched symbol is changed to be an alias and no resolver is
67 : required. Otherwise, redirect all calls and references to the dispatched
68 : symbol, but only create the resolver body if the default version is
69 : implemented. */
70 :
71 : static void
72 84 : create_dispatcher_calls (struct cgraph_node *node)
73 : {
74 84 : ipa_ref *ref;
75 :
76 84 : if (!targetm.has_ifunc_p ())
77 : {
78 0 : error_at (DECL_SOURCE_LOCATION (node->decl),
79 : "the call requires %<ifunc%>, which is not"
80 : " supported by this target");
81 0 : return;
82 : }
83 84 : else if (!targetm.get_function_versions_dispatcher)
84 : {
85 0 : error_at (DECL_SOURCE_LOCATION (node->decl),
86 : "target does not support function version dispatcher");
87 0 : return;
88 : }
89 :
90 84 : tree idecl = targetm.get_function_versions_dispatcher (node->decl);
91 84 : if (!idecl)
92 : {
93 0 : error_at (DECL_SOURCE_LOCATION (node->decl),
94 : "default %<target_clones%> attribute was not set");
95 0 : return;
96 : }
97 :
98 84 : cgraph_node *inode = cgraph_node::get (idecl);
99 84 : gcc_assert (inode);
100 84 : cgraph_function_version_info *inode_info = inode->function_version ();
101 84 : gcc_assert (inode_info);
102 :
103 84 : tree resolver_decl = NULL;
104 :
105 : /* For target_version semantics, if there is a lone default declaration
106 : it needs to be mangled, with an alias from the dispatched symbol to the
107 : default version. */
108 84 : if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
109 : && TREE_STATIC (node->decl)
110 : && inode_info->next
111 : && !inode_info->next->next)
112 : {
113 : inode->alias = true;
114 : inode->alias_target = inode_info->next->this_node->decl;
115 : inode->externally_visible = true;
116 : if (!inode->analyzed)
117 : inode->resolve_alias
118 : (cgraph_node::get (inode_info->next->this_node->decl));
119 :
120 : DECL_ATTRIBUTES (idecl)
121 : = make_attribute ("alias",
122 : IDENTIFIER_POINTER
123 : (DECL_ASSEMBLER_NAME
124 : (inode_info->next->this_node->decl)),
125 : DECL_ATTRIBUTES (node->decl));
126 : TREE_USED (idecl) = true;
127 : DECL_EXTERNAL (idecl) = false;
128 : TREE_STATIC (idecl) = true;
129 : return;
130 : }
131 : /* In target_version semantics, only create the resolver if the
132 : default node is implemented. */
133 84 : else if (TARGET_HAS_FMV_TARGET_ATTRIBUTE || TREE_STATIC (node->decl))
134 : {
135 84 : resolver_decl = targetm.generate_version_dispatcher_body (inode);
136 : /* Update aliases. */
137 84 : inode->alias = true;
138 84 : inode->alias_target = resolver_decl;
139 84 : if (!inode->analyzed)
140 0 : inode->resolve_alias (cgraph_node::get (resolver_decl));
141 : }
142 :
143 84 : auto_vec<cgraph_edge *> edges_to_redirect;
144 : /* We need to capture the references by value rather than just pointers to them
145 : and remove them right away, as removing them later would invalidate what
146 : some other reference pointers point to. */
147 84 : auto_vec<ipa_ref> references_to_redirect;
148 :
149 198 : while (node->iterate_referring (0, ref))
150 : {
151 114 : references_to_redirect.safe_push (*ref);
152 114 : ref->remove_reference ();
153 : }
154 :
155 : /* We need to remember NEXT_CALLER as it could be modified in the loop. */
156 147 : for (cgraph_edge *e = node->callers; e ; e = e->next_caller)
157 63 : edges_to_redirect.safe_push (e);
158 :
159 84 : if (!edges_to_redirect.is_empty () || !references_to_redirect.is_empty ())
160 : {
161 : /* Redirect edges. */
162 : unsigned i;
163 : cgraph_edge *e;
164 147 : FOR_EACH_VEC_ELT (edges_to_redirect, i, e)
165 : {
166 63 : e->redirect_callee (inode);
167 63 : cgraph_edge::redirect_call_stmt_to_callee (e);
168 : }
169 :
170 : /* Redirect references. */
171 198 : FOR_EACH_VEC_ELT (references_to_redirect, i, ref)
172 : {
173 114 : if (ref->use == IPA_REF_ADDR)
174 : {
175 110 : struct walk_stmt_info wi;
176 110 : memset (&wi, 0, sizeof (wi));
177 110 : wi.info = (void *)node->function_version ();
178 :
179 110 : if (dyn_cast<varpool_node *> (ref->referring))
180 : {
181 1 : hash_set<tree> visited_nodes;
182 1 : walk_tree (&DECL_INITIAL (ref->referring->decl),
183 : replace_function_decl, &wi, &visited_nodes);
184 1 : }
185 : else
186 : {
187 109 : gimple_stmt_iterator it = gsi_for_stmt (ref->stmt);
188 109 : if (ref->referring->decl != resolver_decl)
189 25 : walk_gimple_stmt (&it, NULL, replace_function_decl, &wi);
190 : }
191 :
192 110 : symtab_node *source = ref->referring;
193 110 : source->create_reference (inode, IPA_REF_ADDR);
194 : }
195 4 : else if (ref->use == IPA_REF_ALIAS)
196 : {
197 4 : symtab_node *source = ref->referring;
198 4 : source->create_reference (inode, IPA_REF_ALIAS);
199 4 : if (inode->get_comdat_group ())
200 : {
201 3 : if (source->same_comdat_group)
202 0 : source->remove_from_same_comdat_group ();
203 3 : source->add_to_same_comdat_group (inode);
204 : }
205 : }
206 : else
207 0 : gcc_unreachable ();
208 : }
209 : }
210 :
211 84 : if (node->definition)
212 : {
213 : /* FIXME: copy of cgraph_node::make_local that should be cleaned up
214 : in next stage1. */
215 76 : node->make_decl_local ();
216 76 : node->set_section (NULL);
217 76 : node->set_comdat_group (NULL);
218 76 : node->externally_visible = false;
219 76 : node->forced_by_abi = false;
220 :
221 76 : DECL_ARTIFICIAL (node->decl) = 1;
222 76 : node->force_output = true;
223 : }
224 84 : }
225 :
226 : /* Creates target clone of NODE. */
227 :
228 : static cgraph_node *
229 155 : create_target_clone (cgraph_node *node, bool definition, char *name,
230 : tree attributes)
231 : {
232 155 : cgraph_node *new_node;
233 :
234 155 : if (definition)
235 : {
236 124 : new_node
237 124 : = node->create_version_clone_with_body (vNULL, NULL, NULL, NULL, NULL,
238 : name, attributes, false);
239 124 : if (new_node == NULL)
240 : return NULL;
241 123 : new_node->force_output = true;
242 : }
243 : else
244 : {
245 31 : tree new_decl = copy_node (node->decl);
246 31 : new_node = cgraph_node::get_create (new_decl);
247 31 : DECL_ATTRIBUTES (new_decl) = attributes;
248 : /* Generate a new name for the new version. */
249 31 : tree fname = clone_function_name (node->decl, name);
250 31 : symtab->change_decl_assembler_name (new_node->decl, fname);
251 : }
252 : return new_node;
253 : }
254 :
255 : /* If the function in NODE has multiple target attributes
256 : create the appropriate clone for each valid target attribute. */
257 :
258 : static bool
259 3584419 : expand_target_clones (struct cgraph_node *node, bool definition)
260 : {
261 : /* Parsing target attributes separated by TARGET_CLONES_ATTR_SEPARATOR. */
262 3584419 : tree attr_target = lookup_attribute ("target_clones",
263 3584419 : DECL_ATTRIBUTES (node->decl));
264 : /* No targets specified. */
265 3584419 : if (!attr_target)
266 : return false;
267 :
268 94 : int num_defaults = 0;
269 94 : auto_vec<string_slice> attr_list = get_clone_versions (node->decl,
270 94 : &num_defaults);
271 :
272 : /* If the target clones list is empty after filtering, remove this node. */
273 94 : if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE && attr_list.is_empty ())
274 : {
275 : node->remove ();
276 : return false;
277 : }
278 :
279 : /* No need to clone for 1 target attribute. */
280 94 : if (attr_list.length () == 1 && TARGET_HAS_FMV_TARGET_ATTRIBUTE)
281 : {
282 0 : warning_at (DECL_SOURCE_LOCATION (node->decl),
283 0 : 0, "single %<target_clones%> attribute is ignored");
284 0 : return false;
285 : }
286 :
287 : /* For target_version semantics, a target clone with just a default version
288 : is the same as an unannotated decl, so can ignore. */
289 94 : if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE
290 : && attr_list.length () == 1
291 : && num_defaults == 1)
292 : return false;
293 :
294 94 : if (node->definition
295 94 : && (node->alias || !tree_versionable_function_p (node->decl)))
296 : {
297 6 : auto_diagnostic_group d;
298 6 : error_at (DECL_SOURCE_LOCATION (node->decl),
299 : "clones for %<target_clones%> attribute cannot be created");
300 6 : const char *reason = NULL;
301 6 : if (lookup_attribute ("noclone", DECL_ATTRIBUTES (node->decl)))
302 : reason = G_("function %q+F can never be copied "
303 : "because it has %<noclone%> attribute");
304 3 : else if (node->alias)
305 : reason
306 : = "%<target_clones%> cannot be combined with %<alias%> attribute";
307 : else
308 2 : reason = copy_forbidden (DECL_STRUCT_FUNCTION (node->decl));
309 2 : if (reason)
310 6 : inform (DECL_SOURCE_LOCATION (node->decl), reason, node->decl);
311 6 : return false;
312 6 : }
313 :
314 : /* Disallow multiple defaults. */
315 88 : if (num_defaults > 1)
316 : {
317 1 : error_at (DECL_SOURCE_LOCATION (node->decl),
318 : "multiple %<default%> targets were set");
319 1 : return false;
320 : }
321 :
322 : /* For target FMV semantics, where target and target_clone mixing
323 : is not supported, disallow target clones with no defaults. */
324 87 : if (TARGET_HAS_FMV_TARGET_ATTRIBUTE && num_defaults == 0)
325 : {
326 1 : error_at (DECL_SOURCE_LOCATION (node->decl),
327 : "%<default%> target was not set");
328 1 : return false;
329 : }
330 :
331 : /* Disallow any empty values in the clone attr. */
332 500 : for (string_slice attr : attr_list)
333 485 : if (attr.empty () || !attr.is_valid ())
334 : {
335 1 : error_at (DECL_SOURCE_LOCATION (node->decl),
336 : "an empty string cannot be in %<target_clones%> attribute");
337 1 : return false;
338 : }
339 :
340 85 : string_slice new_attr_name = TARGET_HAS_FMV_TARGET_ATTRIBUTE
341 : ? "target"
342 85 : : "target_version";
343 :
344 85 : cgraph_function_version_info *node_v = node->function_version ();
345 :
346 85 : if (!node_v)
347 85 : node_v = node->insert_new_function_version ();
348 :
349 : /* If this target_clones contains a default, then convert this node to the
350 : default. If this node does not contain default (this is only possible
351 : in target_version semantics) then remove the node. This is safe at this
352 : point as only target_clones declarations containing default version is
353 : resolvable so this decl will have no calls/references. */
354 :
355 85 : tree attrs = remove_attribute ("target_clones",
356 85 : DECL_ATTRIBUTES (node->decl));
357 85 : tree assembler_name = node_v->assembler_name;
358 :
359 : /* Change the current node into the default node. */
360 85 : if (num_defaults == 1)
361 : {
362 : /* Setting new attribute to initial function. */
363 85 : tree attributes = make_attribute (new_attr_name, "default", attrs);
364 85 : DECL_ATTRIBUTES (node->decl) = attributes;
365 85 : DECL_FUNCTION_VERSIONED (node->decl) = true;
366 :
367 85 : node->is_target_clone = true;
368 85 : node->local = false;
369 :
370 : /* Remangle base node after new target version string set. */
371 85 : tree id = targetm.mangle_decl_assembler_name (node->decl, assembler_name);
372 85 : symtab->change_decl_assembler_name (node->decl, id);
373 : }
374 : else
375 : {
376 : /* Target clones without a default are only allowed for target_version
377 : semantics where we can have target_clones/target_version mixing. */
378 0 : gcc_assert (!TARGET_HAS_FMV_TARGET_ATTRIBUTE);
379 :
380 : /* If there isn't a default version, can safely remove this version.
381 : The node itself gets removed after the other versions are created. */
382 : cgraph_function_version_info *temp = node_v;
383 : node_v = node_v->next ? node_v->next : node_v->prev;
384 : cgraph_node::delete_function_version (temp);
385 : }
386 :
387 493 : for (string_slice attr : attr_list)
388 : {
389 : /* Skip default nodes. */
390 239 : if (attr == "default")
391 84 : continue;
392 :
393 : /* Create new target clone. */
394 155 : tree attributes = make_attribute (new_attr_name, attr, attrs);
395 :
396 155 : cgraph_node *new_node
397 155 : = create_target_clone (node, definition, NULL, attributes);
398 155 : if (new_node == NULL)
399 1 : return false;
400 154 : new_node->local = false;
401 :
402 154 : DECL_FUNCTION_VERSIONED (new_node->decl) = true;
403 154 : if (!node_v)
404 0 : node_v = new_node->insert_new_function_version ();
405 : else
406 154 : cgraph_node::add_function_version (node_v, new_node->decl);
407 :
408 : /* Use the base node's assembler name for all created nodes. */
409 154 : new_node->function_version ()->assembler_name = assembler_name;
410 154 : new_node->is_target_clone = true;
411 :
412 : /* Mangle all new nodes. */
413 154 : tree id = targetm.mangle_decl_assembler_name
414 154 : (new_node->decl, new_node->function_version ()->assembler_name);
415 154 : symtab->change_decl_assembler_name (new_node->decl, id);
416 : }
417 :
418 : /* If there are no default versions in the target_clones, this node is not
419 : reused, so can delete this node. */
420 84 : if (num_defaults == 0)
421 0 : node->remove ();
422 :
423 : return true;
424 94 : }
425 :
426 : /* When NODE is part of an FMV function set, consider all callees and check if
427 : any can provably always resolve a certain version and then call that version
428 : directly. */
429 :
430 : static void
431 3584888 : redirect_to_specific_clone (cgraph_node *node)
432 : {
433 3584888 : if (!targetm.compare_version_priority || !optimize)
434 : return;
435 :
436 : /* We need to remember NEXT_CALLER as it could be modified in the loop. */
437 8121103 : for (cgraph_edge *e = node->callees; e ; e = e->next_callee)
438 : {
439 : /* Only if this is a call to a dispatched symbol. */
440 5348293 : if (!e->callee->dispatcher_function)
441 5348207 : continue;
442 :
443 86 : cgraph_function_version_info *callee_v
444 86 : = e->callee->function_version ();
445 86 : cgraph_function_version_info *caller_v
446 86 : = e->caller->function_version ();
447 :
448 86 : gcc_assert (callee_v);
449 :
450 : /* Find the default nodes for both callee and caller (if present). */
451 86 : cgraph_function_version_info *callee_default_v = callee_v->next;
452 86 : cgraph_function_version_info *caller_default_v = caller_v;
453 86 : if (caller_v)
454 : {
455 9 : while (caller_default_v->prev)
456 : caller_default_v = caller_default_v->prev;
457 7 : if (!is_function_default_version (caller_default_v->this_node->decl))
458 79 : caller_default_v = NULL;
459 : }
460 :
461 : /* If this is not the TU that contains the definition of the default
462 : version we are not guaranteed to have visibility of all versions
463 : so cannot reason about them. */
464 101 : if (!callee_default_v
465 86 : || !callee_default_v->this_node->binds_to_current_def_p ())
466 15 : continue;
467 :
468 71 : cgraph_function_version_info *highest_callable_fn = NULL;
469 71 : for (cgraph_function_version_info *ver = callee_v->next;
470 444 : ver;
471 373 : ver = ver->next)
472 373 : if (targetm.target_option.functions_b_resolvable_from_a
473 373 : (node->decl, ver->this_node->decl, node->decl))
474 7 : highest_callable_fn = ver;
475 :
476 71 : if (!highest_callable_fn)
477 64 : continue;
478 :
479 7 : bool inlinable = true;
480 :
481 : /* If there are higher priority versions of callee and caller has no
482 : more version information, then not callable. */
483 7 : if (highest_callable_fn->next)
484 : {
485 : /* If this is not the TU where the callee default is defined then
486 : cannot reason about the caller versions. */
487 5 : if (!caller_default_v
488 5 : || !caller_default_v->this_node->binds_to_current_def_p ())
489 0 : continue;
490 :
491 : /* If every higher priority version would imply a higher priority
492 : version of caller would have been selected, then this is
493 : callable. */
494 5 : for (cgraph_function_version_info *callee_ver
495 : = highest_callable_fn->next;
496 10 : callee_ver; callee_ver = callee_ver->next)
497 : {
498 5 : bool is_possible = true;
499 5 : for (cgraph_function_version_info *caller_ver = caller_v->next;
500 5 : caller_ver; caller_ver = caller_ver->next)
501 5 : if (targetm.target_option.functions_b_resolvable_from_a
502 5 : (callee_ver->this_node->decl, caller_ver->this_node->decl,
503 : node->decl))
504 : {
505 : is_possible = false;
506 : break;
507 : }
508 5 : if (is_possible)
509 : {
510 : inlinable = false;
511 : break;
512 : }
513 : }
514 : }
515 5 : if (inlinable)
516 : {
517 7 : e->redirect_callee (highest_callable_fn->this_node);
518 7 : cgraph_edge::redirect_call_stmt_to_callee (e);
519 : }
520 : }
521 : }
522 :
523 : /* Checks if NODE is in the 'simple' target_clones case, which is where NODE
524 : is a declaration annotated with target_clones containing the default, and it
525 : is the sole function declaration in the FMV function set. */
526 :
527 : static bool
528 0 : is_simple_target_clones_case (cgraph_node *node)
529 : {
530 : /* target attribute semantics doesnt support the complex case,
531 : so this is always true. */
532 0 : if (TARGET_HAS_FMV_TARGET_ATTRIBUTE)
533 0 : return true;
534 :
535 : int num_defaults = 0;
536 : auto versions = get_clone_versions (node->decl, &num_defaults);
537 : if (versions.is_empty () || num_defaults != 1)
538 : return false;
539 :
540 : cgraph_function_version_info *fv = node->function_version ();
541 :
542 : if (fv && (fv->next || fv->prev))
543 : return false;
544 :
545 : return true;
546 : }
547 :
548 : static unsigned int
549 459709 : ipa_target_clone (bool early)
550 : {
551 459709 : struct cgraph_node *node;
552 459709 : auto_vec<cgraph_node *> to_dispatch;
553 :
554 : /* Don't need to do anything early for target attribute semantics. */
555 459709 : if (early && TARGET_HAS_FMV_TARGET_ATTRIBUTE)
556 : return 0;
557 :
558 : /* For target attribute semantics, this pass skips the early phase, and in
559 : the later stage is only responsible for expanding and dispatching
560 : target_clone declarations, as target annotated functions are dispatched
561 : in the front end.
562 :
563 : The expanding and dispatching can be done at the late stage as the
564 : target_clone functions aren't allowed to be part of a larger FMV set, so
565 : all versions will all have the same body, so early optimisations are safe
566 : to treat a call to a target_clones set as a call to one function.
567 :
568 : For target_version semantics, this pass is responsible for expanding
569 : target_clones and dispatching all FMV function sets, including ones only
570 : made up of target_version declarations.
571 :
572 : Cases where there is more than one declaration must be expanded and
573 : dispatched at the early stage, as the declarations may have different
574 : bodies, and so the early optimisation passes would not be valid.
575 :
576 : The late stage is only used for the expansion and dispatching of the simple
577 : case where the FMV set is defined by a single target_clone attribute. */
578 :
579 7628520 : FOR_EACH_FUNCTION_REMOVABLE (node)
580 : {
581 : /* In the early stage, we need to expand any target clone that is not
582 : the simple case. Simple cases are dispatched in the later stage. */
583 :
584 3584419 : if (early == !is_simple_target_clones_case (node))
585 3584419 : if (expand_target_clones (node, node->definition)
586 3584419 : && TARGET_HAS_FMV_TARGET_ATTRIBUTE)
587 : /* In non target_version semantics, dispatch all target clones. */
588 84 : to_dispatch.safe_push (node);
589 : }
590 :
591 : /* In target_version semantics dispatch all FMV function sets with a default
592 : implementation in the early stage.
593 : Also dispatch any default versions generated by expanding target_clones
594 : in the late stage. */
595 :
596 : if (!TARGET_HAS_FMV_TARGET_ATTRIBUTE)
597 : FOR_EACH_FUNCTION (node)
598 : if (is_function_default_version (node->decl)
599 : && DECL_FUNCTION_VERSIONED (node->decl)
600 : /* Don't dispatch target clones, as they haven't been expanded so
601 : are simple. */
602 : && !lookup_attribute ("target_clones", DECL_ATTRIBUTES (node->decl)))
603 : to_dispatch.safe_push (node);
604 :
605 229925 : for (unsigned i = 0; i < to_dispatch.length (); i++)
606 84 : create_dispatcher_calls (to_dispatch[i]);
607 :
608 3814729 : FOR_EACH_FUNCTION (node)
609 3584888 : redirect_to_specific_clone (node);
610 :
611 : return 0;
612 459709 : }
613 :
614 : namespace {
615 :
616 : const pass_data pass_data_target_clone =
617 : {
618 : SIMPLE_IPA_PASS, /* type */
619 : "targetclone", /* name */
620 : OPTGROUP_NONE, /* optinfo_flags */
621 : TV_NONE, /* tv_id */
622 : ( PROP_ssa | PROP_cfg ), /* properties_required */
623 : 0, /* properties_provided */
624 : 0, /* properties_destroyed */
625 : 0, /* todo_flags_start */
626 : TODO_update_ssa /* todo_flags_finish */
627 : };
628 :
629 : class pass_target_clone : public simple_ipa_opt_pass
630 : {
631 : public:
632 571444 : pass_target_clone (gcc::context *ctxt)
633 1142888 : : simple_ipa_opt_pass (pass_data_target_clone, ctxt), early_p (false)
634 : {}
635 : bool early_p;
636 :
637 571444 : void set_pass_param (unsigned int n, bool param) final override
638 : {
639 571444 : gcc_assert (n == 0);
640 571444 : early_p = param;
641 571444 : }
642 : /* opt_pass methods: */
643 : bool gate (function *) final override;
644 285722 : opt_pass * clone () final override { return new pass_target_clone (m_ctxt); }
645 459709 : unsigned int execute (function *) final override
646 : {
647 459709 : return ipa_target_clone (early_p);
648 : }
649 : };
650 :
651 : bool
652 459920 : pass_target_clone::gate (function *)
653 : {
654 : /* If there were any errors avoid pass property verification errors. */
655 459920 : return !seen_error ();
656 : }
657 :
658 : } // anon namespace
659 :
660 : simple_ipa_opt_pass *
661 285722 : make_pass_target_clone (gcc::context *ctxt)
662 : {
663 285722 : return new pass_target_clone (ctxt);
664 : }
|