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
5 This file is part of GCC.
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
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
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/>. */
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
30 In future more information will be tracked.
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.
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
46 #include "coretypes.h"
50 #include "alloc-pool.h"
51 #include "tree-pass.h"
52 #include "gimple-iterator.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"
63 #include "ipa-modref-tree.h"
64 #include "ipa-modref.h"
66 /* Class (from which there is one global instance) that holds modref summaries
67 for all analyzed functions. */
68 class GTY((user)) modref_summaries
69 : public fast_function_summary <modref_summary *, va_gc>
72 modref_summaries (symbol_table *symtab)
73 : fast_function_summary <modref_summary *, va_gc> (symtab) {}
74 virtual void insert (cgraph_node *, modref_summary *state);
75 virtual void duplicate (cgraph_node *src_node,
76 cgraph_node *dst_node,
77 modref_summary *src_data,
78 modref_summary *dst_data);
79 /* This flag controls whether newly inserted functions should be analyzed
80 in IPA or normal mode. Functions inserted between IPA analysis and
81 ipa-modref pass execution needs to be analyzed in IPA mode while all
82 other insertions leads to normal analysis. */
86 /* Global variable holding all modref summaries. */
87 static GTY(()) fast_function_summary <modref_summary *, va_gc> *summaries;
89 /* Summary for a single function which this pass produces. */
91 modref_summary::modref_summary ()
92 : loads (NULL), stores (NULL), loads_lto (NULL),
93 stores_lto (NULL), finished (0)
97 modref_summary::~modref_summary ()
104 ggc_delete (loads_lto);
106 ggc_delete (stores_lto);
109 /* Dump records TT to OUT. */
112 dump_records (modref_records *tt, FILE *out)
114 fprintf (out, " Limits: %i bases, %i refs\n",
115 (int)tt->max_bases, (int)tt->max_refs);
118 fprintf (out, " Every base\n");
122 modref_base_node <alias_set_type> *n;
123 FOR_EACH_VEC_SAFE_ELT (tt->bases, i, n)
125 fprintf (out, " Base %i: alias set %i\n", (int)i, n->base);
128 fprintf (out, " Every ref\n");
132 modref_ref_node <alias_set_type> *r;
133 FOR_EACH_VEC_SAFE_ELT (n->refs, j, r)
135 fprintf (out, " Ref %i: alias set %i\n", (int)j, r->ref);
140 /* Dump records TT to OUT. */
143 dump_lto_records (modref_records_lto *tt, FILE *out)
145 fprintf (out, " Limits: %i bases, %i refs\n",
146 (int)tt->max_bases, (int)tt->max_refs);
149 fprintf (out, " Every base\n");
153 modref_base_node <tree> *n;
154 FOR_EACH_VEC_SAFE_ELT (tt->bases, i, n)
156 fprintf (out, " Base %i:", (int)i);
157 print_generic_expr (dump_file, n->base);
158 fprintf (out, " (alias set %i)\n",
159 n->base ? get_alias_set (n->base) : 0);
162 fprintf (out, " Every ref\n");
166 modref_ref_node <tree> *r;
167 FOR_EACH_VEC_SAFE_ELT (n->refs, j, r)
169 fprintf (out, " Ref %i:", (int)j);
170 print_generic_expr (dump_file, r->ref);
171 fprintf (out, " (alias set %i)\n",
172 r->ref ? get_alias_set (r->ref) : 0);
180 modref_summary::dump (FILE *out)
184 fprintf (out, " loads:\n");
185 dump_records (loads, out);
189 fprintf (out, " stores:\n");
190 dump_records (stores, out);
194 fprintf (out, " LTO loads:\n");
195 dump_lto_records (loads_lto, out);
199 fprintf (out, " LTO stores:\n");
200 dump_lto_records (stores_lto, out);
205 /* Get function summary for FUNC if it exists, return NULL otherwise. */
208 get_modref_function_summary (cgraph_node *func)
210 /* Avoid creation of the summary too early (e.g. when front-end calls us). */
214 /* A single function body may be represented by multiple symbols with
215 different visibility. For example, if FUNC is an interposable alias,
216 we don't want to return anything, even if we have summary for the target
218 enum availability avail;
219 func = func->function_or_virtual_thunk_symbol
220 (&avail, cgraph_node::get (current_function_decl));
221 if (avail <= AVAIL_INTERPOSABLE)
224 /* Attempt to get summary for FUNC. If analysis of FUNC hasn't finished yet,
225 don't return anything. */
226 modref_summary *r = summaries->get (func);
227 if (r && r->finished)
233 /* Record access into the modref_records data structure. */
236 record_access (modref_records *tt, ao_ref *ref)
238 alias_set_type base_set = !flag_strict_aliasing ? 0
239 : ao_ref_base_alias_set (ref);
240 alias_set_type ref_set = !flag_strict_aliasing ? 0
241 : (ao_ref_alias_set (ref));
244 fprintf (dump_file, " - Recording base_set=%i ref_set=%i\n",
247 tt->insert (base_set, ref_set);
250 /* IPA version of record_access_tree. */
253 record_access_lto (modref_records_lto *tt, ao_ref *ref)
255 /* get_alias_set sometimes use different type to compute the alias set
256 than TREE_TYPE (base). Do same adjustments. */
257 tree base_type = NULL_TREE, ref_type = NULL_TREE;
258 if (flag_strict_aliasing)
263 while (handled_component_p (base))
264 base = TREE_OPERAND (base, 0);
266 base_type = reference_alias_ptr_type_1 (&base);
269 base_type = TREE_TYPE (base);
271 base_type = TYPE_REF_CAN_ALIAS_ALL (base_type)
272 ? NULL_TREE : TREE_TYPE (base_type);
274 tree ref_expr = ref->ref;
275 ref_type = reference_alias_ptr_type_1 (&ref_expr);
278 ref_type = TREE_TYPE (ref_expr);
280 ref_type = TYPE_REF_CAN_ALIAS_ALL (ref_type)
281 ? NULL_TREE : TREE_TYPE (ref_type);
283 /* Sanity check that we are in sync with what get_alias_set does. */
284 gcc_checking_assert ((!base_type && !ao_ref_base_alias_set (ref))
285 || get_alias_set (base_type)
286 == ao_ref_base_alias_set (ref));
287 gcc_checking_assert ((!ref_type && !ao_ref_alias_set (ref))
288 || get_alias_set (ref_type)
289 == ao_ref_alias_set (ref));
291 /* Do not bother to record types that have no meaningful alias set.
292 Also skip variably modified types since these go to local streams. */
293 if (base_type && (!get_alias_set (base_type)
294 || variably_modified_type_p (base_type, NULL_TREE)))
295 base_type = NULL_TREE;
296 if (ref_type && (!get_alias_set (ref_type)
297 || variably_modified_type_p (ref_type, NULL_TREE)))
298 ref_type = NULL_TREE;
302 fprintf (dump_file, " - Recording base type:");
303 print_generic_expr (dump_file, base_type);
304 fprintf (dump_file, " (alias set %i) ref type:",
305 base_type ? get_alias_set (base_type) : 0);
306 print_generic_expr (dump_file, ref_type);
307 fprintf (dump_file, " (alias set %i)\n",
308 ref_type ? get_alias_set (ref_type) : 0);
311 tt->insert (base_type, ref_type);
314 /* Returns true if and only if we should store the access to EXPR.
315 Some accesses, e.g. loads from automatic variables, are not interesting. */
318 record_access_p (tree expr)
320 /* Non-escaping memory is fine */
321 tree t = get_base_address (expr);
322 if (t && (INDIRECT_REF_P (t)
323 || TREE_CODE (t) == MEM_REF
324 || TREE_CODE (t) == TARGET_MEM_REF)
325 && TREE_CODE (TREE_OPERAND (t, 0)) == SSA_NAME
326 && !ptr_deref_may_alias_global_p (TREE_OPERAND (t, 0)))
329 fprintf (dump_file, " - Non-escaping memory, ignoring.\n");
333 /* Automatic variables are fine. */
335 && auto_var_in_fn_p (t, current_function_decl))
338 fprintf (dump_file, " - Automatic variable, ignoring.\n");
342 /* Read-only variables are fine. */
343 if (DECL_P (t) && TREE_READONLY (t))
346 fprintf (dump_file, " - Read-only variable, ignoring.\n");
353 /* Return true if ECF flags says that stores can be ignored. */
356 ignore_stores_p (tree caller, int flags)
358 if (flags & ECF_PURE)
360 if ((flags & (ECF_NORETURN | ECF_NOTHROW)) == (ECF_NORETURN | ECF_NOTHROW)
361 || (!opt_for_fn (caller, flag_exceptions) && (flags & ECF_NORETURN)))
366 /* Analyze function call STMT in function F. */
369 analyze_call (modref_summary *cur_summary,
372 /* Check flags on the function call. In certain cases, analysis can be
374 int flags = gimple_call_flags (stmt);
375 if (flags & (ECF_CONST | ECF_NOVOPS))
379 " - ECF_CONST | ECF_NOVOPS, ignoring all stores and all loads "
380 "except for args.\n");
384 /* Pure functions do not affect global memory. Stores by functions which are
385 noreturn and do not throw can safely be ignored. */
386 bool ignore_stores = ignore_stores_p (current_function_decl, flags);
388 /* Next, we try to get the callee's function declaration. The goal is to
389 merge their summary with ours. */
390 tree callee = gimple_call_fndecl (stmt);
392 /* Check if this is an indirect call. */
395 /* If the indirect call does not write memory, our store summary is
396 unaffected, but we have to discard our loads summary (we don't know
397 anything about the loads that the called function performs). */
401 fprintf (dump_file, " - Indirect call which does not write memory, "
402 "discarding loads.\n");
403 if (cur_summary->loads)
404 cur_summary->loads->collapse ();
405 if (cur_summary->loads_lto)
406 cur_summary->loads_lto->collapse ();
410 fprintf (dump_file, " - Indirect call.\n");
414 struct cgraph_node *callee_node = cgraph_node::get_create (callee);
416 /* We can not safely optimize based on summary of callee if it does
417 not always bind to current def: it is possible that memory load
418 was optimized out earlier which may not happen in the interposed
420 if (!callee_node->binds_to_current_def_p ())
423 fprintf (dump_file, " - May be interposed: collapsing loads.\n");
424 if (cur_summary->loads)
425 cur_summary->loads->collapse ();
426 if (cur_summary->loads_lto)
427 cur_summary->loads_lto->collapse ();
430 /* If this is a recursive call, the target summary is the same as ours, so
431 there's nothing to do. */
432 if (recursive_call_p (current_function_decl, callee))
435 fprintf (dump_file, " - Skipping recursive call.\n");
439 gcc_assert (callee_node != NULL);
441 /* Get the function symbol and its availability. */
442 enum availability avail;
443 callee_node = callee_node->function_symbol (&avail);
444 if (avail <= AVAIL_INTERPOSABLE)
446 /* Keep stores summary, but discard all loads for interposable function
450 if (cur_summary->loads)
451 cur_summary->loads->collapse ();
452 if (cur_summary->loads_lto)
453 cur_summary->loads_lto->collapse ();
457 fprintf (dump_file, " - Function availability <= AVAIL_INTERPOSABLE.\n");
461 /* Get callee's modref summary. As above, if there's no summary, we either
462 have to give up or, if stores are ignored, we can just purge loads. */
463 modref_summary *callee_summary = summaries->get (callee_node);
468 if (cur_summary->loads)
469 cur_summary->loads->collapse ();
470 if (cur_summary->loads_lto)
471 cur_summary->loads_lto->collapse ();
475 fprintf (dump_file, " - No modref summary available for callee.\n");
479 /* Merge with callee's summary. */
480 if (cur_summary->loads)
481 cur_summary->loads->merge (callee_summary->loads);
482 if (cur_summary->loads_lto)
483 cur_summary->loads_lto->merge (callee_summary->loads_lto);
486 if (cur_summary->stores)
487 cur_summary->stores->merge (callee_summary->stores);
488 if (cur_summary->stores_lto)
489 cur_summary->stores_lto->merge (callee_summary->stores_lto);
495 /* Helper for analyze_stmt. */
498 analyze_load (gimple *, tree, tree op, void *data)
500 modref_summary *summary = (modref_summary *)data;
504 fprintf (dump_file, " - Analyzing load: ");
505 print_generic_expr (dump_file, op);
506 fprintf (dump_file, "\n");
509 if (!record_access_p (op))
513 ao_ref_init (&r, op);
516 record_access (summary->loads, &r);
517 if (summary->loads_lto)
518 record_access_lto (summary->loads_lto, &r);
522 /* Helper for analyze_stmt. */
525 analyze_store (gimple *, tree, tree op, void *data)
527 modref_summary *summary = (modref_summary *)data;
531 fprintf (dump_file, " - Analyzing store: ");
532 print_generic_expr (dump_file, op);
533 fprintf (dump_file, "\n");
536 if (!record_access_p (op))
540 ao_ref_init (&r, op);
543 record_access (((modref_summary *)data)->stores, &r);
544 if (summary->stores_lto)
545 record_access_lto (((modref_summary *)data)->stores_lto, &r);
549 /* Analyze statement STMT of function F.
550 If IPA is true do not merge in side effects of calls. */
553 analyze_stmt (modref_summary *summary, gimple *stmt, bool ipa)
555 /* Analyze all loads and stores in STMT. */
556 walk_stmt_load_store_ops (stmt, summary,
557 analyze_load, analyze_store);
558 /* or call analyze_load_ipa, analyze_store_ipa */
560 switch (gimple_code (stmt))
563 /* If the ASM statement does not read nor write memory, there's nothing
564 to do. Otherwise just give up. */
565 if (!gimple_asm_clobbers_memory_p (as_a <gasm *> (stmt)))
568 fprintf (dump_file, " - Function contains GIMPLE_ASM statement "
569 "which clobbers memory.\n");
573 return analyze_call (summary, stmt);
576 /* Nothing to do for other types of statements. */
581 /* Analyze function F. IPA indicates whether we're running in tree mode (false)
582 or the IPA mode (true). */
585 analyze_function (function *f, bool ipa)
588 fprintf (dump_file, "modref analyzing '%s' (ipa=%i)...\n",
589 function_name (f), ipa);
591 /* Don't analyze this function if it's compiled with -fno-strict-aliasing. */
592 if (!flag_ipa_modref)
595 /* Initialize the summary. */
597 summaries = new (ggc_alloc <modref_summaries> ())
598 modref_summaries (symtab);
599 else /* Remove existing summary if we are re-running the pass. */
600 summaries->remove (cgraph_node::get (f->decl));
602 ((modref_summaries *)summaries)->ipa = ipa;
604 modref_summary *summary = summaries->get_create (cgraph_node::get (f->decl));
606 /* Compute no-LTO summaries when local optimization is going to happen. */
607 bool nolto = (!ipa || ((!flag_lto || flag_fat_lto_objects) && !in_lto_p)
608 || (in_lto_p && !flag_wpa
609 && flag_incremental_link != INCREMENTAL_LINK_LTO));
611 /* Compute LTO when LTO streaming is going to happen. */
612 bool lto = ipa && ((flag_lto && !in_lto_p)
614 || flag_incremental_link == INCREMENTAL_LINK_LTO);
616 /* Create and initialize summary for F.
617 Note that summaries may be already allocated from previous
621 gcc_assert (!summary->loads);
623 = new (ggc_alloc <modref_tree<alias_set_type> > ())
624 modref_records (param_modref_max_bases,
625 param_modref_max_refs);
626 gcc_assert (!summary->stores);
628 = new (ggc_alloc <modref_tree<alias_set_type> > ())
629 modref_records (param_modref_max_bases,
630 param_modref_max_refs);
634 gcc_assert (!summary->loads_lto);
636 = new (ggc_alloc <modref_tree<tree> > ())
637 modref_records_lto (param_modref_max_bases,
638 param_modref_max_refs);
639 gcc_assert (!summary->stores_lto);
641 = new (ggc_alloc <modref_tree<tree> > ())
642 modref_records_lto (param_modref_max_bases,
643 param_modref_max_refs);
645 summary->finished = false;
647 /* Analyze each statement in each basic block of the function. If the
648 statement cannot be analyzed (for any reason), the entire function cannot
649 be analyzed by modref. */
651 FOR_EACH_BB_FN (bb, f)
653 gimple_stmt_iterator si;
654 for (si = gsi_after_labels (bb); !gsi_end_p (si); gsi_next (&si))
656 if (!analyze_stmt (summary, gsi_stmt (si), ipa))
658 cgraph_node *fnode = cgraph_node::get (current_function_decl);
659 summaries->remove (fnode);
662 " - modref done with result: not tracked.\n");
669 summary->finished = true;
673 fprintf (dump_file, " - modref done with result: tracked.\n");
674 summary->dump (dump_file);
678 /* Callback for generate_summary. */
681 modref_generate (void)
683 struct cgraph_node *node;
684 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
686 function *f = DECL_STRUCT_FUNCTION (node->decl);
690 analyze_function (f, true);
695 /* Called when a new function is inserted to callgraph late. */
698 modref_summaries::insert (struct cgraph_node *node, modref_summary *)
700 if (!DECL_STRUCT_FUNCTION (node->decl))
702 push_cfun (DECL_STRUCT_FUNCTION (node->decl));
703 analyze_function (DECL_STRUCT_FUNCTION (node->decl), ipa);
707 /* Called when new clone is inserted to callgraph late. */
710 modref_summaries::duplicate (cgraph_node *, cgraph_node *,
711 modref_summary *src_data,
712 modref_summary *dst_data)
714 dst_data->finished = src_data->finished;
715 if (src_data->stores)
717 dst_data->stores = new (ggc_alloc <modref_tree<alias_set_type> > ())
719 (src_data->stores->max_bases,
720 src_data->stores->max_refs);
721 dst_data->stores->merge (src_data->stores);
725 dst_data->loads = new (ggc_alloc <modref_tree<alias_set_type> > ())
727 (src_data->loads->max_bases,
728 src_data->loads->max_refs);
729 dst_data->loads->merge (src_data->loads);
731 if (src_data->stores_lto)
733 dst_data->stores_lto = new (ggc_alloc <modref_tree<tree> > ())
735 (src_data->stores_lto->max_bases,
736 src_data->stores_lto->max_refs);
737 dst_data->stores_lto->merge (src_data->stores_lto);
739 if (src_data->loads_lto)
741 dst_data->loads_lto = new (ggc_alloc <modref_tree<tree> > ())
743 (src_data->stores_lto->max_bases,
744 src_data->stores_lto->max_refs);
745 dst_data->loads_lto->merge (src_data->loads_lto);
751 /* Definition of the modref pass on GIMPLE. */
752 const pass_data pass_data_modref = {
757 (PROP_cfg | PROP_ssa),
764 class pass_modref : public gimple_opt_pass
767 pass_modref (gcc::context *ctxt)
768 : gimple_opt_pass (pass_data_modref, ctxt) {}
772 ggc_delete (summaries);
776 /* opt_pass methods: */
779 return new pass_modref (m_ctxt);
781 virtual bool gate (function *)
783 return flag_ipa_modref;
785 virtual unsigned int execute (function *);
788 /* Encode TT to the output block OB using the summary streaming API. */
791 write_modref_records (modref_records_lto *tt, struct output_block *ob)
793 streamer_write_uhwi (ob, tt->max_bases);
794 streamer_write_uhwi (ob, tt->max_refs);
796 streamer_write_uhwi (ob, tt->every_base);
797 streamer_write_uhwi (ob, vec_safe_length (tt->bases));
799 modref_base_node <tree> *base_node;
800 FOR_EACH_VEC_SAFE_ELT (tt->bases, i, base_node)
802 stream_write_tree (ob, base_node->base, true);
804 streamer_write_uhwi (ob, base_node->every_ref);
805 streamer_write_uhwi (ob, vec_safe_length (base_node->refs));
807 modref_ref_node <tree> *ref_node;
808 FOR_EACH_VEC_SAFE_ELT (base_node->refs, j, ref_node)
810 stream_write_tree (ob, ref_node->ref, true);
815 /* Read a modref_tree from the input block IB using the data from DATA_IN.
816 This assumes that the tree was encoded using write_modref_tree.
817 Either nolto_ret or lto_ret is initialized by the tree depending whether
818 LTO streaming is expected or not. */
821 read_modref_records (lto_input_block *ib, struct data_in *data_in,
822 modref_records **nolto_ret,
823 modref_records_lto **lto_ret)
825 size_t max_bases = streamer_read_uhwi (ib);
826 size_t max_refs = streamer_read_uhwi (ib);
828 /* Decide whether we want to turn LTO data types to non-LTO (i.e. when
829 LTO re-streaming is not going to happen). */
830 if (flag_wpa || flag_incremental_link == INCREMENTAL_LINK_LTO)
831 *lto_ret = new (ggc_alloc <modref_records_lto> ()) modref_records_lto
832 (max_bases, max_refs);
834 *nolto_ret = new (ggc_alloc <modref_records> ()) modref_records
835 (max_bases, max_refs);
837 size_t every_base = streamer_read_uhwi (ib);
838 size_t nbase = streamer_read_uhwi (ib);
840 gcc_assert (!every_base || nbase == 0);
844 (*nolto_ret)->collapse ();
846 (*lto_ret)->collapse ();
848 for (size_t i = 0; i < nbase; i++)
850 tree base_tree = stream_read_tree (ib, data_in);
851 modref_base_node <alias_set_type> *nolto_base_node = NULL;
852 modref_base_node <tree> *lto_base_node = NULL;
854 /* At stream in time we have LTO alias info. Check if we streamed in
855 something obviously unnecessary. Do not glob types by alias sets;
856 it is not 100% clear that ltrans types will get merged same way.
857 Types may get refined based on ODR type conflicts. */
858 if (base_tree && !get_alias_set (base_tree))
862 fprintf (dump_file, "Streamed in alias set 0 type ");
863 print_generic_expr (dump_file, base_tree);
864 fprintf (dump_file, "\n");
870 nolto_base_node = (*nolto_ret)->insert_base (base_tree
871 ? get_alias_set (base_tree)
874 lto_base_node = (*lto_ret)->insert_base (base_tree);
875 size_t every_ref = streamer_read_uhwi (ib);
876 size_t nref = streamer_read_uhwi (ib);
878 gcc_assert (!every_ref || nref == 0);
882 nolto_base_node->collapse ();
884 lto_base_node->collapse ();
886 for (size_t j = 0; j < nref; j++)
888 tree ref_tree = stream_read_tree (ib, data_in);
890 if (ref_tree && !get_alias_set (ref_tree))
894 fprintf (dump_file, "Streamed in alias set 0 type ");
895 print_generic_expr (dump_file, ref_tree);
896 fprintf (dump_file, "\n");
902 nolto_base_node->insert_ref (ref_tree ? get_alias_set (ref_tree)
905 lto_base_node->insert_ref (ref_tree, max_refs);
910 /* Callback for write_summary. */
915 struct output_block *ob = create_output_block (LTO_section_ipa_modref);
916 lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
917 unsigned int count = 0;
922 streamer_write_uhwi (ob, 0);
923 streamer_write_char_stream (ob->main_stream, 0);
924 produce_asm (ob, NULL);
925 destroy_output_block (ob);
929 for (i = 0; i < lto_symtab_encoder_size (encoder); i++)
931 symtab_node *snode = lto_symtab_encoder_deref (encoder, i);
932 cgraph_node *cnode = dyn_cast <cgraph_node *> (snode);
934 if (cnode && cnode->definition && !cnode->alias
935 && summaries->get (cnode))
938 streamer_write_uhwi (ob, count);
940 for (i = 0; i < lto_symtab_encoder_size (encoder); i++)
942 symtab_node *snode = lto_symtab_encoder_deref (encoder, i);
943 cgraph_node *cnode = dyn_cast <cgraph_node *> (snode);
945 if (cnode && cnode->definition && !cnode->alias)
948 modref_summary *r = summaries->get (cnode);
953 streamer_write_uhwi (ob, lto_symtab_encoder_encode (encoder, cnode));
955 streamer_write_uhwi (ob, r->loads_lto ? 1 : 0);
956 streamer_write_uhwi (ob, r->stores_lto ? 1 : 0);
958 write_modref_records (r->loads_lto, ob);
960 write_modref_records (r->stores_lto, ob);
963 streamer_write_char_stream (ob->main_stream, 0);
964 produce_asm (ob, NULL);
965 destroy_output_block (ob);
969 read_section (struct lto_file_decl_data *file_data, const char *data,
972 const struct lto_function_header *header
973 = (const struct lto_function_header *) data;
974 const int cfg_offset = sizeof (struct lto_function_header);
975 const int main_offset = cfg_offset + header->cfg_size;
976 const int string_offset = main_offset + header->main_size;
977 struct data_in *data_in;
979 unsigned int f_count;
981 lto_input_block ib ((const char *) data + main_offset, header->main_size,
982 file_data->mode_table);
985 = lto_data_in_create (file_data, (const char *) data + string_offset,
986 header->string_size, vNULL);
987 f_count = streamer_read_uhwi (&ib);
988 for (i = 0; i < f_count; i++)
990 struct cgraph_node *node;
991 lto_symtab_encoder_t encoder;
993 unsigned int index = streamer_read_uhwi (&ib);
994 encoder = file_data->symtab_node_encoder;
995 node = dyn_cast <cgraph_node *> (lto_symtab_encoder_deref (encoder,
998 modref_summary *modref_sum = summaries->get_create (node);
999 modref_sum->finished = false;
1000 int have_loads = streamer_read_uhwi (&ib);
1001 int have_stores = streamer_read_uhwi (&ib);
1002 gcc_assert (!modref_sum->loads_lto
1003 && !modref_sum->stores_lto
1004 && !modref_sum->loads
1005 && !modref_sum->stores);
1007 read_modref_records (&ib, data_in,
1009 &modref_sum->loads_lto);
1011 read_modref_records (&ib, data_in,
1012 &modref_sum->stores,
1013 &modref_sum->stores_lto);
1016 fprintf (dump_file, "Read modref for %s\n",
1017 node->dump_name ());
1018 modref_sum->dump (dump_file);
1021 modref_sum->finished = true;
1024 lto_free_section_data (file_data, LTO_section_ipa_modref, NULL, data,
1026 lto_data_in_delete (data_in);
1029 /* Callback for read_summary. */
1034 struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
1035 struct lto_file_decl_data *file_data;
1039 summaries = new (ggc_alloc <modref_summaries> ())
1040 modref_summaries (symtab);
1041 ((modref_summaries *)summaries)->ipa = true;
1043 while ((file_data = file_data_vec[j++]))
1046 const char *data = lto_get_summary_section_data (file_data,
1047 LTO_section_ipa_modref,
1050 read_section (file_data, data, len);
1052 /* Fatal error here. We do not want to support compiling ltrans units
1053 with different version of compiler or different flags than the WPA
1054 unit, so this should never happen. */
1055 fatal_error (input_location,
1056 "IPA modref summary is missing in input file");
1060 /* Definition of the modref IPA pass. */
1061 const pass_data pass_data_ipa_modref =
1063 IPA_PASS, /* type */
1064 "modref", /* name */
1065 OPTGROUP_IPA, /* optinfo_flags */
1066 TV_IPA_MODREF, /* tv_id */
1067 0, /* properties_required */
1068 0, /* properties_provided */
1069 0, /* properties_destroyed */
1070 0, /* todo_flags_start */
1071 ( TODO_dump_symtab ), /* todo_flags_finish */
1074 class pass_ipa_modref : public ipa_opt_pass_d
1077 pass_ipa_modref (gcc::context *ctxt)
1078 : ipa_opt_pass_d (pass_data_ipa_modref, ctxt,
1079 modref_generate, /* generate_summary */
1080 modref_write, /* write_summary */
1081 modref_read, /* read_summary */
1082 modref_write, /* write_optimization_summary */
1083 modref_read, /* read_optimization_summary */
1084 NULL, /* stmt_fixup */
1085 0, /* function_transform_todo_flags_start */
1086 NULL, /* function_transform */
1087 NULL) /* variable_transform */
1090 /* opt_pass methods: */
1091 opt_pass *clone () { return new pass_ipa_modref (m_ctxt); }
1092 virtual bool gate (function *)
1096 virtual unsigned int execute (function *);
1102 unsigned int pass_modref::execute (function *f)
1104 /* If new function is being added during IPA, we can skip analysis. */
1105 if (summaries && ((modref_summaries *)summaries)->ipa)
1107 analyze_function (f, false);
1112 make_pass_modref (gcc::context *ctxt)
1114 return new pass_modref (ctxt);
1118 make_pass_ipa_modref (gcc::context *ctxt)
1120 return new pass_ipa_modref (ctxt);
1123 /* Skip edges from and to nodes without ipa_pure_const enabled.
1124 Ignore not available symbols. */
1127 ignore_edge (struct cgraph_edge *e)
1129 enum availability avail;
1130 cgraph_node *callee = e->callee->function_or_virtual_thunk_symbol
1131 (&avail, e->caller);
1133 return (avail <= AVAIL_INTERPOSABLE
1134 || !summaries->get (callee)
1135 || flags_from_decl_or_type (e->callee->decl)
1136 & (ECF_CONST | ECF_NOVOPS));
1139 /* Run the IPA pass. This will take a function's summaries and calls and
1140 construct new summaries which represent a transitive closure. So that
1141 summary of an analyzed function contains information about the loads and
1142 stores that the function or any function that it calls does. */
1144 unsigned int pass_ipa_modref::execute (function *)
1149 struct cgraph_node **order = XCNEWVEC (struct cgraph_node *,
1150 symtab->cgraph_count);
1152 order_pos = ipa_reduced_postorder (order, true, ignore_edge);
1155 /* Iterate over all strongly connected components in post-order. */
1156 for (i = 0; i < order_pos; i++)
1158 bool its_hopeless = false;
1159 modref_records *loads = NULL;
1160 modref_records *stores = NULL;
1161 modref_records_lto *loads_lto = NULL;
1162 modref_records_lto *stores_lto = NULL;
1164 /* Get the component's representative. That's just any node in the
1165 component from which we can traverse the entire component. */
1166 struct cgraph_node *component_node = order[i];
1167 cgraph_node *first = NULL;
1170 fprintf (dump_file, "Start of SCC component\n");
1172 /* Walk the component. CUR is the current node of the component that's
1174 for (struct cgraph_node *cur = component_node; cur && !its_hopeless;
1175 cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
1177 /* Merge in summaries from CUR. */
1178 modref_summary *cur_summary = summaries->get (cur);
1181 fprintf (dump_file, " Processing %s\n",
1184 /* We don't know anything about CUR, hence we cannot tell anything
1185 about the entire component. */
1189 fprintf (dump_file, " No summary\n");
1190 its_hopeless = true;
1194 /* Summaries are all going to be same, pick first ones and merge
1199 loads = cur_summary->loads;
1200 stores = cur_summary->stores;
1201 loads_lto = cur_summary->loads_lto;
1202 stores_lto = cur_summary->stores_lto;
1204 for (cgraph_edge *e = cur->indirect_calls; e; e = e->next_callee)
1206 if (e->indirect_info->ecf_flags & (ECF_CONST | ECF_NOVOPS))
1208 if (ignore_stores_p (cur->decl, e->indirect_info->ecf_flags))
1211 fprintf (dump_file, " Indirect call: "
1212 "collapsing loads\n");
1216 loads_lto->collapse ();
1221 fprintf (dump_file, " Indirect call: giving up\n");
1222 its_hopeless = true;
1226 /* Walk every function that CUR calls and merge its summary. */
1227 for (cgraph_edge *callee_edge = cur->callees; callee_edge;
1228 callee_edge = callee_edge->next_callee)
1230 int flags = flags_from_decl_or_type (callee_edge->callee->decl);
1231 modref_summary *callee_summary;
1232 struct cgraph_node *callee;
1234 if (flags & (ECF_CONST | ECF_NOVOPS))
1238 fprintf (dump_file, " Call to %s\n",
1241 /* We can not safely optimize based on summary of callee if it
1242 does not always bind to current def: it is possible that
1243 memory load was optimized out earlier which may not happen in
1244 the interposed variant. */
1245 if (!callee_edge->binds_to_current_def_p ())
1250 loads_lto->collapse ();
1252 fprintf (dump_file, " May not bind local;"
1253 " collapsing loads\n");
1256 /* Get the callee and its summary. */
1257 enum availability avail;
1258 callee = callee_edge->callee->function_or_virtual_thunk_symbol
1261 /* See if we can derive something from ECF flags. Be careful on
1262 not skipping calls within the SCC component: we must merge
1263 all their summaries.
1264 If we switch to iterative dataflow that may be necessary
1265 for future improvements this may go away. */
1267 && ((struct ipa_dfs_info *)cur->aux)->scc_no
1268 == ((struct ipa_dfs_info *)callee->aux)->scc_no)
1271 bool ignore_stores = ignore_stores_p (cur->decl, flags);
1273 /* We don't know anything about CALLEE, hence we cannot tell
1274 anything about the entire component. */
1276 if (avail <= AVAIL_INTERPOSABLE
1277 || !(callee_summary = summaries->get (callee)))
1281 its_hopeless = true;
1282 if (dump_file && avail <= AVAIL_INTERPOSABLE)
1283 fprintf (dump_file, " Call target interposable"
1284 "or not available\n");
1286 fprintf (dump_file, " No call target summary\n");
1294 loads_lto->collapse ();
1295 if (dump_file && avail <= AVAIL_INTERPOSABLE)
1296 fprintf (dump_file, " Call target interposable"
1297 "or not available; collapsing loads\n");
1299 fprintf (dump_file, " No call target summary;"
1300 " collapsing loads\n");
1305 /* Merge in callee's information. */
1306 if (callee_summary->loads
1307 && callee_summary->loads != loads)
1308 loads->merge (callee_summary->loads);
1309 if (callee_summary->stores
1310 && callee_summary->stores != stores)
1311 stores->merge (callee_summary->stores);
1312 if (callee_summary->loads_lto
1313 && callee_summary->loads_lto != loads_lto)
1314 loads_lto->merge (callee_summary->loads_lto);
1315 if (callee_summary->stores_lto
1316 && callee_summary->stores_lto != stores_lto)
1317 stores_lto->merge (callee_summary->stores_lto);
1321 /* At this time, ipa_loads and ipa_stores contain information
1322 about all loads and stores done by any of the component's nodes and
1323 all functions that any of the nodes calls. We will now propagate
1324 this information to all nodes in the component. Therefore, we will
1325 walk the component one more time to do it. */
1326 for (struct cgraph_node *cur = component_node; cur;
1327 cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
1329 modref_summary *cur_summary = summaries->get (cur);
1332 /* The function doesn't have a summary. We must have noticed
1333 that during the first pass and the hopeless flag must
1334 therefore be set. Skip the function. */
1335 gcc_assert (its_hopeless);
1337 else if (its_hopeless)
1340 fprintf (dump_file, "Cleared modref info for %s\n",
1342 summaries->remove (cur);
1351 cur_summary->loads->merge (loads);
1353 cur_summary->stores->merge (stores);
1355 cur_summary->loads_lto->merge (loads_lto);
1357 cur_summary->stores_lto->merge (stores_lto);
1359 cur_summary->finished = true;
1362 fprintf (dump_file, "Propagated modref for %s%s%s\n",
1364 TREE_READONLY (cur->decl) ? " (const)" : "",
1365 DECL_PURE_P (cur->decl) ? " (pure)" : "");
1366 cur_summary->dump (dump_file);
1371 ((modref_summaries *)summaries)->ipa = false;
1372 ipa_free_postorder_info ();
1376 #include "gt-ipa-modref.h"