]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/ipa-modref.c
Cleanup modref interfaces.
[thirdparty/gcc.git] / gcc / ipa-modref.c
1 /* Search for references that a functions loads or stores.
2 Copyright (C) 2020 Free Software Foundation, Inc.
3 Contributed by David Cepelik and Jan Hubicka
4
5 This file is part of GCC.
6
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
10 version.
11
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
20
21 /* Mod/ref pass records summary about loads and stores performed by the
22 function. This is later used by alias analysis to disambiguate memory
23 accesses across function calls. The summary has a form of decision tree and
24 contains:
25
26 - base alias set
27 and for each:
28 - ref alias set
29
30 In future more information will be tracked.
31
32 This file contains a tree pass and an IPA pass. Both performs the same
33 analys however tree pass is executed during early and late optimization
34 passes to propagate info downwards in the compilation order. IPA pass
35 propagates across the callgraph and is able to handle recursion and works on
36 whole program during link-time analysis.
37
38 LTO mode differs from the local mode by not recording alias sets but types
39 that are translated to alias sets later. This is necessary in order stream
40 the information because the alias sets are rebuild at stream-in time and may
41 not correspond to ones seen during analysis. For this reason part of analysis
42 is duplicated. */
43
44 #include "config.h"
45 #include "system.h"
46 #include "coretypes.h"
47 #include "backend.h"
48 #include "tree.h"
49 #include "gimple.h"
50 #include "alloc-pool.h"
51 #include "tree-pass.h"
52 #include "gimple-iterator.h"
53 #include "tree-dfa.h"
54 #include "cgraph.h"
55 #include "ipa-utils.h"
56 #include "symbol-summary.h"
57 #include "gimple-pretty-print.h"
58 #include "gimple-walk.h"
59 #include "print-tree.h"
60 #include "tree-streamer.h"
61 #include "alias.h"
62 #include "calls.h"
63 #include "ipa-modref-tree.h"
64 #include "ipa-modref.h"
65 #include "value-range.h"
66 #include "ipa-prop.h"
67 #include "ipa-fnsummary.h"
68
69 /* Class (from which there is one global instance) that holds modref summaries
70 for all analyzed functions. */
71 class GTY((user)) modref_summaries
72 : public fast_function_summary <modref_summary *, va_gc>
73 {
74 public:
75 modref_summaries (symbol_table *symtab)
76 : fast_function_summary <modref_summary *, va_gc> (symtab) {}
77 virtual void insert (cgraph_node *, modref_summary *state);
78 virtual void duplicate (cgraph_node *src_node,
79 cgraph_node *dst_node,
80 modref_summary *src_data,
81 modref_summary *dst_data);
82 /* This flag controls whether newly inserted functions should be analyzed
83 in IPA or normal mode. Functions inserted between IPA analysis and
84 ipa-modref pass execution needs to be analyzed in IPA mode while all
85 other insertions leads to normal analysis. */
86 bool ipa;
87 };
88
89 /* Global variable holding all modref summaries. */
90 static GTY(()) fast_function_summary <modref_summary *, va_gc> *summaries;
91
92 /* Summary for a single function which this pass produces. */
93
94 modref_summary::modref_summary ()
95 : loads (NULL), stores (NULL), loads_lto (NULL),
96 stores_lto (NULL), finished (0)
97 {
98 }
99
100 modref_summary::~modref_summary ()
101 {
102 if (loads)
103 ggc_delete (loads);
104 if (stores)
105 ggc_delete (stores);
106 if (loads_lto)
107 ggc_delete (loads_lto);
108 if (stores_lto)
109 ggc_delete (stores_lto);
110 }
111
112 /* Return true if lto summary is potentially useful for optimization. */
113
114 bool
115 modref_summary::lto_useful_p (int ecf_flags)
116 {
117 if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
118 return false;
119 if (loads_lto && !loads_lto->every_base)
120 return true;
121 if (ecf_flags & ECF_PURE)
122 return false;
123 return stores_lto && !stores_lto->every_base;
124 }
125
126 /* Return true if summary is potentially useful for optimization. */
127
128 bool
129 modref_summary::useful_p (int ecf_flags)
130 {
131 if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
132 return false;
133 if (lto_useful_p (ecf_flags))
134 return true;
135 if (loads && !loads->every_base)
136 return true;
137 if (ecf_flags & ECF_PURE)
138 return false;
139 return stores && !loads->every_base;
140 }
141
142 /* Dump records TT to OUT. */
143
144 static void
145 dump_records (modref_records *tt, FILE *out)
146 {
147 fprintf (out, " Limits: %i bases, %i refs\n",
148 (int)tt->max_bases, (int)tt->max_refs);
149 if (tt->every_base)
150 {
151 fprintf (out, " Every base\n");
152 return;
153 }
154 size_t i;
155 modref_base_node <alias_set_type> *n;
156 FOR_EACH_VEC_SAFE_ELT (tt->bases, i, n)
157 {
158 fprintf (out, " Base %i: alias set %i\n", (int)i, n->base);
159 if (n->every_ref)
160 {
161 fprintf (out, " Every ref\n");
162 continue;
163 }
164 size_t j;
165 modref_ref_node <alias_set_type> *r;
166 FOR_EACH_VEC_SAFE_ELT (n->refs, j, r)
167 {
168 fprintf (out, " Ref %i: alias set %i\n", (int)j, r->ref);
169 }
170 }
171 }
172
173 /* Dump records TT to OUT. */
174
175 static void
176 dump_lto_records (modref_records_lto *tt, FILE *out)
177 {
178 fprintf (out, " Limits: %i bases, %i refs\n",
179 (int)tt->max_bases, (int)tt->max_refs);
180 if (tt->every_base)
181 {
182 fprintf (out, " Every base\n");
183 return;
184 }
185 size_t i;
186 modref_base_node <tree> *n;
187 FOR_EACH_VEC_SAFE_ELT (tt->bases, i, n)
188 {
189 fprintf (out, " Base %i:", (int)i);
190 print_generic_expr (dump_file, n->base);
191 fprintf (out, " (alias set %i)\n",
192 n->base ? get_alias_set (n->base) : 0);
193 if (n->every_ref)
194 {
195 fprintf (out, " Every ref\n");
196 continue;
197 }
198 size_t j;
199 modref_ref_node <tree> *r;
200 FOR_EACH_VEC_SAFE_ELT (n->refs, j, r)
201 {
202 fprintf (out, " Ref %i:", (int)j);
203 print_generic_expr (dump_file, r->ref);
204 fprintf (out, " (alias set %i)\n",
205 r->ref ? get_alias_set (r->ref) : 0);
206 }
207 }
208 }
209
210 /* Dump summary. */
211
212 void
213 modref_summary::dump (FILE *out)
214 {
215 if (loads)
216 {
217 fprintf (out, " loads:\n");
218 dump_records (loads, out);
219 }
220 if (stores)
221 {
222 fprintf (out, " stores:\n");
223 dump_records (stores, out);
224 }
225 if (loads_lto)
226 {
227 fprintf (out, " LTO loads:\n");
228 dump_lto_records (loads_lto, out);
229 }
230 if (stores_lto)
231 {
232 fprintf (out, " LTO stores:\n");
233 dump_lto_records (stores_lto, out);
234 }
235 }
236
237
238 /* Get function summary for FUNC if it exists, return NULL otherwise. */
239
240 modref_summary *
241 get_modref_function_summary (cgraph_node *func)
242 {
243 /* Avoid creation of the summary too early (e.g. when front-end calls us). */
244 if (!summaries)
245 return NULL;
246
247 /* A single function body may be represented by multiple symbols with
248 different visibility. For example, if FUNC is an interposable alias,
249 we don't want to return anything, even if we have summary for the target
250 function. */
251 enum availability avail;
252 func = func->function_or_virtual_thunk_symbol
253 (&avail, cgraph_node::get (current_function_decl));
254 if (avail <= AVAIL_INTERPOSABLE)
255 return NULL;
256
257 /* Attempt to get summary for FUNC. If analysis of FUNC hasn't finished yet,
258 don't return anything. */
259 modref_summary *r = summaries->get (func);
260 if (r && r->finished)
261 return r;
262
263 return NULL;
264 }
265
266 /* Record access into the modref_records data structure. */
267
268 static void
269 record_access (modref_records *tt, ao_ref *ref)
270 {
271 alias_set_type base_set = !flag_strict_aliasing ? 0
272 : ao_ref_base_alias_set (ref);
273 alias_set_type ref_set = !flag_strict_aliasing ? 0
274 : (ao_ref_alias_set (ref));
275 if (dump_file)
276 {
277 fprintf (dump_file, " - Recording base_set=%i ref_set=%i\n",
278 base_set, ref_set);
279 }
280 tt->insert (base_set, ref_set);
281 }
282
283 /* IPA version of record_access_tree. */
284
285 static void
286 record_access_lto (modref_records_lto *tt, ao_ref *ref)
287 {
288 /* get_alias_set sometimes use different type to compute the alias set
289 than TREE_TYPE (base). Do same adjustments. */
290 tree base_type = NULL_TREE, ref_type = NULL_TREE;
291 if (flag_strict_aliasing)
292 {
293 tree base;
294
295 base = ref->ref;
296 while (handled_component_p (base))
297 base = TREE_OPERAND (base, 0);
298
299 base_type = reference_alias_ptr_type_1 (&base);
300
301 if (!base_type)
302 base_type = TREE_TYPE (base);
303 else
304 base_type = TYPE_REF_CAN_ALIAS_ALL (base_type)
305 ? NULL_TREE : TREE_TYPE (base_type);
306
307 tree ref_expr = ref->ref;
308 ref_type = reference_alias_ptr_type_1 (&ref_expr);
309
310 if (!ref_type)
311 ref_type = TREE_TYPE (ref_expr);
312 else
313 ref_type = TYPE_REF_CAN_ALIAS_ALL (ref_type)
314 ? NULL_TREE : TREE_TYPE (ref_type);
315
316 /* Sanity check that we are in sync with what get_alias_set does. */
317 gcc_checking_assert ((!base_type && !ao_ref_base_alias_set (ref))
318 || get_alias_set (base_type)
319 == ao_ref_base_alias_set (ref));
320 gcc_checking_assert ((!ref_type && !ao_ref_alias_set (ref))
321 || get_alias_set (ref_type)
322 == ao_ref_alias_set (ref));
323
324 /* Do not bother to record types that have no meaningful alias set.
325 Also skip variably modified types since these go to local streams. */
326 if (base_type && (!get_alias_set (base_type)
327 || variably_modified_type_p (base_type, NULL_TREE)))
328 base_type = NULL_TREE;
329 if (ref_type && (!get_alias_set (ref_type)
330 || variably_modified_type_p (ref_type, NULL_TREE)))
331 ref_type = NULL_TREE;
332 }
333 if (dump_file)
334 {
335 fprintf (dump_file, " - Recording base type:");
336 print_generic_expr (dump_file, base_type);
337 fprintf (dump_file, " (alias set %i) ref type:",
338 base_type ? get_alias_set (base_type) : 0);
339 print_generic_expr (dump_file, ref_type);
340 fprintf (dump_file, " (alias set %i)\n",
341 ref_type ? get_alias_set (ref_type) : 0);
342 }
343
344 tt->insert (base_type, ref_type);
345 }
346
347 /* Returns true if and only if we should store the access to EXPR.
348 Some accesses, e.g. loads from automatic variables, are not interesting. */
349
350 static bool
351 record_access_p (tree expr)
352 {
353 if (refs_local_or_readonly_memory_p (expr))
354 {
355 if (dump_file)
356 fprintf (dump_file, " - Read-only or local, ignoring.\n");
357 return false;
358 }
359 return true;
360 }
361
362 /* Return true if ECF flags says that stores can be ignored. */
363
364 static bool
365 ignore_stores_p (tree caller, int flags)
366 {
367 if (flags & ECF_PURE)
368 return true;
369 if ((flags & (ECF_NORETURN | ECF_NOTHROW)) == (ECF_NORETURN | ECF_NOTHROW)
370 || (!opt_for_fn (caller, flag_exceptions) && (flags & ECF_NORETURN)))
371 return true;
372 return false;
373 }
374
375 /* Analyze function call STMT in function F. */
376
377 static bool
378 analyze_call (modref_summary *cur_summary,
379 gimple *stmt)
380 {
381 /* Check flags on the function call. In certain cases, analysis can be
382 simplified. */
383 int flags = gimple_call_flags (stmt);
384 if (flags & (ECF_CONST | ECF_NOVOPS))
385 {
386 if (dump_file)
387 fprintf (dump_file,
388 " - ECF_CONST | ECF_NOVOPS, ignoring all stores and all loads "
389 "except for args.\n");
390 return true;
391 }
392
393 /* Pure functions do not affect global memory. Stores by functions which are
394 noreturn and do not throw can safely be ignored. */
395 bool ignore_stores = ignore_stores_p (current_function_decl, flags);
396
397 /* Next, we try to get the callee's function declaration. The goal is to
398 merge their summary with ours. */
399 tree callee = gimple_call_fndecl (stmt);
400
401 /* Check if this is an indirect call. */
402 if (!callee)
403 {
404 /* If the indirect call does not write memory, our store summary is
405 unaffected, but we have to discard our loads summary (we don't know
406 anything about the loads that the called function performs). */
407 if (ignore_stores)
408 {
409 if (dump_file)
410 fprintf (dump_file, " - Indirect call which does not write memory, "
411 "discarding loads.\n");
412 if (cur_summary->loads)
413 cur_summary->loads->collapse ();
414 if (cur_summary->loads_lto)
415 cur_summary->loads_lto->collapse ();
416 return true;
417 }
418 if (dump_file)
419 fprintf (dump_file, " - Indirect call.\n");
420 return false;
421 }
422
423 struct cgraph_node *callee_node = cgraph_node::get_create (callee);
424
425 /* We can not safely optimize based on summary of callee if it does
426 not always bind to current def: it is possible that memory load
427 was optimized out earlier which may not happen in the interposed
428 variant. */
429 if (!callee_node->binds_to_current_def_p ())
430 {
431 if (dump_file)
432 fprintf (dump_file, " - May be interposed: collapsing loads.\n");
433 if (cur_summary->loads)
434 cur_summary->loads->collapse ();
435 if (cur_summary->loads_lto)
436 cur_summary->loads_lto->collapse ();
437 }
438
439 /* If this is a recursive call, the target summary is the same as ours, so
440 there's nothing to do. */
441 if (recursive_call_p (current_function_decl, callee))
442 {
443 if (dump_file)
444 fprintf (dump_file, " - Skipping recursive call.\n");
445 return true;
446 }
447
448 gcc_assert (callee_node != NULL);
449
450 /* Get the function symbol and its availability. */
451 enum availability avail;
452 callee_node = callee_node->function_symbol (&avail);
453 if (avail <= AVAIL_INTERPOSABLE)
454 {
455 /* Keep stores summary, but discard all loads for interposable function
456 symbols. */
457 if (ignore_stores)
458 {
459 if (cur_summary->loads)
460 cur_summary->loads->collapse ();
461 if (cur_summary->loads_lto)
462 cur_summary->loads_lto->collapse ();
463 return true;
464 }
465 if (dump_file)
466 fprintf (dump_file, " - Function availability <= AVAIL_INTERPOSABLE.\n");
467 return false;
468 }
469
470 /* Get callee's modref summary. As above, if there's no summary, we either
471 have to give up or, if stores are ignored, we can just purge loads. */
472 modref_summary *callee_summary = summaries->get (callee_node);
473 if (!callee_summary)
474 {
475 if (ignore_stores)
476 {
477 if (cur_summary->loads)
478 cur_summary->loads->collapse ();
479 if (cur_summary->loads_lto)
480 cur_summary->loads_lto->collapse ();
481 return true;
482 }
483 if (dump_file)
484 fprintf (dump_file, " - No modref summary available for callee.\n");
485 return false;
486 }
487
488 /* Merge with callee's summary. */
489 if (cur_summary->loads)
490 cur_summary->loads->merge (callee_summary->loads);
491 if (cur_summary->loads_lto)
492 cur_summary->loads_lto->merge (callee_summary->loads_lto);
493 if (!ignore_stores)
494 {
495 if (cur_summary->stores)
496 cur_summary->stores->merge (callee_summary->stores);
497 if (cur_summary->stores_lto)
498 cur_summary->stores_lto->merge (callee_summary->stores_lto);
499 }
500
501 return true;
502 }
503
504 /* Helper for analyze_stmt. */
505
506 static bool
507 analyze_load (gimple *, tree, tree op, void *data)
508 {
509 modref_summary *summary = (modref_summary *)data;
510
511 if (dump_file)
512 {
513 fprintf (dump_file, " - Analyzing load: ");
514 print_generic_expr (dump_file, op);
515 fprintf (dump_file, "\n");
516 }
517
518 if (!record_access_p (op))
519 return false;
520
521 ao_ref r;
522 ao_ref_init (&r, op);
523
524 if (summary->loads)
525 record_access (summary->loads, &r);
526 if (summary->loads_lto)
527 record_access_lto (summary->loads_lto, &r);
528 return false;
529 }
530
531 /* Helper for analyze_stmt. */
532
533 static bool
534 analyze_store (gimple *, tree, tree op, void *data)
535 {
536 modref_summary *summary = (modref_summary *)data;
537
538 if (dump_file)
539 {
540 fprintf (dump_file, " - Analyzing store: ");
541 print_generic_expr (dump_file, op);
542 fprintf (dump_file, "\n");
543 }
544
545 if (!record_access_p (op))
546 return false;
547
548 ao_ref r;
549 ao_ref_init (&r, op);
550
551 if (summary->stores)
552 record_access (((modref_summary *)data)->stores, &r);
553 if (summary->stores_lto)
554 record_access_lto (((modref_summary *)data)->stores_lto, &r);
555 return false;
556 }
557
558 /* Analyze statement STMT of function F.
559 If IPA is true do not merge in side effects of calls. */
560
561 static bool
562 analyze_stmt (modref_summary *summary, gimple *stmt, bool ipa)
563 {
564 /* There is no need to record clobbers. */
565 if (gimple_clobber_p (stmt))
566 return false;
567 /* Analyze all loads and stores in STMT. */
568 walk_stmt_load_store_ops (stmt, summary,
569 analyze_load, analyze_store);
570 /* or call analyze_load_ipa, analyze_store_ipa */
571
572 switch (gimple_code (stmt))
573 {
574 case GIMPLE_ASM:
575 /* If the ASM statement does not read nor write memory, there's nothing
576 to do. Otherwise just give up. */
577 if (!gimple_asm_clobbers_memory_p (as_a <gasm *> (stmt)))
578 return true;
579 if (dump_file)
580 fprintf (dump_file, " - Function contains GIMPLE_ASM statement "
581 "which clobbers memory.\n");
582 return false;
583 case GIMPLE_CALL:
584 if (!ipa)
585 return analyze_call (summary, stmt);
586 return true;
587 default:
588 /* Nothing to do for other types of statements. */
589 return true;
590 }
591 }
592
593 /* Analyze function F. IPA indicates whether we're running in tree mode (false)
594 or the IPA mode (true). */
595
596 static void
597 analyze_function (function *f, bool ipa)
598 {
599 if (dump_file)
600 fprintf (dump_file, "modref analyzing '%s' (ipa=%i)%s%s\n",
601 function_name (f), ipa,
602 TREE_READONLY (current_function_decl) ? " (const)" : "",
603 DECL_PURE_P (current_function_decl) ? " (pure)" : "");
604
605 /* Don't analyze this function if it's compiled with -fno-strict-aliasing. */
606 if (!flag_ipa_modref)
607 return;
608
609 /* Initialize the summary. */
610 if (!summaries)
611 summaries = new (ggc_alloc <modref_summaries> ())
612 modref_summaries (symtab);
613 else /* Remove existing summary if we are re-running the pass. */
614 summaries->remove (cgraph_node::get (f->decl));
615
616 ((modref_summaries *)summaries)->ipa = ipa;
617
618 modref_summary *summary = summaries->get_create (cgraph_node::get (f->decl));
619
620 /* Compute no-LTO summaries when local optimization is going to happen. */
621 bool nolto = (!ipa || ((!flag_lto || flag_fat_lto_objects) && !in_lto_p)
622 || (in_lto_p && !flag_wpa
623 && flag_incremental_link != INCREMENTAL_LINK_LTO));
624
625 /* Compute LTO when LTO streaming is going to happen. */
626 bool lto = ipa && ((flag_lto && !in_lto_p)
627 || flag_wpa
628 || flag_incremental_link == INCREMENTAL_LINK_LTO);
629
630 /* Create and initialize summary for F.
631 Note that summaries may be already allocated from previous
632 run of the pass. */
633 if (nolto)
634 {
635 gcc_assert (!summary->loads);
636 summary->loads
637 = new (ggc_alloc <modref_tree<alias_set_type> > ())
638 modref_records (param_modref_max_bases,
639 param_modref_max_refs);
640 gcc_assert (!summary->stores);
641 summary->stores
642 = new (ggc_alloc <modref_tree<alias_set_type> > ())
643 modref_records (param_modref_max_bases,
644 param_modref_max_refs);
645 }
646 if (lto)
647 {
648 gcc_assert (!summary->loads_lto);
649 summary->loads_lto
650 = new (ggc_alloc <modref_tree<tree> > ())
651 modref_records_lto (param_modref_max_bases,
652 param_modref_max_refs);
653 gcc_assert (!summary->stores_lto);
654 summary->stores_lto
655 = new (ggc_alloc <modref_tree<tree> > ())
656 modref_records_lto (param_modref_max_bases,
657 param_modref_max_refs);
658 }
659 summary->finished = false;
660 int ecf_flags = flags_from_decl_or_type (current_function_decl);
661
662 /* Analyze each statement in each basic block of the function. If the
663 statement cannot be analyzed (for any reason), the entire function cannot
664 be analyzed by modref. */
665 basic_block bb;
666 FOR_EACH_BB_FN (bb, f)
667 {
668 gimple_stmt_iterator si;
669 for (si = gsi_after_labels (bb); !gsi_end_p (si); gsi_next (&si))
670 {
671 if (!analyze_stmt (summary, gsi_stmt (si), ipa)
672 || !summary->useful_p (ecf_flags))
673 {
674 cgraph_node *fnode = cgraph_node::get (current_function_decl);
675 summaries->remove (fnode);
676 if (dump_file)
677 fprintf (dump_file,
678 " - modref done with result: not tracked.\n");
679 return;
680 }
681 }
682 }
683
684 if (!ipa)
685 summary->finished = true;
686
687 if (dump_file)
688 {
689 fprintf (dump_file, " - modref done with result: tracked.\n");
690 summary->dump (dump_file);
691 }
692 }
693
694 /* Callback for generate_summary. */
695
696 static void
697 modref_generate (void)
698 {
699 struct cgraph_node *node;
700 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
701 {
702 function *f = DECL_STRUCT_FUNCTION (node->decl);
703 if (!f)
704 continue;
705 push_cfun (f);
706 analyze_function (f, true);
707 pop_cfun ();
708 }
709 }
710
711 /* Called when a new function is inserted to callgraph late. */
712
713 void
714 modref_summaries::insert (struct cgraph_node *node, modref_summary *)
715 {
716 if (!DECL_STRUCT_FUNCTION (node->decl))
717 return;
718 push_cfun (DECL_STRUCT_FUNCTION (node->decl));
719 analyze_function (DECL_STRUCT_FUNCTION (node->decl), ipa);
720 pop_cfun ();
721 }
722
723 /* Called when new clone is inserted to callgraph late. */
724
725 void
726 modref_summaries::duplicate (cgraph_node *, cgraph_node *,
727 modref_summary *src_data,
728 modref_summary *dst_data)
729 {
730 dst_data->finished = src_data->finished;
731 if (src_data->stores)
732 {
733 dst_data->stores = new (ggc_alloc <modref_tree<alias_set_type> > ())
734 modref_records
735 (src_data->stores->max_bases,
736 src_data->stores->max_refs);
737 dst_data->stores->merge (src_data->stores);
738 }
739 if (src_data->loads)
740 {
741 dst_data->loads = new (ggc_alloc <modref_tree<alias_set_type> > ())
742 modref_records
743 (src_data->loads->max_bases,
744 src_data->loads->max_refs);
745 dst_data->loads->merge (src_data->loads);
746 }
747 if (src_data->stores_lto)
748 {
749 dst_data->stores_lto = new (ggc_alloc <modref_tree<tree> > ())
750 modref_records_lto
751 (src_data->stores_lto->max_bases,
752 src_data->stores_lto->max_refs);
753 dst_data->stores_lto->merge (src_data->stores_lto);
754 }
755 if (src_data->loads_lto)
756 {
757 dst_data->loads_lto = new (ggc_alloc <modref_tree<tree> > ())
758 modref_records_lto
759 (src_data->stores_lto->max_bases,
760 src_data->stores_lto->max_refs);
761 dst_data->loads_lto->merge (src_data->loads_lto);
762 }
763 }
764
765 namespace
766 {
767 /* Definition of the modref pass on GIMPLE. */
768 const pass_data pass_data_modref = {
769 GIMPLE_PASS,
770 "modref",
771 OPTGROUP_IPA,
772 TV_TREE_MODREF,
773 (PROP_cfg | PROP_ssa),
774 0,
775 0,
776 0,
777 0,
778 };
779
780 class pass_modref : public gimple_opt_pass
781 {
782 public:
783 pass_modref (gcc::context *ctxt)
784 : gimple_opt_pass (pass_data_modref, ctxt) {}
785
786 /* opt_pass methods: */
787 opt_pass *clone ()
788 {
789 return new pass_modref (m_ctxt);
790 }
791 virtual bool gate (function *)
792 {
793 return flag_ipa_modref;
794 }
795 virtual unsigned int execute (function *);
796 };
797
798 /* Encode TT to the output block OB using the summary streaming API. */
799
800 static void
801 write_modref_records (modref_records_lto *tt, struct output_block *ob)
802 {
803 streamer_write_uhwi (ob, tt->max_bases);
804 streamer_write_uhwi (ob, tt->max_refs);
805
806 streamer_write_uhwi (ob, tt->every_base);
807 streamer_write_uhwi (ob, vec_safe_length (tt->bases));
808 size_t i;
809 modref_base_node <tree> *base_node;
810 FOR_EACH_VEC_SAFE_ELT (tt->bases, i, base_node)
811 {
812 stream_write_tree (ob, base_node->base, true);
813
814 streamer_write_uhwi (ob, base_node->every_ref);
815 streamer_write_uhwi (ob, vec_safe_length (base_node->refs));
816 size_t j;
817 modref_ref_node <tree> *ref_node;
818 FOR_EACH_VEC_SAFE_ELT (base_node->refs, j, ref_node)
819 {
820 stream_write_tree (ob, ref_node->ref, true);
821 }
822 }
823 }
824
825 /* Read a modref_tree from the input block IB using the data from DATA_IN.
826 This assumes that the tree was encoded using write_modref_tree.
827 Either nolto_ret or lto_ret is initialized by the tree depending whether
828 LTO streaming is expected or not. */
829
830 void
831 read_modref_records (lto_input_block *ib, struct data_in *data_in,
832 modref_records **nolto_ret,
833 modref_records_lto **lto_ret)
834 {
835 size_t max_bases = streamer_read_uhwi (ib);
836 size_t max_refs = streamer_read_uhwi (ib);
837
838 /* Decide whether we want to turn LTO data types to non-LTO (i.e. when
839 LTO re-streaming is not going to happen). */
840 if (flag_wpa || flag_incremental_link == INCREMENTAL_LINK_LTO)
841 *lto_ret = new (ggc_alloc <modref_records_lto> ()) modref_records_lto
842 (max_bases, max_refs);
843 else
844 *nolto_ret = new (ggc_alloc <modref_records> ()) modref_records
845 (max_bases, max_refs);
846
847 size_t every_base = streamer_read_uhwi (ib);
848 size_t nbase = streamer_read_uhwi (ib);
849
850 gcc_assert (!every_base || nbase == 0);
851 if (every_base)
852 {
853 if (*nolto_ret)
854 (*nolto_ret)->collapse ();
855 if (*lto_ret)
856 (*lto_ret)->collapse ();
857 }
858 for (size_t i = 0; i < nbase; i++)
859 {
860 tree base_tree = stream_read_tree (ib, data_in);
861 modref_base_node <alias_set_type> *nolto_base_node = NULL;
862 modref_base_node <tree> *lto_base_node = NULL;
863
864 /* At stream in time we have LTO alias info. Check if we streamed in
865 something obviously unnecessary. Do not glob types by alias sets;
866 it is not 100% clear that ltrans types will get merged same way.
867 Types may get refined based on ODR type conflicts. */
868 if (base_tree && !get_alias_set (base_tree))
869 {
870 if (dump_file)
871 {
872 fprintf (dump_file, "Streamed in alias set 0 type ");
873 print_generic_expr (dump_file, base_tree);
874 fprintf (dump_file, "\n");
875 }
876 base_tree = NULL;
877 }
878
879 if (*nolto_ret)
880 nolto_base_node = (*nolto_ret)->insert_base (base_tree
881 ? get_alias_set (base_tree)
882 : 0);
883 if (*lto_ret)
884 lto_base_node = (*lto_ret)->insert_base (base_tree);
885 size_t every_ref = streamer_read_uhwi (ib);
886 size_t nref = streamer_read_uhwi (ib);
887
888 gcc_assert (!every_ref || nref == 0);
889 if (every_ref)
890 {
891 if (nolto_base_node)
892 nolto_base_node->collapse ();
893 if (lto_base_node)
894 lto_base_node->collapse ();
895 }
896 for (size_t j = 0; j < nref; j++)
897 {
898 tree ref_tree = stream_read_tree (ib, data_in);
899
900 if (ref_tree && !get_alias_set (ref_tree))
901 {
902 if (dump_file)
903 {
904 fprintf (dump_file, "Streamed in alias set 0 type ");
905 print_generic_expr (dump_file, ref_tree);
906 fprintf (dump_file, "\n");
907 }
908 base_tree = NULL;
909 }
910
911 if (nolto_base_node)
912 nolto_base_node->insert_ref (ref_tree ? get_alias_set (ref_tree)
913 : 0, max_refs);
914 if (lto_base_node)
915 lto_base_node->insert_ref (ref_tree, max_refs);
916 }
917 }
918 }
919
920 /* Callback for write_summary. */
921
922 static void
923 modref_write ()
924 {
925 struct output_block *ob = create_output_block (LTO_section_ipa_modref);
926 lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
927 unsigned int count = 0;
928 int i;
929
930 if (!summaries)
931 {
932 streamer_write_uhwi (ob, 0);
933 streamer_write_char_stream (ob->main_stream, 0);
934 produce_asm (ob, NULL);
935 destroy_output_block (ob);
936 return;
937 }
938
939 for (i = 0; i < lto_symtab_encoder_size (encoder); i++)
940 {
941 symtab_node *snode = lto_symtab_encoder_deref (encoder, i);
942 cgraph_node *cnode = dyn_cast <cgraph_node *> (snode);
943 modref_summary *r;
944
945 if (cnode && cnode->definition && !cnode->alias
946 && (r = summaries->get (cnode))
947 && r->lto_useful_p (flags_from_decl_or_type (cnode->decl)))
948 count++;
949 }
950 streamer_write_uhwi (ob, count);
951
952 for (i = 0; i < lto_symtab_encoder_size (encoder); i++)
953 {
954 symtab_node *snode = lto_symtab_encoder_deref (encoder, i);
955 cgraph_node *cnode = dyn_cast <cgraph_node *> (snode);
956
957 if (cnode && cnode->definition && !cnode->alias)
958 {
959
960 modref_summary *r = summaries->get (cnode);
961
962 if (!r || !r->lto_useful_p (flags_from_decl_or_type (cnode->decl)))
963 continue;
964
965 streamer_write_uhwi (ob, lto_symtab_encoder_encode (encoder, cnode));
966
967 streamer_write_uhwi (ob, r->loads_lto ? 1 : 0);
968 streamer_write_uhwi (ob, r->stores_lto ? 1 : 0);
969 if (r->loads_lto)
970 write_modref_records (r->loads_lto, ob);
971 if (r->stores_lto)
972 write_modref_records (r->stores_lto, ob);
973 }
974 }
975 streamer_write_char_stream (ob->main_stream, 0);
976 produce_asm (ob, NULL);
977 destroy_output_block (ob);
978 }
979
980 static void
981 read_section (struct lto_file_decl_data *file_data, const char *data,
982 size_t len)
983 {
984 const struct lto_function_header *header
985 = (const struct lto_function_header *) data;
986 const int cfg_offset = sizeof (struct lto_function_header);
987 const int main_offset = cfg_offset + header->cfg_size;
988 const int string_offset = main_offset + header->main_size;
989 struct data_in *data_in;
990 unsigned int i;
991 unsigned int f_count;
992
993 lto_input_block ib ((const char *) data + main_offset, header->main_size,
994 file_data->mode_table);
995
996 data_in
997 = lto_data_in_create (file_data, (const char *) data + string_offset,
998 header->string_size, vNULL);
999 f_count = streamer_read_uhwi (&ib);
1000 for (i = 0; i < f_count; i++)
1001 {
1002 struct cgraph_node *node;
1003 lto_symtab_encoder_t encoder;
1004
1005 unsigned int index = streamer_read_uhwi (&ib);
1006 encoder = file_data->symtab_node_encoder;
1007 node = dyn_cast <cgraph_node *> (lto_symtab_encoder_deref (encoder,
1008 index));
1009
1010 modref_summary *modref_sum = summaries->get_create (node);
1011 modref_sum->finished = false;
1012 int have_loads = streamer_read_uhwi (&ib);
1013 int have_stores = streamer_read_uhwi (&ib);
1014 gcc_assert (!modref_sum->loads_lto
1015 && !modref_sum->stores_lto
1016 && !modref_sum->loads
1017 && !modref_sum->stores);
1018 if (have_loads)
1019 read_modref_records (&ib, data_in,
1020 &modref_sum->loads,
1021 &modref_sum->loads_lto);
1022 if (have_stores)
1023 read_modref_records (&ib, data_in,
1024 &modref_sum->stores,
1025 &modref_sum->stores_lto);
1026 if (dump_file)
1027 {
1028 fprintf (dump_file, "Read modref for %s\n",
1029 node->dump_name ());
1030 modref_sum->dump (dump_file);
1031 }
1032 if (flag_ltrans)
1033 modref_sum->finished = true;
1034 }
1035
1036 lto_free_section_data (file_data, LTO_section_ipa_modref, NULL, data,
1037 len);
1038 lto_data_in_delete (data_in);
1039 }
1040
1041 /* Callback for read_summary. */
1042
1043 static void
1044 modref_read (void)
1045 {
1046 struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
1047 struct lto_file_decl_data *file_data;
1048 unsigned int j = 0;
1049
1050 if (!summaries)
1051 summaries = new (ggc_alloc <modref_summaries> ())
1052 modref_summaries (symtab);
1053 ((modref_summaries *)summaries)->ipa = true;
1054
1055 while ((file_data = file_data_vec[j++]))
1056 {
1057 size_t len;
1058 const char *data = lto_get_summary_section_data (file_data,
1059 LTO_section_ipa_modref,
1060 &len);
1061 if (data)
1062 read_section (file_data, data, len);
1063 else
1064 /* Fatal error here. We do not want to support compiling ltrans units
1065 with different version of compiler or different flags than the WPA
1066 unit, so this should never happen. */
1067 fatal_error (input_location,
1068 "IPA modref summary is missing in input file");
1069 }
1070 }
1071
1072 /* Definition of the modref IPA pass. */
1073 const pass_data pass_data_ipa_modref =
1074 {
1075 IPA_PASS, /* type */
1076 "modref", /* name */
1077 OPTGROUP_IPA, /* optinfo_flags */
1078 TV_IPA_MODREF, /* tv_id */
1079 0, /* properties_required */
1080 0, /* properties_provided */
1081 0, /* properties_destroyed */
1082 0, /* todo_flags_start */
1083 ( TODO_dump_symtab ), /* todo_flags_finish */
1084 };
1085
1086 class pass_ipa_modref : public ipa_opt_pass_d
1087 {
1088 public:
1089 pass_ipa_modref (gcc::context *ctxt)
1090 : ipa_opt_pass_d (pass_data_ipa_modref, ctxt,
1091 modref_generate, /* generate_summary */
1092 modref_write, /* write_summary */
1093 modref_read, /* read_summary */
1094 modref_write, /* write_optimization_summary */
1095 modref_read, /* read_optimization_summary */
1096 NULL, /* stmt_fixup */
1097 0, /* function_transform_todo_flags_start */
1098 NULL, /* function_transform */
1099 NULL) /* variable_transform */
1100 {}
1101
1102 /* opt_pass methods: */
1103 opt_pass *clone () { return new pass_ipa_modref (m_ctxt); }
1104 virtual bool gate (function *)
1105 {
1106 return true;
1107 }
1108 virtual unsigned int execute (function *);
1109
1110 };
1111
1112 }
1113
1114 unsigned int pass_modref::execute (function *f)
1115 {
1116 /* If new function is being added during IPA, we can skip analysis. */
1117 if (summaries && ((modref_summaries *)summaries)->ipa)
1118 return 0;
1119 analyze_function (f, false);
1120 return 0;
1121 }
1122
1123 gimple_opt_pass *
1124 make_pass_modref (gcc::context *ctxt)
1125 {
1126 return new pass_modref (ctxt);
1127 }
1128
1129 ipa_opt_pass_d *
1130 make_pass_ipa_modref (gcc::context *ctxt)
1131 {
1132 return new pass_ipa_modref (ctxt);
1133 }
1134
1135 /* Skip edges from and to nodes without ipa_pure_const enabled.
1136 Ignore not available symbols. */
1137
1138 static bool
1139 ignore_edge (struct cgraph_edge *e)
1140 {
1141 enum availability avail;
1142 cgraph_node *callee = e->callee->function_or_virtual_thunk_symbol
1143 (&avail, e->caller);
1144
1145 return (avail <= AVAIL_INTERPOSABLE
1146 || !summaries->get (callee)
1147 || flags_from_decl_or_type (e->callee->decl)
1148 & (ECF_CONST | ECF_NOVOPS));
1149 }
1150
1151 /* Run the IPA pass. This will take a function's summaries and calls and
1152 construct new summaries which represent a transitive closure. So that
1153 summary of an analyzed function contains information about the loads and
1154 stores that the function or any function that it calls does. */
1155
1156 unsigned int pass_ipa_modref::execute (function *)
1157 {
1158 if (!summaries)
1159 return 0;
1160
1161 struct cgraph_node **order = XCNEWVEC (struct cgraph_node *,
1162 symtab->cgraph_count);
1163 int order_pos;
1164 order_pos = ipa_reduced_postorder (order, true, ignore_edge);
1165 int i;
1166
1167 /* Iterate over all strongly connected components in post-order. */
1168 for (i = 0; i < order_pos; i++)
1169 {
1170 bool its_hopeless = false;
1171 modref_records *loads = NULL;
1172 modref_records *stores = NULL;
1173 modref_records_lto *loads_lto = NULL;
1174 modref_records_lto *stores_lto = NULL;
1175
1176 /* Get the component's representative. That's just any node in the
1177 component from which we can traverse the entire component. */
1178 struct cgraph_node *component_node = order[i];
1179 cgraph_node *first = NULL;
1180
1181 if (dump_file)
1182 fprintf (dump_file, "Start of SCC component\n");
1183
1184 /* Walk the component. CUR is the current node of the component that's
1185 being processed. */
1186 for (struct cgraph_node *cur = component_node; cur && !its_hopeless;
1187 cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
1188 {
1189 /* Merge in summaries from CUR. */
1190 modref_summary *cur_summary = summaries->get (cur);
1191
1192 if (dump_file)
1193 fprintf (dump_file, " Processing %s\n",
1194 cur->dump_name ());
1195
1196 /* We don't know anything about CUR, hence we cannot tell anything
1197 about the entire component. */
1198 if (!cur_summary)
1199 {
1200 if (dump_file)
1201 fprintf (dump_file, " No summary\n");
1202 its_hopeless = true;
1203 break;
1204 }
1205
1206 /* Summaries are all going to be same, pick first ones and merge
1207 everything in. */
1208 if (!first)
1209 {
1210 first = cur;
1211 loads = cur_summary->loads;
1212 stores = cur_summary->stores;
1213 loads_lto = cur_summary->loads_lto;
1214 stores_lto = cur_summary->stores_lto;
1215 }
1216 for (cgraph_edge *e = cur->indirect_calls; e; e = e->next_callee)
1217 {
1218 if (e->indirect_info->ecf_flags & (ECF_CONST | ECF_NOVOPS))
1219 continue;
1220 if (ignore_stores_p (cur->decl, e->indirect_info->ecf_flags))
1221 {
1222 if (dump_file)
1223 fprintf (dump_file, " Indirect call: "
1224 "collapsing loads\n");
1225 if (loads)
1226 loads->collapse ();
1227 if (loads_lto)
1228 loads_lto->collapse ();
1229 }
1230 else
1231 {
1232 if (dump_file)
1233 fprintf (dump_file, " Indirect call: giving up\n");
1234 its_hopeless = true;
1235 }
1236 }
1237
1238 /* Walk every function that CUR calls and merge its summary. */
1239 for (cgraph_edge *callee_edge = cur->callees; callee_edge;
1240 callee_edge = callee_edge->next_callee)
1241 {
1242 int flags = flags_from_decl_or_type (callee_edge->callee->decl);
1243 modref_summary *callee_summary;
1244 struct cgraph_node *callee;
1245
1246 if (flags & (ECF_CONST | ECF_NOVOPS))
1247 continue;
1248
1249 if (dump_file)
1250 fprintf (dump_file, " Call to %s\n",
1251 callee_edge->callee->dump_name ());
1252
1253 /* We can not safely optimize based on summary of callee if it
1254 does not always bind to current def: it is possible that
1255 memory load was optimized out earlier which may not happen in
1256 the interposed variant. */
1257 if (!callee_edge->binds_to_current_def_p ())
1258 {
1259 if (loads)
1260 loads->collapse ();
1261 if (loads_lto)
1262 loads_lto->collapse ();
1263 if (dump_file)
1264 fprintf (dump_file, " May not bind local;"
1265 " collapsing loads\n");
1266 }
1267
1268 /* Get the callee and its summary. */
1269 enum availability avail;
1270 callee = callee_edge->callee->function_or_virtual_thunk_symbol
1271 (&avail, cur);
1272
1273 /* See if we can derive something from ECF flags. Be careful on
1274 not skipping calls within the SCC component: we must merge
1275 all their summaries.
1276 If we switch to iterative dataflow that may be necessary
1277 for future improvements this may go away. */
1278 if (callee->aux
1279 && ((struct ipa_dfs_info *)cur->aux)->scc_no
1280 == ((struct ipa_dfs_info *)callee->aux)->scc_no)
1281 flags = 0;
1282
1283 bool ignore_stores = ignore_stores_p (cur->decl, flags);
1284
1285 /* We don't know anything about CALLEE, hence we cannot tell
1286 anything about the entire component. */
1287
1288 if (avail <= AVAIL_INTERPOSABLE
1289 || !(callee_summary = summaries->get (callee)))
1290 {
1291 if (!ignore_stores)
1292 {
1293 its_hopeless = true;
1294 if (dump_file && avail <= AVAIL_INTERPOSABLE)
1295 fprintf (dump_file, " Call target interposable"
1296 " or not available\n");
1297 else if (dump_file)
1298 fprintf (dump_file, " No call target summary\n");
1299 break;
1300 }
1301 else
1302 {
1303 if (loads)
1304 loads->collapse ();
1305 if (loads_lto)
1306 loads_lto->collapse ();
1307 if (dump_file && avail <= AVAIL_INTERPOSABLE)
1308 fprintf (dump_file, " Call target interposable"
1309 "or not available; collapsing loads\n");
1310 else if (dump_file)
1311 fprintf (dump_file, " No call target summary;"
1312 " collapsing loads\n");
1313 continue;
1314 }
1315 }
1316
1317 /* Merge in callee's information. */
1318 if (callee_summary->loads
1319 && callee_summary->loads != loads)
1320 loads->merge (callee_summary->loads);
1321 if (callee_summary->stores
1322 && callee_summary->stores != stores)
1323 stores->merge (callee_summary->stores);
1324 if (callee_summary->loads_lto
1325 && callee_summary->loads_lto != loads_lto)
1326 loads_lto->merge (callee_summary->loads_lto);
1327 if (callee_summary->stores_lto
1328 && callee_summary->stores_lto != stores_lto)
1329 stores_lto->merge (callee_summary->stores_lto);
1330 }
1331 }
1332
1333 /* At this time, ipa_loads and ipa_stores contain information
1334 about all loads and stores done by any of the component's nodes and
1335 all functions that any of the nodes calls. We will now propagate
1336 this information to all nodes in the component. Therefore, we will
1337 walk the component one more time to do it. */
1338 for (struct cgraph_node *cur = component_node; cur;
1339 cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
1340 {
1341 modref_summary *cur_summary = summaries->get (cur);
1342 if (!cur_summary)
1343 {
1344 /* The function doesn't have a summary. We must have noticed
1345 that during the first pass and the hopeless flag must
1346 therefore be set. Skip the function. */
1347 gcc_assert (its_hopeless);
1348 }
1349 else if (its_hopeless)
1350 {
1351 if (dump_file)
1352 fprintf (dump_file, "Cleared modref info for %s\n",
1353 cur->dump_name ());
1354 summaries->remove (cur);
1355 }
1356 else
1357 {
1358 if (cur == first)
1359 ;
1360 else
1361 {
1362 if (loads)
1363 cur_summary->loads->merge (loads);
1364 if (stores)
1365 cur_summary->stores->merge (stores);
1366 if (loads_lto)
1367 cur_summary->loads_lto->merge (loads_lto);
1368 if (stores_lto)
1369 cur_summary->stores_lto->merge (stores_lto);
1370 }
1371 cur_summary->finished = true;
1372 if (dump_file)
1373 {
1374 fprintf (dump_file, "Propagated modref for %s%s%s\n",
1375 cur->dump_name (),
1376 TREE_READONLY (cur->decl) ? " (const)" : "",
1377 DECL_PURE_P (cur->decl) ? " (pure)" : "");
1378 cur_summary->dump (dump_file);
1379 }
1380 }
1381 }
1382 }
1383 ((modref_summaries *)summaries)->ipa = false;
1384 ipa_free_postorder_info ();
1385 return 0;
1386 }
1387
1388 /* Summaries must stay alive until end of compilation. */
1389
1390 void
1391 ipa_modref_c_finalize ()
1392 {
1393 if (summaries)
1394 ggc_delete (summaries);
1395 summaries = NULL;
1396 }
1397
1398 #include "gt-ipa-modref.h"