Branch data Line data Source code
1 : : /* Localize comdats.
2 : : Copyright (C) 2014-2024 Free Software Foundation, Inc.
3 : :
4 : : This file is part of GCC.
5 : :
6 : : GCC is free software; you can redistribute it and/or modify it under
7 : : the terms of the GNU General Public License as published by the Free
8 : : Software Foundation; either version 3, or (at your option) any later
9 : : version.
10 : :
11 : : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
12 : : WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 : : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 : : for more details.
15 : :
16 : : You should have received a copy of the GNU General Public License
17 : : along with GCC; see the file COPYING3. If not see
18 : : <http://www.gnu.org/licenses/>. */
19 : :
20 : : /* This is very simple pass that looks for static symbols that are used
21 : : exclusively by symbol within one comdat group. In this case it makes
22 : : sense to bring the symbol itself into the group to avoid dead code
23 : : that would arrise when the comdat group from current unit is replaced
24 : : by a different copy. Consider for example:
25 : :
26 : : static int q(void)
27 : : {
28 : : ....
29 : : }
30 : : inline int t(void)
31 : : {
32 : : return q();
33 : : }
34 : :
35 : : if Q is used only by T, it makes sense to put Q into T's comdat group.
36 : :
37 : : The pass solve simple dataflow across the callgraph trying to prove what
38 : : symbols are used exclusively from a given comdat group.
39 : :
40 : : The implementation maintains a queue linked by AUX pointer terminated by
41 : : pointer value 1. Lattice values are NULL for TOP, actual comdat group, or
42 : : ERROR_MARK_NODE for bottom.
43 : :
44 : : TODO: When symbol is used only by comdat symbols, but from different groups,
45 : : it would make sense to produce a new comdat group for it with anonymous name.
46 : :
47 : : TODO2: We can't mix variables and functions within one group. Currently
48 : : we just give up on references of symbols of different types. We also should
49 : : handle this by anonymous comdat group section. */
50 : :
51 : : #include "config.h"
52 : : #include "system.h"
53 : : #include "coretypes.h"
54 : : #include "tm.h"
55 : : #include "tree.h"
56 : : #include "tree-pass.h"
57 : : #include "cgraph.h"
58 : :
59 : : /* Main dataflow loop propagating comdat groups across
60 : : the symbol table. All references to SYMBOL are examined
61 : : and NEWGROUP is updated accordingly. MAP holds current lattice
62 : : values for individual symbols. */
63 : :
64 : : tree
65 : 72246 : propagate_comdat_group (struct symtab_node *symbol,
66 : : tree newgroup, hash_map<symtab_node *, tree> &map)
67 : : {
68 : 72246 : int i;
69 : 72246 : struct ipa_ref *ref;
70 : :
71 : : /* Walk all references to SYMBOL, recursively dive into aliases. */
72 : :
73 : 96321 : for (i = 0;
74 : 192642 : symbol->iterate_referring (i, ref)
75 : 96321 : && newgroup != error_mark_node; i++)
76 : : {
77 : 45549 : struct symtab_node *symbol2 = ref->referring;
78 : :
79 : 45549 : if (ref->use == IPA_REF_ALIAS)
80 : : {
81 : 1302 : newgroup = propagate_comdat_group (symbol2, newgroup, map);
82 : 1302 : continue;
83 : : }
84 : :
85 : : /* One COMDAT group cannot hold both variables and functions at
86 : : a same time. For now we just go to BOTTOM, in future we may
87 : : invent special comdat groups for this case. */
88 : :
89 : 44247 : if (symbol->type != symbol2->type)
90 : : {
91 : 21474 : newgroup = error_mark_node;
92 : 21474 : break;
93 : : }
94 : :
95 : : /* If we see inline clone, its comdat group actually
96 : : corresponds to the comdat group of the function it is inlined
97 : : to. */
98 : :
99 : 22773 : if (cgraph_node * cn = dyn_cast <cgraph_node *> (symbol2))
100 : : {
101 : 13620 : if (cn->inlined_to)
102 : 1859 : symbol2 = cn->inlined_to;
103 : : }
104 : :
105 : : /* The actual merge operation. */
106 : :
107 : 22773 : tree *val2 = map.get (symbol2);
108 : :
109 : 22773 : if (val2 && *val2 != newgroup)
110 : : {
111 : 21350 : if (!newgroup)
112 : : newgroup = *val2;
113 : : else
114 : 14 : newgroup = error_mark_node;
115 : : }
116 : : }
117 : :
118 : : /* If we analyze function, walk also callers. */
119 : :
120 : 72246 : cgraph_node *cnode = dyn_cast <cgraph_node *> (symbol);
121 : :
122 : 50262 : if (cnode)
123 : 50262 : for (struct cgraph_edge * edge = cnode->callers;
124 : 96517 : edge && newgroup != error_mark_node; edge = edge->next_caller)
125 : : {
126 : 46255 : struct symtab_node *symbol2 = edge->caller;
127 : :
128 : 46255 : if (cgraph_node * cn = dyn_cast <cgraph_node *> (symbol2))
129 : : {
130 : : /* Thunks cannot call across section boundary. */
131 : 46255 : if (cn->thunk)
132 : 427 : newgroup = propagate_comdat_group (symbol2, newgroup, map);
133 : : /* If we see inline clone, its comdat group actually
134 : : corresponds to the comdat group of the function it
135 : : is inlined to. */
136 : 46255 : if (cn->inlined_to)
137 : 16694 : symbol2 = cn->inlined_to;
138 : : }
139 : :
140 : : /* The actual merge operation. */
141 : :
142 : 46255 : tree *val2 = map.get (symbol2);
143 : :
144 : 46255 : if (val2 && *val2 != newgroup)
145 : : {
146 : 28491 : if (!newgroup)
147 : : newgroup = *val2;
148 : : else
149 : 4664 : newgroup = error_mark_node;
150 : : }
151 : : }
152 : 72246 : return newgroup;
153 : : }
154 : :
155 : :
156 : : /* Add all references of SYMBOL that are defined into queue started by FIRST
157 : : and linked by AUX pointer (unless they are already enqueued).
158 : : Walk recursively inlined functions. */
159 : :
160 : : void
161 : 161005 : enqueue_references (symtab_node **first,
162 : : symtab_node *symbol)
163 : : {
164 : 161005 : int i;
165 : 161005 : struct ipa_ref *ref = NULL;
166 : :
167 : 366694 : for (i = 0; symbol->iterate_reference (i, ref); i++)
168 : : {
169 : 205689 : symtab_node *node = ref->referred->ultimate_alias_target ();
170 : :
171 : : /* Always keep thunks in same sections as target function. */
172 : 205689 : if (is_a <cgraph_node *>(node))
173 : 22676 : node = dyn_cast <cgraph_node *> (node)->function_symbol ();
174 : 205689 : if (!node->aux && node->definition)
175 : : {
176 : 1200 : node->aux = *first;
177 : 1200 : *first = node;
178 : : }
179 : : }
180 : :
181 : 161005 : if (cgraph_node *cnode = dyn_cast <cgraph_node *> (symbol))
182 : : {
183 : 139993 : struct cgraph_edge *edge;
184 : :
185 : 565643 : for (edge = cnode->callees; edge; edge = edge->next_callee)
186 : 425650 : if (!edge->inline_failed)
187 : 94134 : enqueue_references (first, edge->callee);
188 : : else
189 : : {
190 : 331516 : symtab_node *node = edge->callee->ultimate_alias_target ();
191 : :
192 : : /* Always keep thunks in same sections as target function. */
193 : 331516 : if (is_a <cgraph_node *>(node))
194 : 331516 : node = dyn_cast <cgraph_node *> (node)->function_symbol ();
195 : 331516 : if (!node->aux && node->definition)
196 : : {
197 : 2530 : node->aux = *first;
198 : 2530 : *first = node;
199 : : }
200 : : }
201 : : }
202 : 161005 : }
203 : :
204 : : /* Set comdat group of SYMBOL to GROUP.
205 : : Callback for for_node_and_aliases. */
206 : :
207 : : bool
208 : 4829 : set_comdat_group (symtab_node *symbol,
209 : : void *head_p)
210 : : {
211 : 4829 : symtab_node *head = (symtab_node *)head_p;
212 : :
213 : 4829 : gcc_assert (!symbol->get_comdat_group ());
214 : 4829 : if (symbol->real_symbol_p ())
215 : : {
216 : 4829 : symbol->set_comdat_group (head->get_comdat_group ());
217 : 4829 : symbol->add_to_same_comdat_group (head);
218 : : }
219 : 4829 : return false;
220 : : }
221 : :
222 : : /* Set comdat group of SYMBOL to GROUP.
223 : : Callback for for_node_thunks_and_aliases. */
224 : :
225 : : bool
226 : 4823 : set_comdat_group_1 (cgraph_node *symbol,
227 : : void *head_p)
228 : : {
229 : 4823 : return set_comdat_group (symbol, head_p);
230 : : }
231 : :
232 : : /* The actual pass with the main dataflow loop. */
233 : :
234 : : static unsigned int
235 : 223972 : ipa_comdats (void)
236 : : {
237 : 223972 : hash_map<symtab_node *, tree> map (251);
238 : 223972 : hash_map<tree, symtab_node *> comdat_head_map (251);
239 : 223972 : symtab_node *symbol;
240 : 223972 : bool comdat_group_seen = false;
241 : 223972 : symtab_node *first = (symtab_node *) (void *) 1;
242 : 223972 : tree group;
243 : :
244 : : /* Start the dataflow by assigning comdat group to symbols that are in comdat
245 : : groups already. All other externally visible symbols must stay, we use
246 : : ERROR_MARK_NODE as bottom for the propagation. */
247 : :
248 : 11309278 : FOR_EACH_DEFINED_SYMBOL (symbol)
249 : 5430667 : if (!symbol->real_symbol_p ())
250 : : ;
251 : 4302894 : else if ((group = symbol->get_comdat_group ()) != NULL)
252 : : {
253 : 509976 : map.put (symbol, group);
254 : 509976 : comdat_head_map.put (group, symbol);
255 : 509976 : comdat_group_seen = true;
256 : :
257 : : /* Mark the symbol so we won't waste time visiting it for dataflow. */
258 : 509976 : symbol->aux = (symtab_node *) (void *) 1;
259 : : }
260 : : /* See symbols that cannot be privatized to comdats; that is externally
261 : : visible symbols or otherwise used ones. We also do not want to mangle
262 : : user section names. */
263 : 3792918 : else if (symbol->externally_visible
264 : : || symbol->force_output
265 : 3792918 : || symbol->used_from_other_partition
266 : 1467203 : || TREE_THIS_VOLATILE (symbol->decl)
267 : 1460972 : || symbol->get_section ()
268 : 4218069 : || (TREE_CODE (symbol->decl) == FUNCTION_DECL
269 : 172676 : && (DECL_STATIC_CONSTRUCTOR (symbol->decl)
270 : 171062 : || DECL_STATIC_DESTRUCTOR (symbol->decl))))
271 : : {
272 : 3369434 : symtab_node *target = symbol->ultimate_alias_target ();
273 : :
274 : : /* Always keep thunks in same sections as target function. */
275 : 3369434 : if (is_a <cgraph_node *>(target))
276 : 1096070 : target = dyn_cast <cgraph_node *> (target)->function_symbol ();
277 : 3369434 : map.put (target, error_mark_node);
278 : :
279 : : /* Mark the symbol so we won't waste time visiting it for dataflow. */
280 : 3369434 : symbol->aux = (symtab_node *) (void *) 1;
281 : : }
282 : : else
283 : : {
284 : : /* Enqueue symbol for dataflow. */
285 : 423484 : symbol->aux = first;
286 : 423484 : first = symbol;
287 : : }
288 : :
289 : 223972 : if (!comdat_group_seen)
290 : : {
291 : 2814438 : FOR_EACH_DEFINED_SYMBOL (symbol)
292 : 2415176 : symbol->aux = NULL;
293 : : return 0;
294 : : }
295 : :
296 : : /* The actual dataflow. */
297 : :
298 : 94865 : while (first != (void *) 1)
299 : : {
300 : 70524 : tree group = NULL;
301 : 70524 : tree newgroup, *val;
302 : :
303 : 70524 : symbol = first;
304 : 70524 : first = (symtab_node *)first->aux;
305 : :
306 : : /* Get current lattice value of SYMBOL. */
307 : 70524 : val = map.get (symbol);
308 : 70524 : if (val)
309 : 302 : group = *val;
310 : :
311 : : /* If it is bottom, there is nothing to do; do not clear AUX
312 : : so we won't re-queue the symbol. */
313 : 70524 : if (group == error_mark_node)
314 : 3653 : continue;
315 : :
316 : 70517 : newgroup = propagate_comdat_group (symbol, group, map);
317 : :
318 : : /* If nothing changed, proceed to next symbol. */
319 : 70517 : if (newgroup == group)
320 : : {
321 : 3646 : symbol->aux = NULL;
322 : 3646 : continue;
323 : : }
324 : :
325 : : /* Update lattice value and enqueue all references for re-visiting. */
326 : 66871 : gcc_assert (newgroup);
327 : 66871 : if (val)
328 : 234 : *val = newgroup;
329 : : else
330 : 66637 : map.put (symbol, newgroup);
331 : 66871 : enqueue_references (&first, symbol);
332 : :
333 : : /* We may need to revisit the symbol unless it is BOTTOM. */
334 : 66871 : if (newgroup != error_mark_node)
335 : 5112 : symbol->aux = NULL;
336 : : }
337 : :
338 : : /* Finally assign symbols to the sections. */
339 : :
340 : 6079664 : FOR_EACH_DEFINED_SYMBOL (symbol)
341 : : {
342 : 3015491 : struct cgraph_node *fun;
343 : 3015491 : symbol->aux = NULL;
344 : 3015491 : if (!symbol->get_comdat_group ()
345 : 2425936 : && !symbol->alias
346 : 2416555 : && (!(fun = dyn_cast <cgraph_node *> (symbol))
347 : 1178507 : || !fun->thunk)
348 : 5431368 : && symbol->real_symbol_p ())
349 : : {
350 : 1573536 : tree *val = map.get (symbol);
351 : :
352 : : /* A NULL here means that SYMBOL is unreachable in the definition
353 : : of ipa-comdats. Either ipa-comdats is wrong about this or someone
354 : : forgot to cleanup and remove unreachable functions earlier. */
355 : 1573536 : gcc_assert (val);
356 : :
357 : 1573536 : tree group = *val;
358 : :
359 : 1573536 : if (group == error_mark_node)
360 : 1568719 : continue;
361 : 4817 : if (dump_file)
362 : : {
363 : 3 : fprintf (dump_file, "Localizing symbol\n");
364 : 3 : symbol->dump (dump_file);
365 : 3 : fprintf (dump_file, "To group: %s\n", IDENTIFIER_POINTER (group));
366 : : }
367 : 4817 : if (is_a <cgraph_node *> (symbol))
368 : 4811 : dyn_cast <cgraph_node *>(symbol)->call_for_symbol_thunks_and_aliases
369 : 4811 : (set_comdat_group_1,
370 : 4811 : *comdat_head_map.get (group),
371 : : true);
372 : : else
373 : 6 : symbol->call_for_symbol_and_aliases
374 : 6 : (set_comdat_group,
375 : 6 : *comdat_head_map.get (group),
376 : : true);
377 : : }
378 : : }
379 : :
380 : : #if 0
381 : : /* Recompute calls comdat local flag. This need to be done after all changes
382 : : are made. */
383 : : cgraph_node *function;
384 : : FOR_EACH_DEFINED_FUNCTION (function)
385 : : if (function->get_comdat_group ())
386 : : function->calls_comdat_local = function->check_calls_comdat_local_p ();
387 : : #endif
388 : : return 0;
389 : 223972 : }
390 : :
391 : : namespace {
392 : :
393 : : const pass_data pass_data_ipa_comdats =
394 : : {
395 : : IPA_PASS, /* type */
396 : : "comdats", /* name */
397 : : OPTGROUP_NONE, /* optinfo_flags */
398 : : TV_IPA_COMDATS, /* tv_id */
399 : : 0, /* properties_required */
400 : : 0, /* properties_provided */
401 : : 0, /* properties_destroyed */
402 : : 0, /* todo_flags_start */
403 : : 0, /* todo_flags_finish */
404 : : };
405 : :
406 : : class pass_ipa_comdats : public ipa_opt_pass_d
407 : : {
408 : : public:
409 : 280114 : pass_ipa_comdats (gcc::context *ctxt)
410 : : : ipa_opt_pass_d (pass_data_ipa_comdats, ctxt,
411 : : NULL, /* generate_summary */
412 : : NULL, /* write_summary */
413 : : NULL, /* read_summary */
414 : : NULL, /* write_optimization_summary */
415 : : NULL, /* read_optimization_summary */
416 : : NULL, /* stmt_fixup */
417 : : 0, /* function_transform_todo_flags_start */
418 : : NULL, /* function_transform */
419 : 280114 : NULL) /* variable_transform */
420 : 280114 : {}
421 : :
422 : : /* opt_pass methods: */
423 : : bool gate (function *) final override;
424 : 223972 : unsigned int execute (function *) final override { return ipa_comdats (); }
425 : :
426 : : }; // class pass_ipa_comdats
427 : :
428 : : bool
429 : 553805 : pass_ipa_comdats::gate (function *)
430 : : {
431 : 553805 : return HAVE_COMDAT_GROUP;
432 : : }
433 : :
434 : : } // anon namespace
435 : :
436 : : ipa_opt_pass_d *
437 : 280114 : make_pass_ipa_comdats (gcc::context *ctxt)
438 : : {
439 : 280114 : return new pass_ipa_comdats (ctxt);
440 : : }
|