1/* Search for references that a functions loads or stores.
2 Copyright (C) 2020-2024 Free Software Foundation, Inc.
3 Contributed by David Cepelik and Jan Hubicka
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License
18along 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.
24
25 This file contains a tree pass and an IPA pass. Both performs the same
26 analysis however tree pass is executed during early and late optimization
27 passes to propagate info downwards in the compilation order. IPA pass
28 propagates across the callgraph and is able to handle recursion and works on
29 whole program during link-time analysis.
30
31 LTO mode differs from the local mode by not recording alias sets but types
32 that are translated to alias sets later. This is necessary in order stream
33 the information because the alias sets are rebuild at stream-in time and may
34 not correspond to ones seen during analysis. For this reason part of
35 analysis is duplicated.
36
37 The following information is computed
38 1) load/store access tree described in ipa-modref-tree.h
39 This is used by tree-ssa-alias to disambiguate load/stores
40 2) EAF flags used by points-to analysis (in tree-ssa-structalias).
41 and defined in tree-core.h.
42 and stored to optimization_summaries.
43
44 There are multiple summaries computed and used during the propagation:
45 - summaries holds summaries from analysis to IPA propagation
46 time.
47 - summaries_lto is same as summaries but holds them in a format
48 that can be streamed (as described above).
49 - fnspec_summary holds fnspec strings for call. This is
50 necessary because gimple_call_fnspec performs additional
51 analysis except for looking callee fndecl.
52 - escape_summary holds escape points for given call edge.
53 That is a vector recording what function parameters
54 may escape to a function call (and with what parameter index). */
55
56#include "config.h"
57#include "system.h"
58#include "coretypes.h"
59#include "backend.h"
60#include "tree.h"
61#include "gimple.h"
62#include "alloc-pool.h"
63#include "tree-pass.h"
64#include "gimple-iterator.h"
65#include "tree-dfa.h"
66#include "cgraph.h"
67#include "ipa-utils.h"
68#include "symbol-summary.h"
69#include "gimple-pretty-print.h"
70#include "gimple-walk.h"
71#include "print-tree.h"
72#include "tree-streamer.h"
73#include "alias.h"
74#include "calls.h"
75#include "ipa-modref-tree.h"
76#include "ipa-modref.h"
77#include "value-range.h"
78#include "sreal.h"
79#include "ipa-cp.h"
80#include "ipa-prop.h"
81#include "ipa-fnsummary.h"
82#include "attr-fnspec.h"
83#include "symtab-clones.h"
84#include "gimple-ssa.h"
85#include "tree-phinodes.h"
86#include "tree-ssa-operands.h"
87#include "ssa-iterators.h"
88#include "stringpool.h"
89#include "tree-ssanames.h"
90#include "attribs.h"
91#include "tree-cfg.h"
92#include "tree-eh.h"
93
94
95namespace {
96
97/* We record fnspec specifiers for call edges since they depends on actual
98 gimple statements. */
99
100class fnspec_summary
101{
102public:
103 char *fnspec;
104
105 fnspec_summary ()
106 : fnspec (NULL)
107 {
108 }
109
110 ~fnspec_summary ()
111 {
112 free (ptr: fnspec);
113 }
114};
115
116/* Summary holding fnspec string for a given call. */
117
118class fnspec_summaries_t : public call_summary <fnspec_summary *>
119{
120public:
121 fnspec_summaries_t (symbol_table *symtab)
122 : call_summary <fnspec_summary *> (symtab) {}
123 /* Hook that is called by summary when an edge is duplicated. */
124 void duplicate (cgraph_edge *,
125 cgraph_edge *,
126 fnspec_summary *src,
127 fnspec_summary *dst) final override
128 {
129 dst->fnspec = xstrdup (src->fnspec);
130 }
131};
132
133static fnspec_summaries_t *fnspec_summaries = NULL;
134
135/* Escape summary holds a vector of param indexes that escape to
136 a given call. */
137struct escape_entry
138{
139 /* Parameter that escapes at a given call. */
140 int parm_index;
141 /* Argument it escapes to. */
142 unsigned int arg;
143 /* Minimal flags known about the argument. */
144 eaf_flags_t min_flags;
145 /* Does it escape directly or indirectly? */
146 bool direct;
147};
148
149/* Dump EAF flags. */
150
151static void
152dump_eaf_flags (FILE *out, int flags, bool newline = true)
153{
154 if (flags & EAF_UNUSED)
155 fprintf (stream: out, format: " unused");
156 if (flags & EAF_NO_DIRECT_CLOBBER)
157 fprintf (stream: out, format: " no_direct_clobber");
158 if (flags & EAF_NO_INDIRECT_CLOBBER)
159 fprintf (stream: out, format: " no_indirect_clobber");
160 if (flags & EAF_NO_DIRECT_ESCAPE)
161 fprintf (stream: out, format: " no_direct_escape");
162 if (flags & EAF_NO_INDIRECT_ESCAPE)
163 fprintf (stream: out, format: " no_indirect_escape");
164 if (flags & EAF_NOT_RETURNED_DIRECTLY)
165 fprintf (stream: out, format: " not_returned_directly");
166 if (flags & EAF_NOT_RETURNED_INDIRECTLY)
167 fprintf (stream: out, format: " not_returned_indirectly");
168 if (flags & EAF_NO_DIRECT_READ)
169 fprintf (stream: out, format: " no_direct_read");
170 if (flags & EAF_NO_INDIRECT_READ)
171 fprintf (stream: out, format: " no_indirect_read");
172 if (newline)
173 fprintf (stream: out, format: "\n");
174}
175
176struct escape_summary
177{
178 auto_vec <escape_entry> esc;
179 void dump (FILE *out)
180 {
181 for (unsigned int i = 0; i < esc.length (); i++)
182 {
183 fprintf (stream: out, format: " parm %i arg %i %s min:",
184 esc[i].parm_index,
185 esc[i].arg,
186 esc[i].direct ? "(direct)" : "(indirect)");
187 dump_eaf_flags (out, flags: esc[i].min_flags, newline: false);
188 }
189 fprintf (stream: out, format: "\n");
190 }
191};
192
193class escape_summaries_t : public call_summary <escape_summary *>
194{
195public:
196 escape_summaries_t (symbol_table *symtab)
197 : call_summary <escape_summary *> (symtab) {}
198 /* Hook that is called by summary when an edge is duplicated. */
199 void duplicate (cgraph_edge *,
200 cgraph_edge *,
201 escape_summary *src,
202 escape_summary *dst) final override
203 {
204 dst->esc = src->esc.copy ();
205 }
206};
207
208static escape_summaries_t *escape_summaries = NULL;
209
210} /* ANON namespace: GTY annotated summaries can not be anonymous. */
211
212
213/* Class (from which there is one global instance) that holds modref summaries
214 for all analyzed functions. */
215
216class GTY((user)) modref_summaries
217 : public fast_function_summary <modref_summary *, va_gc>
218{
219public:
220 modref_summaries (symbol_table *symtab)
221 : fast_function_summary <modref_summary *, va_gc> (symtab) {}
222 void insert (cgraph_node *, modref_summary *state) final override;
223 void duplicate (cgraph_node *src_node,
224 cgraph_node *dst_node,
225 modref_summary *src_data,
226 modref_summary *dst_data) final override;
227 static modref_summaries *create_ggc (symbol_table *symtab)
228 {
229 return new (ggc_alloc_no_dtor<modref_summaries> ())
230 modref_summaries (symtab);
231 }
232};
233
234class modref_summary_lto;
235
236/* Class (from which there is one global instance) that holds modref summaries
237 for all analyzed functions. */
238
239class GTY((user)) modref_summaries_lto
240 : public fast_function_summary <modref_summary_lto *, va_gc>
241{
242public:
243 modref_summaries_lto (symbol_table *symtab)
244 : fast_function_summary <modref_summary_lto *, va_gc> (symtab),
245 propagated (false) {}
246 void insert (cgraph_node *, modref_summary_lto *state) final override;
247 void duplicate (cgraph_node *src_node,
248 cgraph_node *dst_node,
249 modref_summary_lto *src_data,
250 modref_summary_lto *dst_data) final override;
251 static modref_summaries_lto *create_ggc (symbol_table *symtab)
252 {
253 return new (ggc_alloc_no_dtor<modref_summaries_lto> ())
254 modref_summaries_lto (symtab);
255 }
256 bool propagated;
257};
258
259/* Global variable holding all modref summaries
260 (from analysis to IPA propagation time). */
261
262static GTY(()) fast_function_summary <modref_summary *, va_gc>
263 *summaries;
264
265/* Global variable holding all modref optimization summaries
266 (from IPA propagation time or used by local optimization pass). */
267
268static GTY(()) fast_function_summary <modref_summary *, va_gc>
269 *optimization_summaries;
270
271/* LTO summaries hold info from analysis to LTO streaming or from LTO
272 stream-in through propagation to LTO stream-out. */
273
274static GTY(()) fast_function_summary <modref_summary_lto *, va_gc>
275 *summaries_lto;
276
277/* Summary for a single function which this pass produces. */
278
279modref_summary::modref_summary ()
280 : loads (NULL), stores (NULL), retslot_flags (0), static_chain_flags (0),
281 writes_errno (false), side_effects (false), nondeterministic (false),
282 calls_interposable (false), global_memory_read (false),
283 global_memory_written (false), try_dse (false)
284{
285}
286
287modref_summary::~modref_summary ()
288{
289 if (loads)
290 ggc_delete (ptr: loads);
291 if (stores)
292 ggc_delete (ptr: stores);
293}
294
295/* Remove all flags from EAF_FLAGS that are implied by ECF_FLAGS and not
296 useful to track. If returns_void is true moreover clear
297 EAF_NOT_RETURNED. */
298static int
299remove_useless_eaf_flags (int eaf_flags, int ecf_flags, bool returns_void)
300{
301 if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
302 eaf_flags &= ~implicit_const_eaf_flags;
303 else if (ecf_flags & ECF_PURE)
304 eaf_flags &= ~implicit_pure_eaf_flags;
305 else if ((ecf_flags & ECF_NORETURN) || returns_void)
306 eaf_flags &= ~(EAF_NOT_RETURNED_DIRECTLY | EAF_NOT_RETURNED_INDIRECTLY);
307 return eaf_flags;
308}
309
310/* Return true if FLAGS holds some useful information. */
311
312static bool
313eaf_flags_useful_p (vec <eaf_flags_t> &flags, int ecf_flags)
314{
315 for (unsigned i = 0; i < flags.length (); i++)
316 if (remove_useless_eaf_flags (eaf_flags: flags[i], ecf_flags, returns_void: false))
317 return true;
318 return false;
319}
320
321/* Return true if summary is potentially useful for optimization.
322 If CHECK_FLAGS is false assume that arg_flags are useful. */
323
324bool
325modref_summary::useful_p (int ecf_flags, bool check_flags)
326{
327 if (arg_flags.length () && !check_flags)
328 return true;
329 if (check_flags && eaf_flags_useful_p (flags&: arg_flags, ecf_flags))
330 return true;
331 arg_flags.release ();
332 if (check_flags && remove_useless_eaf_flags (eaf_flags: retslot_flags, ecf_flags, returns_void: false))
333 return true;
334 if (check_flags
335 && remove_useless_eaf_flags (eaf_flags: static_chain_flags, ecf_flags, returns_void: false))
336 return true;
337 if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
338 return ((!side_effects || !nondeterministic)
339 && (ecf_flags & ECF_LOOPING_CONST_OR_PURE));
340 if (loads && !loads->every_base)
341 return true;
342 else
343 kills.release ();
344 if (ecf_flags & ECF_PURE)
345 return ((!side_effects || !nondeterministic)
346 && (ecf_flags & ECF_LOOPING_CONST_OR_PURE));
347 return stores && !stores->every_base;
348}
349
350/* Single function summary used for LTO. */
351
352typedef modref_tree <tree> modref_records_lto;
353struct GTY(()) modref_summary_lto
354{
355 /* Load and stores in functions using types rather then alias sets.
356
357 This is necessary to make the information streamable for LTO but is also
358 more verbose and thus more likely to hit the limits. */
359 modref_records_lto *loads;
360 modref_records_lto *stores;
361 auto_vec<modref_access_node> GTY((skip)) kills;
362 auto_vec<eaf_flags_t> GTY((skip)) arg_flags;
363 eaf_flags_t retslot_flags;
364 eaf_flags_t static_chain_flags;
365 unsigned writes_errno : 1;
366 unsigned side_effects : 1;
367 unsigned nondeterministic : 1;
368 unsigned calls_interposable : 1;
369
370 modref_summary_lto ();
371 ~modref_summary_lto ();
372 void dump (FILE *);
373 bool useful_p (int ecf_flags, bool check_flags = true);
374};
375
376/* Summary for a single function which this pass produces. */
377
378modref_summary_lto::modref_summary_lto ()
379 : loads (NULL), stores (NULL), retslot_flags (0), static_chain_flags (0),
380 writes_errno (false), side_effects (false), nondeterministic (false),
381 calls_interposable (false)
382{
383}
384
385modref_summary_lto::~modref_summary_lto ()
386{
387 if (loads)
388 ggc_delete (ptr: loads);
389 if (stores)
390 ggc_delete (ptr: stores);
391}
392
393
394/* Return true if lto summary is potentially useful for optimization.
395 If CHECK_FLAGS is false assume that arg_flags are useful. */
396
397bool
398modref_summary_lto::useful_p (int ecf_flags, bool check_flags)
399{
400 if (arg_flags.length () && !check_flags)
401 return true;
402 if (check_flags && eaf_flags_useful_p (flags&: arg_flags, ecf_flags))
403 return true;
404 arg_flags.release ();
405 if (check_flags && remove_useless_eaf_flags (eaf_flags: retslot_flags, ecf_flags, returns_void: false))
406 return true;
407 if (check_flags
408 && remove_useless_eaf_flags (eaf_flags: static_chain_flags, ecf_flags, returns_void: false))
409 return true;
410 if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
411 return ((!side_effects || !nondeterministic)
412 && (ecf_flags & ECF_LOOPING_CONST_OR_PURE));
413 if (loads && !loads->every_base)
414 return true;
415 else
416 kills.release ();
417 if (ecf_flags & ECF_PURE)
418 return ((!side_effects || !nondeterministic)
419 && (ecf_flags & ECF_LOOPING_CONST_OR_PURE));
420 return stores && !stores->every_base;
421}
422
423/* Dump records TT to OUT. */
424
425static void
426dump_records (modref_records *tt, FILE *out)
427{
428 if (tt->every_base)
429 {
430 fprintf (stream: out, format: " Every base\n");
431 return;
432 }
433 size_t i;
434 modref_base_node <alias_set_type> *n;
435 FOR_EACH_VEC_SAFE_ELT (tt->bases, i, n)
436 {
437 fprintf (stream: out, format: " Base %i: alias set %i\n", (int)i, n->base);
438 if (n->every_ref)
439 {
440 fprintf (stream: out, format: " Every ref\n");
441 continue;
442 }
443 size_t j;
444 modref_ref_node <alias_set_type> *r;
445 FOR_EACH_VEC_SAFE_ELT (n->refs, j, r)
446 {
447 fprintf (stream: out, format: " Ref %i: alias set %i\n", (int)j, r->ref);
448 if (r->every_access)
449 {
450 fprintf (stream: out, format: " Every access\n");
451 continue;
452 }
453 size_t k;
454 modref_access_node *a;
455 FOR_EACH_VEC_SAFE_ELT (r->accesses, k, a)
456 {
457 fprintf (stream: out, format: " access:");
458 a->dump (out);
459 }
460 }
461 }
462}
463
464/* Dump records TT to OUT. */
465
466static void
467dump_lto_records (modref_records_lto *tt, FILE *out)
468{
469 if (tt->every_base)
470 {
471 fprintf (stream: out, format: " Every base\n");
472 return;
473 }
474 size_t i;
475 modref_base_node <tree> *n;
476 FOR_EACH_VEC_SAFE_ELT (tt->bases, i, n)
477 {
478 fprintf (stream: out, format: " Base %i:", (int)i);
479 print_generic_expr (out, n->base);
480 fprintf (stream: out, format: " (alias set %i)\n",
481 n->base ? get_alias_set (n->base) : 0);
482 if (n->every_ref)
483 {
484 fprintf (stream: out, format: " Every ref\n");
485 continue;
486 }
487 size_t j;
488 modref_ref_node <tree> *r;
489 FOR_EACH_VEC_SAFE_ELT (n->refs, j, r)
490 {
491 fprintf (stream: out, format: " Ref %i:", (int)j);
492 print_generic_expr (out, r->ref);
493 fprintf (stream: out, format: " (alias set %i)\n",
494 r->ref ? get_alias_set (r->ref) : 0);
495 if (r->every_access)
496 {
497 fprintf (stream: out, format: " Every access\n");
498 continue;
499 }
500 size_t k;
501 modref_access_node *a;
502 FOR_EACH_VEC_SAFE_ELT (r->accesses, k, a)
503 {
504 fprintf (stream: out, format: " access:");
505 a->dump (out);
506 }
507 }
508 }
509}
510
511/* Dump all escape points of NODE to OUT. */
512
513static void
514dump_modref_edge_summaries (FILE *out, cgraph_node *node, int depth)
515{
516 int i = 0;
517 if (!escape_summaries)
518 return;
519 for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
520 {
521 class escape_summary *sum = escape_summaries->get (edge: e);
522 if (sum)
523 {
524 fprintf (stream: out, format: "%*sIndirect call %i in %s escapes:",
525 depth, "", i, node->dump_name ());
526 sum->dump (out);
527 }
528 i++;
529 }
530 for (cgraph_edge *e = node->callees; e; e = e->next_callee)
531 {
532 if (!e->inline_failed)
533 dump_modref_edge_summaries (out, node: e->callee, depth: depth + 1);
534 class escape_summary *sum = escape_summaries->get (edge: e);
535 if (sum)
536 {
537 fprintf (stream: out, format: "%*sCall %s->%s escapes:", depth, "",
538 node->dump_name (), e->callee->dump_name ());
539 sum->dump (out);
540 }
541 class fnspec_summary *fsum = fnspec_summaries->get (edge: e);
542 if (fsum)
543 {
544 fprintf (stream: out, format: "%*sCall %s->%s fnspec: %s\n", depth, "",
545 node->dump_name (), e->callee->dump_name (),
546 fsum->fnspec);
547 }
548 }
549}
550
551/* Remove all call edge summaries associated with NODE. */
552
553static void
554remove_modref_edge_summaries (cgraph_node *node)
555{
556 if (!escape_summaries)
557 return;
558 for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
559 escape_summaries->remove (edge: e);
560 for (cgraph_edge *e = node->callees; e; e = e->next_callee)
561 {
562 if (!e->inline_failed)
563 remove_modref_edge_summaries (node: e->callee);
564 escape_summaries->remove (edge: e);
565 fnspec_summaries->remove (edge: e);
566 }
567}
568
569/* Dump summary. */
570
571void
572modref_summary::dump (FILE *out) const
573{
574 if (loads)
575 {
576 fprintf (stream: out, format: " loads:\n");
577 dump_records (tt: loads, out);
578 }
579 if (stores)
580 {
581 fprintf (stream: out, format: " stores:\n");
582 dump_records (tt: stores, out);
583 }
584 if (kills.length ())
585 {
586 fprintf (stream: out, format: " kills:\n");
587 for (auto kill : kills)
588 {
589 fprintf (stream: out, format: " ");
590 kill.dump (out);
591 }
592 }
593 if (writes_errno)
594 fprintf (stream: out, format: " Writes errno\n");
595 if (side_effects)
596 fprintf (stream: out, format: " Side effects\n");
597 if (nondeterministic)
598 fprintf (stream: out, format: " Nondeterministic\n");
599 if (calls_interposable)
600 fprintf (stream: out, format: " Calls interposable\n");
601 if (global_memory_read)
602 fprintf (stream: out, format: " Global memory read\n");
603 if (global_memory_written)
604 fprintf (stream: out, format: " Global memory written\n");
605 if (try_dse)
606 fprintf (stream: out, format: " Try dse\n");
607 if (arg_flags.length ())
608 {
609 for (unsigned int i = 0; i < arg_flags.length (); i++)
610 if (arg_flags[i])
611 {
612 fprintf (stream: out, format: " parm %i flags:", i);
613 dump_eaf_flags (out, flags: arg_flags[i]);
614 }
615 }
616 if (retslot_flags)
617 {
618 fprintf (stream: out, format: " Retslot flags:");
619 dump_eaf_flags (out, flags: retslot_flags);
620 }
621 if (static_chain_flags)
622 {
623 fprintf (stream: out, format: " Static chain flags:");
624 dump_eaf_flags (out, flags: static_chain_flags);
625 }
626}
627
628/* Dump summary. */
629
630void
631modref_summary_lto::dump (FILE *out)
632{
633 fprintf (stream: out, format: " loads:\n");
634 dump_lto_records (tt: loads, out);
635 fprintf (stream: out, format: " stores:\n");
636 dump_lto_records (tt: stores, out);
637 if (kills.length ())
638 {
639 fprintf (stream: out, format: " kills:\n");
640 for (auto kill : kills)
641 {
642 fprintf (stream: out, format: " ");
643 kill.dump (out);
644 }
645 }
646 if (writes_errno)
647 fprintf (stream: out, format: " Writes errno\n");
648 if (side_effects)
649 fprintf (stream: out, format: " Side effects\n");
650 if (nondeterministic)
651 fprintf (stream: out, format: " Nondeterministic\n");
652 if (calls_interposable)
653 fprintf (stream: out, format: " Calls interposable\n");
654 if (arg_flags.length ())
655 {
656 for (unsigned int i = 0; i < arg_flags.length (); i++)
657 if (arg_flags[i])
658 {
659 fprintf (stream: out, format: " parm %i flags:", i);
660 dump_eaf_flags (out, flags: arg_flags[i]);
661 }
662 }
663 if (retslot_flags)
664 {
665 fprintf (stream: out, format: " Retslot flags:");
666 dump_eaf_flags (out, flags: retslot_flags);
667 }
668 if (static_chain_flags)
669 {
670 fprintf (stream: out, format: " Static chain flags:");
671 dump_eaf_flags (out, flags: static_chain_flags);
672 }
673}
674
675/* Called after summary is produced and before it is used by local analysis.
676 Can be called multiple times in case summary needs to update signature.
677 FUN is decl of function summary is attached to. */
678void
679modref_summary::finalize (tree fun)
680{
681 global_memory_read = !loads || loads->global_access_p ();
682 global_memory_written = !stores || stores->global_access_p ();
683
684 /* We can do DSE if we know function has no side effects and
685 we can analyze all stores. Disable dse if there are too many
686 stores to try. */
687 if (side_effects || global_memory_written || writes_errno)
688 try_dse = false;
689 else
690 {
691 try_dse = true;
692 size_t i, j, k;
693 int num_tests = 0, max_tests
694 = opt_for_fn (fun, param_modref_max_tests);
695 modref_base_node <alias_set_type> *base_node;
696 modref_ref_node <alias_set_type> *ref_node;
697 modref_access_node *access_node;
698 FOR_EACH_VEC_SAFE_ELT (stores->bases, i, base_node)
699 {
700 if (base_node->every_ref)
701 {
702 try_dse = false;
703 break;
704 }
705 FOR_EACH_VEC_SAFE_ELT (base_node->refs, j, ref_node)
706 {
707 if (base_node->every_ref)
708 {
709 try_dse = false;
710 break;
711 }
712 FOR_EACH_VEC_SAFE_ELT (ref_node->accesses, k, access_node)
713 if (num_tests++ > max_tests
714 || !access_node->parm_offset_known)
715 {
716 try_dse = false;
717 break;
718 }
719 if (!try_dse)
720 break;
721 }
722 if (!try_dse)
723 break;
724 }
725 }
726 if (loads->every_base)
727 load_accesses = 1;
728 else
729 {
730 load_accesses = 0;
731 for (auto base_node : loads->bases)
732 {
733 if (base_node->every_ref)
734 load_accesses++;
735 else
736 for (auto ref_node : base_node->refs)
737 if (ref_node->every_access)
738 load_accesses++;
739 else
740 load_accesses += ref_node->accesses->length ();
741 }
742 }
743}
744
745/* Get function summary for FUNC if it exists, return NULL otherwise. */
746
747modref_summary *
748get_modref_function_summary (cgraph_node *func)
749{
750 /* Avoid creation of the summary too early (e.g. when front-end calls us). */
751 if (!optimization_summaries)
752 return NULL;
753
754 /* A single function body may be represented by multiple symbols with
755 different visibility. For example, if FUNC is an interposable alias,
756 we don't want to return anything, even if we have summary for the target
757 function. */
758 enum availability avail;
759 func = func->ultimate_alias_target
760 (availability: &avail, ref: current_function_decl ?
761 cgraph_node::get (decl: current_function_decl) : NULL);
762 if (avail <= AVAIL_INTERPOSABLE)
763 return NULL;
764
765 modref_summary *r = optimization_summaries->get (node: func);
766 return r;
767}
768
769/* Get function summary for CALL if it exists, return NULL otherwise.
770 If non-null set interposed to indicate whether function may not
771 bind to current def. In this case sometimes loads from function
772 needs to be ignored. */
773
774modref_summary *
775get_modref_function_summary (gcall *call, bool *interposed)
776{
777 tree callee = gimple_call_fndecl (gs: call);
778 if (!callee)
779 return NULL;
780 struct cgraph_node *node = cgraph_node::get (decl: callee);
781 if (!node)
782 return NULL;
783 modref_summary *r = get_modref_function_summary (func: node);
784 if (interposed && r)
785 *interposed = r->calls_interposable
786 || !node->binds_to_current_def_p ();
787 return r;
788}
789
790
791namespace {
792
793/* Return true if ECF flags says that nondeterminism can be ignored. */
794
795static bool
796ignore_nondeterminism_p (tree caller, int flags)
797{
798 if (flags & (ECF_CONST | ECF_PURE))
799 return true;
800 if ((flags & (ECF_NORETURN | ECF_NOTHROW)) == (ECF_NORETURN | ECF_NOTHROW)
801 || (!opt_for_fn (caller, flag_exceptions) && (flags & ECF_NORETURN)))
802 return true;
803 return false;
804}
805
806/* Return true if ECF flags says that return value can be ignored. */
807
808static bool
809ignore_retval_p (tree caller, int flags)
810{
811 if ((flags & (ECF_NORETURN | ECF_NOTHROW)) == (ECF_NORETURN | ECF_NOTHROW)
812 || (!opt_for_fn (caller, flag_exceptions) && (flags & ECF_NORETURN)))
813 return true;
814 return false;
815}
816
817/* Return true if ECF flags says that stores can be ignored. */
818
819static bool
820ignore_stores_p (tree caller, int flags)
821{
822 if (flags & (ECF_PURE | ECF_CONST | ECF_NOVOPS))
823 return true;
824 if ((flags & (ECF_NORETURN | ECF_NOTHROW)) == (ECF_NORETURN | ECF_NOTHROW)
825 || (!opt_for_fn (caller, flag_exceptions) && (flags & ECF_NORETURN)))
826 return true;
827 return false;
828}
829
830/* Determine parm_map for PTR which is supposed to be a pointer. */
831
832modref_parm_map
833parm_map_for_ptr (tree op)
834{
835 bool offset_known;
836 poly_int64 offset;
837 struct modref_parm_map parm_map;
838 gcall *call;
839
840 parm_map.parm_offset_known = false;
841 parm_map.parm_offset = 0;
842
843 offset_known = unadjusted_ptr_and_unit_offset (op, ret: &op, offset_ret: &offset);
844 if (TREE_CODE (op) == SSA_NAME
845 && SSA_NAME_IS_DEFAULT_DEF (op)
846 && TREE_CODE (SSA_NAME_VAR (op)) == PARM_DECL)
847 {
848 int index = 0;
849
850 if (cfun->static_chain_decl
851 && op == ssa_default_def (cfun, cfun->static_chain_decl))
852 index = MODREF_STATIC_CHAIN_PARM;
853 else
854 for (tree t = DECL_ARGUMENTS (current_function_decl);
855 t != SSA_NAME_VAR (op); t = DECL_CHAIN (t))
856 index++;
857 parm_map.parm_index = index;
858 parm_map.parm_offset_known = offset_known;
859 parm_map.parm_offset = offset;
860 }
861 else if (points_to_local_or_readonly_memory_p (op))
862 parm_map.parm_index = MODREF_LOCAL_MEMORY_PARM;
863 /* Memory allocated in the function is not visible to caller before the
864 call and thus we do not need to record it as load/stores/kills. */
865 else if (TREE_CODE (op) == SSA_NAME
866 && (call = dyn_cast<gcall *>(SSA_NAME_DEF_STMT (op))) != NULL
867 && gimple_call_flags (call) & ECF_MALLOC)
868 parm_map.parm_index = MODREF_LOCAL_MEMORY_PARM;
869 else
870 parm_map.parm_index = MODREF_UNKNOWN_PARM;
871 return parm_map;
872}
873
874/* Return true if ARG with EAF flags FLAGS can not make any caller's parameter
875 used (if LOAD is true we check loads, otherwise stores). */
876
877static bool
878verify_arg (tree arg, int flags, bool load)
879{
880 if (flags & EAF_UNUSED)
881 return true;
882 if (load && (flags & EAF_NO_DIRECT_READ))
883 return true;
884 if (!load
885 && (flags & (EAF_NO_DIRECT_CLOBBER | EAF_NO_INDIRECT_CLOBBER))
886 == (EAF_NO_DIRECT_CLOBBER | EAF_NO_INDIRECT_CLOBBER))
887 return true;
888 if (is_gimple_constant (t: arg))
889 return true;
890 if (DECL_P (arg) && TREE_READONLY (arg))
891 return true;
892 if (TREE_CODE (arg) == ADDR_EXPR)
893 {
894 tree t = get_base_address (TREE_OPERAND (arg, 0));
895 if (is_gimple_constant (t))
896 return true;
897 if (DECL_P (t)
898 && (TREE_READONLY (t) || TREE_CODE (t) == FUNCTION_DECL))
899 return true;
900 }
901 return false;
902}
903
904/* Return true if STMT may access memory that is pointed to by parameters
905 of caller and which is not seen as an escape by PTA.
906 CALLEE_ECF_FLAGS are ECF flags of callee. If LOAD is true then by access
907 we mean load, otherwise we mean store. */
908
909static bool
910may_access_nonescaping_parm_p (gcall *call, int callee_ecf_flags, bool load)
911{
912 int implicit_flags = 0;
913
914 if (ignore_stores_p (caller: current_function_decl, flags: callee_ecf_flags))
915 implicit_flags |= ignore_stores_eaf_flags;
916 if (callee_ecf_flags & ECF_PURE)
917 implicit_flags |= implicit_pure_eaf_flags;
918 if (callee_ecf_flags & (ECF_CONST | ECF_NOVOPS))
919 implicit_flags |= implicit_const_eaf_flags;
920 if (gimple_call_chain (gs: call)
921 && !verify_arg (arg: gimple_call_chain (gs: call),
922 flags: gimple_call_static_chain_flags (call) | implicit_flags,
923 load))
924 return true;
925 for (unsigned int i = 0; i < gimple_call_num_args (gs: call); i++)
926 if (!verify_arg (arg: gimple_call_arg (gs: call, index: i),
927 flags: gimple_call_arg_flags (call, i) | implicit_flags,
928 load))
929 return true;
930 return false;
931}
932
933
934/* Analyze memory accesses (loads, stores and kills) performed
935 by the function. Set also side_effects, calls_interposable
936 and nondeterminism flags. */
937
938class modref_access_analysis
939{
940public:
941 modref_access_analysis (bool ipa, modref_summary *summary,
942 modref_summary_lto *summary_lto)
943 : m_summary (summary), m_summary_lto (summary_lto), m_ipa (ipa)
944 {
945 }
946 void analyze ();
947private:
948 bool set_side_effects ();
949 bool set_nondeterministic ();
950 static modref_access_node get_access (ao_ref *ref);
951 static void record_access (modref_records *, ao_ref *, modref_access_node &);
952 static void record_access_lto (modref_records_lto *, ao_ref *,
953 modref_access_node &a);
954 bool record_access_p (tree);
955 bool record_unknown_load ();
956 bool record_unknown_store ();
957 bool record_global_memory_load ();
958 bool record_global_memory_store ();
959 bool merge_call_side_effects (gimple *, modref_summary *,
960 cgraph_node *, bool);
961 modref_access_node get_access_for_fnspec (gcall *, attr_fnspec &,
962 unsigned int, modref_parm_map &);
963 void process_fnspec (gcall *);
964 void analyze_call (gcall *);
965 static bool analyze_load (gimple *, tree, tree, void *);
966 static bool analyze_store (gimple *, tree, tree, void *);
967 void analyze_stmt (gimple *, bool);
968 void propagate ();
969
970 /* Summary being computed.
971 We work either with m_summary or m_summary_lto. Never on both. */
972 modref_summary *m_summary;
973 modref_summary_lto *m_summary_lto;
974 /* Recursive calls needs simplistic dataflow after analysis finished.
975 Collect all calls into this vector during analysis and later process
976 them in propagate. */
977 auto_vec <gimple *, 32> m_recursive_calls;
978 /* ECF flags of function being analyzed. */
979 int m_ecf_flags;
980 /* True if IPA propagation will be done later. */
981 bool m_ipa;
982 /* Set true if statement currently analyze is known to be
983 executed each time function is called. */
984 bool m_always_executed;
985};
986
987/* Set side_effects flag and return if something changed. */
988
989bool
990modref_access_analysis::set_side_effects ()
991{
992 bool changed = false;
993
994 if (m_summary && !m_summary->side_effects)
995 {
996 m_summary->side_effects = true;
997 changed = true;
998 }
999 if (m_summary_lto && !m_summary_lto->side_effects)
1000 {
1001 m_summary_lto->side_effects = true;
1002 changed = true;
1003 }
1004 return changed;
1005}
1006
1007/* Set nondeterministic flag and return if something changed. */
1008
1009bool
1010modref_access_analysis::set_nondeterministic ()
1011{
1012 bool changed = false;
1013
1014 if (m_summary && !m_summary->nondeterministic)
1015 {
1016 m_summary->side_effects = m_summary->nondeterministic = true;
1017 changed = true;
1018 }
1019 if (m_summary_lto && !m_summary_lto->nondeterministic)
1020 {
1021 m_summary_lto->side_effects = m_summary_lto->nondeterministic = true;
1022 changed = true;
1023 }
1024 return changed;
1025}
1026
1027/* Construct modref_access_node from REF. */
1028
1029modref_access_node
1030modref_access_analysis::get_access (ao_ref *ref)
1031{
1032 tree base;
1033
1034 base = ao_ref_base (ref);
1035 modref_access_node a = {.offset: ref->offset, .size: ref->size, .max_size: ref->max_size,
1036 .parm_offset: 0, .parm_index: MODREF_UNKNOWN_PARM, .parm_offset_known: false, .adjustments: 0};
1037 if (TREE_CODE (base) == MEM_REF || TREE_CODE (base) == TARGET_MEM_REF)
1038 {
1039 tree memref = base;
1040 modref_parm_map m = parm_map_for_ptr (TREE_OPERAND (base, 0));
1041
1042 a.parm_index = m.parm_index;
1043 if (a.parm_index != MODREF_UNKNOWN_PARM && TREE_CODE (memref) == MEM_REF)
1044 {
1045 a.parm_offset_known
1046 = wi::to_poly_wide (TREE_OPERAND
1047 (memref, 1)).to_shwi (r: &a.parm_offset);
1048 if (a.parm_offset_known && m.parm_offset_known)
1049 a.parm_offset += m.parm_offset;
1050 else
1051 a.parm_offset_known = false;
1052 }
1053 }
1054 else
1055 a.parm_index = MODREF_UNKNOWN_PARM;
1056 return a;
1057}
1058
1059/* Record access into the modref_records data structure. */
1060
1061void
1062modref_access_analysis::record_access (modref_records *tt,
1063 ao_ref *ref,
1064 modref_access_node &a)
1065{
1066 alias_set_type base_set = !flag_strict_aliasing
1067 || !flag_ipa_strict_aliasing ? 0
1068 : ao_ref_base_alias_set (ref);
1069 alias_set_type ref_set = !flag_strict_aliasing
1070 || !flag_ipa_strict_aliasing ? 0
1071 : (ao_ref_alias_set (ref));
1072 if (dump_file)
1073 {
1074 fprintf (stream: dump_file, format: " - Recording base_set=%i ref_set=%i ",
1075 base_set, ref_set);
1076 a.dump (out: dump_file);
1077 }
1078 tt->insert (fndecl: current_function_decl, base: base_set, ref: ref_set, a, record_adjustments: false);
1079}
1080
1081/* IPA version of record_access_tree. */
1082
1083void
1084modref_access_analysis::record_access_lto (modref_records_lto *tt, ao_ref *ref,
1085 modref_access_node &a)
1086{
1087 /* get_alias_set sometimes use different type to compute the alias set
1088 than TREE_TYPE (base). Do same adjustments. */
1089 tree base_type = NULL_TREE, ref_type = NULL_TREE;
1090 if (flag_strict_aliasing && flag_ipa_strict_aliasing)
1091 {
1092 tree base;
1093
1094 base = ref->ref;
1095 while (handled_component_p (t: base))
1096 base = TREE_OPERAND (base, 0);
1097
1098 base_type = reference_alias_ptr_type_1 (&base);
1099
1100 if (!base_type)
1101 base_type = TREE_TYPE (base);
1102 else
1103 base_type = TYPE_REF_CAN_ALIAS_ALL (base_type)
1104 ? NULL_TREE : TREE_TYPE (base_type);
1105
1106 tree ref_expr = ref->ref;
1107 ref_type = reference_alias_ptr_type_1 (&ref_expr);
1108
1109 if (!ref_type)
1110 ref_type = TREE_TYPE (ref_expr);
1111 else
1112 ref_type = TYPE_REF_CAN_ALIAS_ALL (ref_type)
1113 ? NULL_TREE : TREE_TYPE (ref_type);
1114
1115 /* Sanity check that we are in sync with what get_alias_set does. */
1116 gcc_checking_assert ((!base_type && !ao_ref_base_alias_set (ref))
1117 || get_alias_set (base_type)
1118 == ao_ref_base_alias_set (ref));
1119 gcc_checking_assert ((!ref_type && !ao_ref_alias_set (ref))
1120 || get_alias_set (ref_type)
1121 == ao_ref_alias_set (ref));
1122
1123 /* Do not bother to record types that have no meaningful alias set.
1124 Also skip variably modified types since these go to local streams. */
1125 if (base_type && (!get_alias_set (base_type)
1126 || variably_modified_type_p (base_type, NULL_TREE)))
1127 base_type = NULL_TREE;
1128 if (ref_type && (!get_alias_set (ref_type)
1129 || variably_modified_type_p (ref_type, NULL_TREE)))
1130 ref_type = NULL_TREE;
1131 }
1132 if (dump_file)
1133 {
1134 fprintf (stream: dump_file, format: " - Recording base type:");
1135 print_generic_expr (dump_file, base_type);
1136 fprintf (stream: dump_file, format: " (alias set %i) ref type:",
1137 base_type ? get_alias_set (base_type) : 0);
1138 print_generic_expr (dump_file, ref_type);
1139 fprintf (stream: dump_file, format: " (alias set %i) ",
1140 ref_type ? get_alias_set (ref_type) : 0);
1141 a.dump (out: dump_file);
1142 }
1143
1144 tt->insert (fndecl: current_function_decl, base: base_type, ref: ref_type, a, record_adjustments: false);
1145}
1146
1147/* Returns true if and only if we should store the access to EXPR.
1148 Some accesses, e.g. loads from automatic variables, are not interesting. */
1149
1150bool
1151modref_access_analysis::record_access_p (tree expr)
1152{
1153 if (TREE_THIS_VOLATILE (expr))
1154 {
1155 if (dump_file)
1156 fprintf (stream: dump_file, format: " (volatile; marking nondeterministic) ");
1157 set_nondeterministic ();
1158 }
1159 if (cfun->can_throw_non_call_exceptions
1160 && tree_could_throw_p (expr))
1161 {
1162 if (dump_file)
1163 fprintf (stream: dump_file, format: " (can throw; marking side effects) ");
1164 set_side_effects ();
1165 }
1166
1167 if (refs_local_or_readonly_memory_p (expr))
1168 {
1169 if (dump_file)
1170 fprintf (stream: dump_file, format: " - Read-only or local, ignoring.\n");
1171 return false;
1172 }
1173 return true;
1174}
1175
1176/* Collapse loads and return true if something changed. */
1177
1178bool
1179modref_access_analysis::record_unknown_load ()
1180{
1181 bool changed = false;
1182
1183 if (m_summary && !m_summary->loads->every_base)
1184 {
1185 m_summary->loads->collapse ();
1186 changed = true;
1187 }
1188 if (m_summary_lto && !m_summary_lto->loads->every_base)
1189 {
1190 m_summary_lto->loads->collapse ();
1191 changed = true;
1192 }
1193 return changed;
1194}
1195
1196/* Collapse loads and return true if something changed. */
1197
1198bool
1199modref_access_analysis::record_unknown_store ()
1200{
1201 bool changed = false;
1202
1203 if (m_summary && !m_summary->stores->every_base)
1204 {
1205 m_summary->stores->collapse ();
1206 changed = true;
1207 }
1208 if (m_summary_lto && !m_summary_lto->stores->every_base)
1209 {
1210 m_summary_lto->stores->collapse ();
1211 changed = true;
1212 }
1213 return changed;
1214}
1215
1216/* Record unknown load from global memory. */
1217
1218bool
1219modref_access_analysis::record_global_memory_load ()
1220{
1221 bool changed = false;
1222 modref_access_node a = {.offset: 0, .size: -1, .max_size: -1,
1223 .parm_offset: 0, .parm_index: MODREF_GLOBAL_MEMORY_PARM, .parm_offset_known: false, .adjustments: 0};
1224
1225 if (m_summary && !m_summary->loads->every_base)
1226 changed |= m_summary->loads->insert (fndecl: current_function_decl, base: 0, ref: 0, a, record_adjustments: false);
1227 if (m_summary_lto && !m_summary_lto->loads->every_base)
1228 changed |= m_summary_lto->loads->insert (fndecl: current_function_decl,
1229 base: 0, ref: 0, a, record_adjustments: false);
1230 return changed;
1231}
1232
1233/* Record unknown store from global memory. */
1234
1235bool
1236modref_access_analysis::record_global_memory_store ()
1237{
1238 bool changed = false;
1239 modref_access_node a = {.offset: 0, .size: -1, .max_size: -1,
1240 .parm_offset: 0, .parm_index: MODREF_GLOBAL_MEMORY_PARM, .parm_offset_known: false, .adjustments: 0};
1241
1242 if (m_summary && !m_summary->stores->every_base)
1243 changed |= m_summary->stores->insert (fndecl: current_function_decl,
1244 base: 0, ref: 0, a, record_adjustments: false);
1245 if (m_summary_lto && !m_summary_lto->stores->every_base)
1246 changed |= m_summary_lto->stores->insert (fndecl: current_function_decl,
1247 base: 0, ref: 0, a, record_adjustments: false);
1248 return changed;
1249}
1250
1251/* Merge side effects of call STMT to function with CALLEE_SUMMARY.
1252 Return true if something changed.
1253 If IGNORE_STORES is true, do not merge stores.
1254 If RECORD_ADJUSTMENTS is true cap number of adjustments to
1255 a given access to make dataflow finite. */
1256
1257bool
1258modref_access_analysis::merge_call_side_effects
1259 (gimple *stmt, modref_summary *callee_summary,
1260 cgraph_node *callee_node, bool record_adjustments)
1261{
1262 gcall *call = as_a <gcall *> (p: stmt);
1263 int flags = gimple_call_flags (call);
1264
1265 /* Nothing to do for non-looping cont functions. */
1266 if ((flags & (ECF_CONST | ECF_NOVOPS))
1267 && !(flags & ECF_LOOPING_CONST_OR_PURE))
1268 return false;
1269
1270 bool changed = false;
1271
1272 if (dump_file)
1273 fprintf (stream: dump_file, format: " - Merging side effects of %s\n",
1274 callee_node->dump_name ());
1275
1276 /* Merge side effects and non-determinism.
1277 PURE/CONST flags makes functions deterministic and if there is
1278 no LOOPING_CONST_OR_PURE they also have no side effects. */
1279 if (!(flags & (ECF_CONST | ECF_NOVOPS | ECF_PURE))
1280 || (flags & ECF_LOOPING_CONST_OR_PURE))
1281 {
1282 if (!m_summary->side_effects && callee_summary->side_effects)
1283 {
1284 if (dump_file)
1285 fprintf (stream: dump_file, format: " - merging side effects.\n");
1286 m_summary->side_effects = true;
1287 changed = true;
1288 }
1289 if (!m_summary->nondeterministic && callee_summary->nondeterministic
1290 && !ignore_nondeterminism_p (caller: current_function_decl, flags))
1291 {
1292 if (dump_file)
1293 fprintf (stream: dump_file, format: " - merging nondeterministic.\n");
1294 m_summary->nondeterministic = true;
1295 changed = true;
1296 }
1297 }
1298
1299 /* For const functions we are done. */
1300 if (flags & (ECF_CONST | ECF_NOVOPS))
1301 return changed;
1302
1303 /* Merge calls_interposable flags. */
1304 if (!m_summary->calls_interposable && callee_summary->calls_interposable)
1305 {
1306 if (dump_file)
1307 fprintf (stream: dump_file, format: " - merging calls interposable.\n");
1308 m_summary->calls_interposable = true;
1309 changed = true;
1310 }
1311
1312 if (!callee_node->binds_to_current_def_p () && !m_summary->calls_interposable)
1313 {
1314 if (dump_file)
1315 fprintf (stream: dump_file, format: " - May be interposed.\n");
1316 m_summary->calls_interposable = true;
1317 changed = true;
1318 }
1319
1320 /* Now merge the actual load, store and kill vectors. For this we need
1321 to compute map translating new parameters to old. */
1322 if (dump_file)
1323 fprintf (stream: dump_file, format: " Parm map:");
1324
1325 auto_vec <modref_parm_map, 32> parm_map;
1326 parm_map.safe_grow_cleared (len: gimple_call_num_args (gs: call), exact: true);
1327 for (unsigned i = 0; i < gimple_call_num_args (gs: call); i++)
1328 {
1329 parm_map[i] = parm_map_for_ptr (op: gimple_call_arg (gs: call, index: i));
1330 if (dump_file)
1331 {
1332 fprintf (stream: dump_file, format: " %i", parm_map[i].parm_index);
1333 if (parm_map[i].parm_offset_known)
1334 {
1335 fprintf (stream: dump_file, format: " offset:");
1336 print_dec (value: (poly_int64)parm_map[i].parm_offset,
1337 file: dump_file, sgn: SIGNED);
1338 }
1339 }
1340 }
1341
1342 modref_parm_map chain_map;
1343 if (gimple_call_chain (gs: call))
1344 {
1345 chain_map = parm_map_for_ptr (op: gimple_call_chain (gs: call));
1346 if (dump_file)
1347 {
1348 fprintf (stream: dump_file, format: "static chain %i", chain_map.parm_index);
1349 if (chain_map.parm_offset_known)
1350 {
1351 fprintf (stream: dump_file, format: " offset:");
1352 print_dec (value: (poly_int64)chain_map.parm_offset,
1353 file: dump_file, sgn: SIGNED);
1354 }
1355 }
1356 }
1357 if (dump_file)
1358 fprintf (stream: dump_file, format: "\n");
1359
1360 /* Kills can me merged in only if we know the function is going to be
1361 always executed. */
1362 if (m_always_executed
1363 && callee_summary->kills.length ()
1364 && (!cfun->can_throw_non_call_exceptions
1365 || !stmt_could_throw_p (cfun, call)))
1366 {
1367 /* Watch for self recursive updates. */
1368 auto_vec<modref_access_node, 32> saved_kills;
1369
1370 saved_kills.reserve_exact (nelems: callee_summary->kills.length ());
1371 saved_kills.splice (src: callee_summary->kills);
1372 for (auto kill : saved_kills)
1373 {
1374 if (kill.parm_index >= (int)parm_map.length ())
1375 continue;
1376 modref_parm_map &m
1377 = kill.parm_index == MODREF_STATIC_CHAIN_PARM
1378 ? chain_map
1379 : parm_map[kill.parm_index];
1380 if (m.parm_index == MODREF_LOCAL_MEMORY_PARM
1381 || m.parm_index == MODREF_UNKNOWN_PARM
1382 || m.parm_index == MODREF_RETSLOT_PARM
1383 || !m.parm_offset_known)
1384 continue;
1385 modref_access_node n = kill;
1386 n.parm_index = m.parm_index;
1387 n.parm_offset += m.parm_offset;
1388 if (modref_access_node::insert_kill (kills&: m_summary->kills, a&: n,
1389 record_adjustments))
1390 changed = true;
1391 }
1392 }
1393
1394 /* Merge in loads. */
1395 changed |= m_summary->loads->merge (fndecl: current_function_decl,
1396 other: callee_summary->loads,
1397 parm_map: &parm_map, static_chain_map: &chain_map,
1398 record_accesses: record_adjustments,
1399 promote_unknown_to_global: !may_access_nonescaping_parm_p
1400 (call, callee_ecf_flags: flags, load: true));
1401 /* Merge in stores. */
1402 if (!ignore_stores_p (caller: current_function_decl, flags))
1403 {
1404 changed |= m_summary->stores->merge (fndecl: current_function_decl,
1405 other: callee_summary->stores,
1406 parm_map: &parm_map, static_chain_map: &chain_map,
1407 record_accesses: record_adjustments,
1408 promote_unknown_to_global: !may_access_nonescaping_parm_p
1409 (call, callee_ecf_flags: flags, load: false));
1410 if (!m_summary->writes_errno
1411 && callee_summary->writes_errno)
1412 {
1413 m_summary->writes_errno = true;
1414 changed = true;
1415 }
1416 }
1417 return changed;
1418}
1419
1420/* Return access mode for argument I of call STMT with FNSPEC. */
1421
1422modref_access_node
1423modref_access_analysis::get_access_for_fnspec (gcall *call, attr_fnspec &fnspec,
1424 unsigned int i,
1425 modref_parm_map &map)
1426{
1427 tree size = NULL_TREE;
1428 unsigned int size_arg;
1429
1430 if (!fnspec.arg_specified_p (i))
1431 ;
1432 else if (fnspec.arg_max_access_size_given_by_arg_p (i, arg: &size_arg))
1433 size = gimple_call_arg (gs: call, index: size_arg);
1434 else if (fnspec.arg_access_size_given_by_type_p (i))
1435 {
1436 tree callee = gimple_call_fndecl (gs: call);
1437 tree t = TYPE_ARG_TYPES (TREE_TYPE (callee));
1438
1439 for (unsigned int p = 0; p < i; p++)
1440 t = TREE_CHAIN (t);
1441 size = TYPE_SIZE_UNIT (TREE_TYPE (TREE_VALUE (t)));
1442 }
1443 modref_access_node a = {.offset: 0, .size: -1, .max_size: -1,
1444 .parm_offset: map.parm_offset, .parm_index: map.parm_index,
1445 .parm_offset_known: map.parm_offset_known, .adjustments: 0};
1446 poly_int64 size_hwi;
1447 if (size
1448 && poly_int_tree_p (t: size, value: &size_hwi)
1449 && coeffs_in_range_p (a: size_hwi, b: 0,
1450 HOST_WIDE_INT_MAX / BITS_PER_UNIT))
1451 {
1452 a.size = -1;
1453 a.max_size = size_hwi << LOG2_BITS_PER_UNIT;
1454 }
1455 return a;
1456}
1457/* Apply side effects of call STMT to CUR_SUMMARY using FNSPEC.
1458 If IGNORE_STORES is true ignore them.
1459 Return false if no useful summary can be produced. */
1460
1461void
1462modref_access_analysis::process_fnspec (gcall *call)
1463{
1464 int flags = gimple_call_flags (call);
1465
1466 /* PURE/CONST flags makes functions deterministic and if there is
1467 no LOOPING_CONST_OR_PURE they also have no side effects. */
1468 if (!(flags & (ECF_CONST | ECF_NOVOPS | ECF_PURE))
1469 || (flags & ECF_LOOPING_CONST_OR_PURE)
1470 || (cfun->can_throw_non_call_exceptions
1471 && stmt_could_throw_p (cfun, call)))
1472 {
1473 set_side_effects ();
1474 if (!ignore_nondeterminism_p (caller: current_function_decl, flags))
1475 set_nondeterministic ();
1476 }
1477
1478 /* For const functions we are done. */
1479 if (flags & (ECF_CONST | ECF_NOVOPS))
1480 return;
1481
1482 attr_fnspec fnspec = gimple_call_fnspec (stmt: call);
1483 /* If there is no fnpec we know nothing about loads & stores. */
1484 if (!fnspec.known_p ())
1485 {
1486 if (dump_file && gimple_call_builtin_p (call, BUILT_IN_NORMAL))
1487 fprintf (stream: dump_file, format: " Builtin with no fnspec: %s\n",
1488 IDENTIFIER_POINTER (DECL_NAME (gimple_call_fndecl (call))));
1489 if (!ignore_stores_p (caller: current_function_decl, flags))
1490 {
1491 if (!may_access_nonescaping_parm_p (call, callee_ecf_flags: flags, load: false))
1492 record_global_memory_store ();
1493 else
1494 record_unknown_store ();
1495 if (!may_access_nonescaping_parm_p (call, callee_ecf_flags: flags, load: true))
1496 record_global_memory_load ();
1497 else
1498 record_unknown_load ();
1499 }
1500 else
1501 {
1502 if (!may_access_nonescaping_parm_p (call, callee_ecf_flags: flags, load: true))
1503 record_global_memory_load ();
1504 else
1505 record_unknown_load ();
1506 }
1507 return;
1508 }
1509 /* Process fnspec. */
1510 if (fnspec.global_memory_read_p ())
1511 {
1512 if (may_access_nonescaping_parm_p (call, callee_ecf_flags: flags, load: true))
1513 record_unknown_load ();
1514 else
1515 record_global_memory_load ();
1516 }
1517 else
1518 {
1519 for (unsigned int i = 0; i < gimple_call_num_args (gs: call); i++)
1520 if (!POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (call, i))))
1521 ;
1522 else if (!fnspec.arg_specified_p (i)
1523 || fnspec.arg_maybe_read_p (i))
1524 {
1525 modref_parm_map map = parm_map_for_ptr
1526 (op: gimple_call_arg (gs: call, index: i));
1527
1528 if (map.parm_index == MODREF_LOCAL_MEMORY_PARM)
1529 continue;
1530 if (map.parm_index == MODREF_UNKNOWN_PARM)
1531 {
1532 record_unknown_load ();
1533 break;
1534 }
1535 modref_access_node a = get_access_for_fnspec (call, fnspec, i, map);
1536 if (a.parm_index == MODREF_LOCAL_MEMORY_PARM)
1537 continue;
1538 if (m_summary)
1539 m_summary->loads->insert (fndecl: current_function_decl, base: 0, ref: 0, a, record_adjustments: false);
1540 if (m_summary_lto)
1541 m_summary_lto->loads->insert (fndecl: current_function_decl, base: 0, ref: 0, a,
1542 record_adjustments: false);
1543 }
1544 }
1545 if (ignore_stores_p (caller: current_function_decl, flags))
1546 return;
1547 if (fnspec.global_memory_written_p ())
1548 {
1549 if (may_access_nonescaping_parm_p (call, callee_ecf_flags: flags, load: false))
1550 record_unknown_store ();
1551 else
1552 record_global_memory_store ();
1553 }
1554 else
1555 {
1556 for (unsigned int i = 0; i < gimple_call_num_args (gs: call); i++)
1557 if (!POINTER_TYPE_P (TREE_TYPE (gimple_call_arg (call, i))))
1558 ;
1559 else if (!fnspec.arg_specified_p (i)
1560 || fnspec.arg_maybe_written_p (i))
1561 {
1562 modref_parm_map map = parm_map_for_ptr
1563 (op: gimple_call_arg (gs: call, index: i));
1564
1565 if (map.parm_index == MODREF_LOCAL_MEMORY_PARM)
1566 continue;
1567 if (map.parm_index == MODREF_UNKNOWN_PARM)
1568 {
1569 record_unknown_store ();
1570 break;
1571 }
1572 modref_access_node a = get_access_for_fnspec (call, fnspec, i, map);
1573 if (a.parm_index == MODREF_LOCAL_MEMORY_PARM)
1574 continue;
1575 if (m_summary)
1576 m_summary->stores->insert (fndecl: current_function_decl, base: 0, ref: 0, a, record_adjustments: false);
1577 if (m_summary_lto)
1578 m_summary_lto->stores->insert (fndecl: current_function_decl,
1579 base: 0, ref: 0, a, record_adjustments: false);
1580 }
1581 if (fnspec.errno_maybe_written_p () && flag_errno_math)
1582 {
1583 if (m_summary)
1584 m_summary->writes_errno = true;
1585 if (m_summary_lto)
1586 m_summary_lto->writes_errno = true;
1587 }
1588 }
1589}
1590
1591/* Analyze function call STMT in function F.
1592 Remember recursive calls in RECURSIVE_CALLS. */
1593
1594void
1595modref_access_analysis::analyze_call (gcall *stmt)
1596{
1597 /* Check flags on the function call. In certain cases, analysis can be
1598 simplified. */
1599 int flags = gimple_call_flags (stmt);
1600
1601 if (dump_file)
1602 {
1603 fprintf (stream: dump_file, format: " - Analyzing call:");
1604 print_gimple_stmt (dump_file, stmt, 0);
1605 }
1606
1607 if ((flags & (ECF_CONST | ECF_NOVOPS))
1608 && !(flags & ECF_LOOPING_CONST_OR_PURE))
1609 {
1610 if (dump_file)
1611 fprintf (stream: dump_file,
1612 format: " - ECF_CONST | ECF_NOVOPS, ignoring all stores and all loads "
1613 "except for args.\n");
1614 return;
1615 }
1616
1617 /* Next, we try to get the callee's function declaration. The goal is to
1618 merge their summary with ours. */
1619 tree callee = gimple_call_fndecl (gs: stmt);
1620
1621 /* Check if this is an indirect call. */
1622 if (!callee)
1623 {
1624 if (dump_file)
1625 fprintf (stream: dump_file, format: gimple_call_internal_p (gs: stmt)
1626 ? " - Internal call" : " - Indirect call.\n");
1627 process_fnspec (call: stmt);
1628 return;
1629 }
1630 /* We only need to handle internal calls in IPA mode. */
1631 gcc_checking_assert (!m_summary_lto && !m_ipa);
1632
1633 struct cgraph_node *callee_node = cgraph_node::get_create (callee);
1634
1635 /* If this is a recursive call, the target summary is the same as ours, so
1636 there's nothing to do. */
1637 if (recursive_call_p (current_function_decl, callee))
1638 {
1639 m_recursive_calls.safe_push (obj: stmt);
1640 set_side_effects ();
1641 if (dump_file)
1642 fprintf (stream: dump_file, format: " - Skipping recursive call.\n");
1643 return;
1644 }
1645
1646 gcc_assert (callee_node != NULL);
1647
1648 /* Get the function symbol and its availability. */
1649 enum availability avail;
1650 callee_node = callee_node->function_symbol (avail: &avail);
1651 bool looping;
1652 if (builtin_safe_for_const_function_p (&looping, callee))
1653 {
1654 if (looping)
1655 set_side_effects ();
1656 if (dump_file)
1657 fprintf (stream: dump_file, format: " - Builtin is safe for const.\n");
1658 return;
1659 }
1660 if (avail <= AVAIL_INTERPOSABLE)
1661 {
1662 if (dump_file)
1663 fprintf (stream: dump_file,
1664 format: " - Function availability <= AVAIL_INTERPOSABLE.\n");
1665 process_fnspec (call: stmt);
1666 return;
1667 }
1668
1669 /* Get callee's modref summary. As above, if there's no summary, we either
1670 have to give up or, if stores are ignored, we can just purge loads. */
1671 modref_summary *callee_summary = optimization_summaries->get (node: callee_node);
1672 if (!callee_summary)
1673 {
1674 if (dump_file)
1675 fprintf (stream: dump_file, format: " - No modref summary available for callee.\n");
1676 process_fnspec (call: stmt);
1677 return;
1678 }
1679
1680 merge_call_side_effects (stmt, callee_summary, callee_node, record_adjustments: false);
1681
1682 return;
1683}
1684
1685/* Helper for analyze_stmt. */
1686
1687bool
1688modref_access_analysis::analyze_load (gimple *, tree, tree op, void *data)
1689{
1690 modref_access_analysis *t = (modref_access_analysis *)data;
1691
1692 if (dump_file)
1693 {
1694 fprintf (stream: dump_file, format: " - Analyzing load: ");
1695 print_generic_expr (dump_file, op);
1696 fprintf (stream: dump_file, format: "\n");
1697 }
1698
1699 if (!t->record_access_p (expr: op))
1700 return false;
1701
1702 ao_ref r;
1703 ao_ref_init (&r, op);
1704 modref_access_node a = get_access (ref: &r);
1705 if (a.parm_index == MODREF_LOCAL_MEMORY_PARM)
1706 return false;
1707
1708 if (t->m_summary)
1709 t->record_access (tt: t->m_summary->loads, ref: &r, a);
1710 if (t->m_summary_lto)
1711 t->record_access_lto (tt: t->m_summary_lto->loads, ref: &r, a);
1712 return false;
1713}
1714
1715/* Helper for analyze_stmt. */
1716
1717bool
1718modref_access_analysis::analyze_store (gimple *stmt, tree, tree op, void *data)
1719{
1720 modref_access_analysis *t = (modref_access_analysis *)data;
1721
1722 if (dump_file)
1723 {
1724 fprintf (stream: dump_file, format: " - Analyzing store: ");
1725 print_generic_expr (dump_file, op);
1726 fprintf (stream: dump_file, format: "\n");
1727 }
1728
1729 if (!t->record_access_p (expr: op))
1730 return false;
1731
1732 ao_ref r;
1733 ao_ref_init (&r, op);
1734 modref_access_node a = get_access (ref: &r);
1735 if (a.parm_index == MODREF_LOCAL_MEMORY_PARM)
1736 return false;
1737
1738 if (t->m_summary)
1739 t->record_access (tt: t->m_summary->stores, ref: &r, a);
1740 if (t->m_summary_lto)
1741 t->record_access_lto (tt: t->m_summary_lto->stores, ref: &r, a);
1742 if (t->m_always_executed
1743 && a.useful_for_kill_p ()
1744 && (!cfun->can_throw_non_call_exceptions
1745 || !stmt_could_throw_p (cfun, stmt)))
1746 {
1747 if (dump_file)
1748 fprintf (stream: dump_file, format: " - Recording kill\n");
1749 if (t->m_summary)
1750 modref_access_node::insert_kill (kills&: t->m_summary->kills, a, record_adjustments: false);
1751 if (t->m_summary_lto)
1752 modref_access_node::insert_kill (kills&: t->m_summary_lto->kills, a, record_adjustments: false);
1753 }
1754 return false;
1755}
1756
1757/* Analyze statement STMT of function F.
1758 If IPA is true do not merge in side effects of calls. */
1759
1760void
1761modref_access_analysis::analyze_stmt (gimple *stmt, bool always_executed)
1762{
1763 m_always_executed = always_executed;
1764 /* In general we can not ignore clobbers because they are barriers for code
1765 motion, however after inlining it is safe to do because local optimization
1766 passes do not consider clobbers from other functions.
1767 Similar logic is in ipa-pure-const.cc. */
1768 if ((m_ipa || cfun->after_inlining) && gimple_clobber_p (s: stmt))
1769 {
1770 if (always_executed && record_access_p (expr: gimple_assign_lhs (gs: stmt)))
1771 {
1772 ao_ref r;
1773 ao_ref_init (&r, gimple_assign_lhs (gs: stmt));
1774 modref_access_node a = get_access (ref: &r);
1775 if (a.useful_for_kill_p ())
1776 {
1777 if (dump_file)
1778 fprintf (stream: dump_file, format: " - Recording kill\n");
1779 if (m_summary)
1780 modref_access_node::insert_kill (kills&: m_summary->kills, a, record_adjustments: false);
1781 if (m_summary_lto)
1782 modref_access_node::insert_kill (kills&: m_summary_lto->kills,
1783 a, record_adjustments: false);
1784 }
1785 }
1786 return;
1787 }
1788
1789 /* Analyze all loads and stores in STMT. */
1790 walk_stmt_load_store_ops (stmt, this,
1791 analyze_load, analyze_store);
1792
1793 switch (gimple_code (g: stmt))
1794 {
1795 case GIMPLE_ASM:
1796 if (gimple_asm_volatile_p (asm_stmt: as_a <gasm *> (p: stmt)))
1797 set_nondeterministic ();
1798 if (cfun->can_throw_non_call_exceptions
1799 && stmt_could_throw_p (cfun, stmt))
1800 set_side_effects ();
1801 /* If the ASM statement does not read nor write memory, there's nothing
1802 to do. Otherwise just give up. */
1803 if (!gimple_asm_clobbers_memory_p (as_a <gasm *> (p: stmt)))
1804 return;
1805 if (dump_file)
1806 fprintf (stream: dump_file, format: " - Function contains GIMPLE_ASM statement "
1807 "which clobbers memory.\n");
1808 record_unknown_load ();
1809 record_unknown_store ();
1810 return;
1811 case GIMPLE_CALL:
1812 if (!m_ipa || gimple_call_internal_p (gs: stmt))
1813 analyze_call (stmt: as_a <gcall *> (p: stmt));
1814 else
1815 {
1816 attr_fnspec fnspec = gimple_call_fnspec (stmt: as_a <gcall *>(p: stmt));
1817
1818 if (fnspec.known_p ()
1819 && (!fnspec.global_memory_read_p ()
1820 || !fnspec.global_memory_written_p ()))
1821 {
1822 cgraph_edge *e = cgraph_node::get
1823 (decl: current_function_decl)->get_edge (call_stmt: stmt);
1824 if (e->callee)
1825 {
1826 fnspec_summaries->get_create (edge: e)->fnspec
1827 = xstrdup (fnspec.get_str ());
1828 if (dump_file)
1829 fprintf (stream: dump_file, format: " Recorded fnspec %s\n",
1830 fnspec.get_str ());
1831 }
1832 }
1833 }
1834 return;
1835 default:
1836 if (cfun->can_throw_non_call_exceptions
1837 && stmt_could_throw_p (cfun, stmt))
1838 set_side_effects ();
1839 return;
1840 }
1841}
1842
1843/* Propagate load/stores across recursive calls. */
1844
1845void
1846modref_access_analysis::propagate ()
1847{
1848 if (m_ipa && m_summary)
1849 return;
1850
1851 bool changed = true;
1852 bool first = true;
1853 cgraph_node *fnode = cgraph_node::get (decl: current_function_decl);
1854
1855 m_always_executed = false;
1856 while (changed && m_summary->useful_p (ecf_flags: m_ecf_flags, check_flags: false))
1857 {
1858 changed = false;
1859 for (unsigned i = 0; i < m_recursive_calls.length (); i++)
1860 {
1861 changed |= merge_call_side_effects (stmt: m_recursive_calls[i], callee_summary: m_summary,
1862 callee_node: fnode, record_adjustments: !first);
1863 }
1864 first = false;
1865 }
1866}
1867
1868/* Analyze function. */
1869
1870void
1871modref_access_analysis::analyze ()
1872{
1873 m_ecf_flags = flags_from_decl_or_type (current_function_decl);
1874 bool summary_useful = true;
1875
1876 /* Analyze each statement in each basic block of the function. If the
1877 statement cannot be analyzed (for any reason), the entire function cannot
1878 be analyzed by modref. */
1879 basic_block bb;
1880 bitmap always_executed_bbs = find_always_executed_bbs (cfun, assume_return_or_eh: true);
1881 FOR_EACH_BB_FN (bb, cfun)
1882 {
1883 gimple_stmt_iterator si;
1884 bool always_executed = bitmap_bit_p (always_executed_bbs, bb->index);
1885
1886 for (si = gsi_start_nondebug_after_labels_bb (bb);
1887 !gsi_end_p (i: si); gsi_next_nondebug (i: &si))
1888 {
1889 /* NULL memory accesses terminates BB. These accesses are known
1890 to trip undefined behavior. gimple-ssa-isolate-paths turns them
1891 to volatile accesses and adds builtin_trap call which would
1892 confuse us otherwise. */
1893 if (infer_nonnull_range_by_dereference (gsi_stmt (i: si),
1894 null_pointer_node))
1895 {
1896 if (dump_file)
1897 fprintf (stream: dump_file, format: " - NULL memory access; terminating BB\n");
1898 if (flag_non_call_exceptions)
1899 set_side_effects ();
1900 break;
1901 }
1902 analyze_stmt (stmt: gsi_stmt (i: si), always_executed);
1903
1904 /* Avoid doing useless work. */
1905 if ((!m_summary || !m_summary->useful_p (ecf_flags: m_ecf_flags, check_flags: false))
1906 && (!m_summary_lto
1907 || !m_summary_lto->useful_p (ecf_flags: m_ecf_flags, check_flags: false)))
1908 {
1909 summary_useful = false;
1910 break;
1911 }
1912 if (always_executed
1913 && stmt_can_throw_external (cfun, gsi_stmt (i: si)))
1914 always_executed = false;
1915 }
1916 if (!summary_useful)
1917 break;
1918 }
1919 /* In non-IPA mode we need to perform iterative dataflow on recursive calls.
1920 This needs to be done after all other side effects are computed. */
1921 if (summary_useful)
1922 {
1923 if (!m_ipa)
1924 propagate ();
1925 if (m_summary && !m_summary->side_effects && !finite_function_p ())
1926 m_summary->side_effects = true;
1927 if (m_summary_lto && !m_summary_lto->side_effects
1928 && !finite_function_p ())
1929 m_summary_lto->side_effects = true;
1930 }
1931 BITMAP_FREE (always_executed_bbs);
1932}
1933
1934/* Return true if OP accesses memory pointed to by SSA_NAME. */
1935
1936bool
1937memory_access_to (tree op, tree ssa_name)
1938{
1939 tree base = get_base_address (t: op);
1940 if (!base)
1941 return false;
1942 if (TREE_CODE (base) != MEM_REF && TREE_CODE (base) != TARGET_MEM_REF)
1943 return false;
1944 return TREE_OPERAND (base, 0) == ssa_name;
1945}
1946
1947/* Consider statement val = *arg.
1948 return EAF flags of ARG that can be determined from EAF flags of VAL
1949 (which are known to be FLAGS). If IGNORE_STORES is true we can ignore
1950 all stores to VAL, i.e. when handling noreturn function. */
1951
1952static int
1953deref_flags (int flags, bool ignore_stores)
1954{
1955 /* Dereference is also a direct read but dereferenced value does not
1956 yield any other direct use. */
1957 int ret = EAF_NO_DIRECT_CLOBBER | EAF_NO_DIRECT_ESCAPE
1958 | EAF_NOT_RETURNED_DIRECTLY;
1959 /* If argument is unused just account for
1960 the read involved in dereference. */
1961 if (flags & EAF_UNUSED)
1962 ret |= EAF_NO_INDIRECT_READ | EAF_NO_INDIRECT_CLOBBER
1963 | EAF_NO_INDIRECT_ESCAPE;
1964 else
1965 {
1966 /* Direct or indirect accesses leads to indirect accesses. */
1967 if (((flags & EAF_NO_DIRECT_CLOBBER)
1968 && (flags & EAF_NO_INDIRECT_CLOBBER))
1969 || ignore_stores)
1970 ret |= EAF_NO_INDIRECT_CLOBBER;
1971 if (((flags & EAF_NO_DIRECT_ESCAPE)
1972 && (flags & EAF_NO_INDIRECT_ESCAPE))
1973 || ignore_stores)
1974 ret |= EAF_NO_INDIRECT_ESCAPE;
1975 if ((flags & EAF_NO_DIRECT_READ)
1976 && (flags & EAF_NO_INDIRECT_READ))
1977 ret |= EAF_NO_INDIRECT_READ;
1978 if ((flags & EAF_NOT_RETURNED_DIRECTLY)
1979 && (flags & EAF_NOT_RETURNED_INDIRECTLY))
1980 ret |= EAF_NOT_RETURNED_INDIRECTLY;
1981 }
1982 return ret;
1983}
1984
1985
1986/* Description of an escape point: a call which affects flags of a given
1987 SSA name. */
1988
1989struct escape_point
1990{
1991 /* Value escapes to this call. */
1992 gcall *call;
1993 /* Argument it escapes to. */
1994 int arg;
1995 /* Flags already known about the argument (this can save us from recording
1996 escape points if local analysis did good job already). */
1997 eaf_flags_t min_flags;
1998 /* Does value escape directly or indirectly? */
1999 bool direct;
2000};
2001
2002/* Lattice used during the eaf flags analysis dataflow. For a given SSA name
2003 we aim to compute its flags and escape points. We also use the lattice
2004 to dynamically build dataflow graph to propagate on. */
2005
2006class modref_lattice
2007{
2008public:
2009 /* EAF flags of the SSA name. */
2010 eaf_flags_t flags;
2011 /* Used during DFS walk to mark names where final value was determined
2012 without need for dataflow. */
2013 bool known;
2014 /* Used during DFS walk to mark open vertices (for cycle detection). */
2015 bool open;
2016 /* Set during DFS walk for names that needs dataflow propagation. */
2017 bool do_dataflow;
2018 /* Used during the iterative dataflow. */
2019 bool changed;
2020
2021 /* When doing IPA analysis we can not merge in callee escape points;
2022 Only remember them and do the merging at IPA propagation time. */
2023 vec <escape_point, va_heap, vl_ptr> escape_points;
2024
2025 /* Representation of a graph for dataflow. This graph is built on-demand
2026 using modref_eaf_analysis::analyze_ssa and later solved by
2027 modref_eaf_analysis::propagate.
2028 Each edge represents the fact that flags of current lattice should be
2029 propagated to lattice of SSA_NAME. */
2030 struct propagate_edge
2031 {
2032 int ssa_name;
2033 bool deref;
2034 };
2035 vec <propagate_edge, va_heap, vl_ptr> propagate_to;
2036
2037 void init ();
2038 void release ();
2039 bool merge (const modref_lattice &with);
2040 bool merge (int flags);
2041 bool merge_deref (const modref_lattice &with, bool ignore_stores);
2042 bool merge_direct_load ();
2043 bool merge_direct_store ();
2044 bool add_escape_point (gcall *call, int arg, int min_flags, bool diret);
2045 void dump (FILE *out, int indent = 0) const;
2046};
2047
2048/* Lattices are saved to vectors, so keep them PODs. */
2049void
2050modref_lattice::init ()
2051{
2052 /* All flags we track. */
2053 int f = EAF_NO_DIRECT_CLOBBER | EAF_NO_INDIRECT_CLOBBER
2054 | EAF_NO_DIRECT_ESCAPE | EAF_NO_INDIRECT_ESCAPE
2055 | EAF_NO_DIRECT_READ | EAF_NO_INDIRECT_READ
2056 | EAF_NOT_RETURNED_DIRECTLY | EAF_NOT_RETURNED_INDIRECTLY
2057 | EAF_UNUSED;
2058 flags = f;
2059 /* Check that eaf_flags_t is wide enough to hold all flags. */
2060 gcc_checking_assert (f == flags);
2061 open = true;
2062 known = false;
2063}
2064
2065/* Release memory. */
2066void
2067modref_lattice::release ()
2068{
2069 escape_points.release ();
2070 propagate_to.release ();
2071}
2072
2073/* Dump lattice to OUT; indent with INDENT spaces. */
2074
2075void
2076modref_lattice::dump (FILE *out, int indent) const
2077{
2078 dump_eaf_flags (out, flags);
2079 if (escape_points.length ())
2080 {
2081 fprintf (stream: out, format: "%*sEscapes:\n", indent, "");
2082 for (unsigned int i = 0; i < escape_points.length (); i++)
2083 {
2084 fprintf (stream: out, format: "%*s Arg %i (%s) min flags", indent, "",
2085 escape_points[i].arg,
2086 escape_points[i].direct ? "direct" : "indirect");
2087 dump_eaf_flags (out, flags: escape_points[i].min_flags, newline: false);
2088 fprintf (stream: out, format: " in call ");
2089 print_gimple_stmt (out, escape_points[i].call, 0);
2090 }
2091 }
2092}
2093
2094/* Add escape point CALL, ARG, MIN_FLAGS, DIRECT. Return false if such escape
2095 point exists. */
2096
2097bool
2098modref_lattice::add_escape_point (gcall *call, int arg, int min_flags,
2099 bool direct)
2100{
2101 escape_point *ep;
2102 unsigned int i;
2103
2104 /* If we already determined flags to be bad enough,
2105 we do not need to record. */
2106 if ((flags & min_flags) == flags || (min_flags & EAF_UNUSED))
2107 return false;
2108
2109 FOR_EACH_VEC_ELT (escape_points, i, ep)
2110 if (ep->call == call && ep->arg == arg && ep->direct == direct)
2111 {
2112 if ((ep->min_flags & min_flags) == min_flags)
2113 return false;
2114 ep->min_flags &= min_flags;
2115 return true;
2116 }
2117 /* Give up if max escape points is met. */
2118 if ((int)escape_points.length () > param_modref_max_escape_points)
2119 {
2120 if (dump_file)
2121 fprintf (stream: dump_file, format: "--param modref-max-escape-points limit reached\n");
2122 merge (flags: 0);
2123 return true;
2124 }
2125 escape_point new_ep = {.call: call, .arg: arg, .min_flags: min_flags, .direct: direct};
2126 escape_points.safe_push (obj: new_ep);
2127 return true;
2128}
2129
2130/* Merge in flags from F. */
2131bool
2132modref_lattice::merge (int f)
2133{
2134 if (f & EAF_UNUSED)
2135 return false;
2136 /* Check that flags seems sane: if function does not read the parameter
2137 it can not access it indirectly. */
2138 gcc_checking_assert (!(f & EAF_NO_DIRECT_READ)
2139 || ((f & EAF_NO_INDIRECT_READ)
2140 && (f & EAF_NO_INDIRECT_CLOBBER)
2141 && (f & EAF_NO_INDIRECT_ESCAPE)
2142 && (f & EAF_NOT_RETURNED_INDIRECTLY)));
2143 if ((flags & f) != flags)
2144 {
2145 flags &= f;
2146 /* Prune obviously useless flags;
2147 We do not have ECF_FLAGS handy which is not big problem since
2148 we will do final flags cleanup before producing summary.
2149 Merging should be fast so it can work well with dataflow. */
2150 flags = remove_useless_eaf_flags (eaf_flags: flags, ecf_flags: 0, returns_void: false);
2151 if (!flags)
2152 escape_points.release ();
2153 return true;
2154 }
2155 return false;
2156}
2157
2158/* Merge in WITH. Return true if anything changed. */
2159
2160bool
2161modref_lattice::merge (const modref_lattice &with)
2162{
2163 if (!with.known)
2164 do_dataflow = true;
2165
2166 bool changed = merge (f: with.flags);
2167
2168 if (!flags)
2169 return changed;
2170 for (unsigned int i = 0; i < with.escape_points.length (); i++)
2171 changed |= add_escape_point (call: with.escape_points[i].call,
2172 arg: with.escape_points[i].arg,
2173 min_flags: with.escape_points[i].min_flags,
2174 direct: with.escape_points[i].direct);
2175 return changed;
2176}
2177
2178/* Merge in deref of WITH. If IGNORE_STORES is true do not consider
2179 stores. Return true if anything changed. */
2180
2181bool
2182modref_lattice::merge_deref (const modref_lattice &with, bool ignore_stores)
2183{
2184 if (!with.known)
2185 do_dataflow = true;
2186
2187 bool changed = merge (f: deref_flags (flags: with.flags, ignore_stores));
2188
2189 if (!flags)
2190 return changed;
2191 for (unsigned int i = 0; i < with.escape_points.length (); i++)
2192 {
2193 int min_flags = with.escape_points[i].min_flags;
2194
2195 if (with.escape_points[i].direct)
2196 min_flags = deref_flags (flags: min_flags, ignore_stores);
2197 else if (ignore_stores)
2198 min_flags |= ignore_stores_eaf_flags;
2199 changed |= add_escape_point (call: with.escape_points[i].call,
2200 arg: with.escape_points[i].arg,
2201 min_flags,
2202 direct: false);
2203 }
2204 return changed;
2205}
2206
2207/* Merge in flags for direct load. */
2208
2209bool
2210modref_lattice::merge_direct_load ()
2211{
2212 return merge (f: ~(EAF_UNUSED | EAF_NO_DIRECT_READ));
2213}
2214
2215/* Merge in flags for direct store. */
2216
2217bool
2218modref_lattice::merge_direct_store ()
2219{
2220 return merge (f: ~(EAF_UNUSED | EAF_NO_DIRECT_CLOBBER));
2221}
2222
2223/* Analyzer of EAF flags.
2224 This is generally dataflow problem over the SSA graph, however we only
2225 care about flags of few selected ssa names (arguments, return slot and
2226 static chain). So we first call analyze_ssa_name on all relevant names
2227 and perform a DFS walk to discover SSA names where flags needs to be
2228 determined. For acyclic graphs we try to determine final flags during
2229 this walk. Once cycles or recursion depth is met we enlist SSA names
2230 for dataflow which is done by propagate call.
2231
2232 After propagation the flags can be obtained using get_ssa_name_flags. */
2233
2234class modref_eaf_analysis
2235{
2236public:
2237 /* Mark NAME as relevant for analysis. */
2238 void analyze_ssa_name (tree name, bool deferred = false);
2239 /* Dataflow solver. */
2240 void propagate ();
2241 /* Return flags computed earlier for NAME. */
2242 int get_ssa_name_flags (tree name)
2243 {
2244 int version = SSA_NAME_VERSION (name);
2245 gcc_checking_assert (m_lattice[version].known);
2246 return m_lattice[version].flags;
2247 }
2248 /* In IPA mode this will record all escape points
2249 determined for NAME to PARM_IDNEX. Flags are minimal
2250 flags known. */
2251 void record_escape_points (tree name, int parm_index, int flags);
2252 modref_eaf_analysis (bool ipa)
2253 {
2254 m_ipa = ipa;
2255 m_depth = 0;
2256 m_lattice.safe_grow_cleared (num_ssa_names, exact: true);
2257 }
2258 ~modref_eaf_analysis ()
2259 {
2260 gcc_checking_assert (!m_depth);
2261 if (m_ipa || m_names_to_propagate.length ())
2262 for (unsigned int i = 0; i < num_ssa_names; i++)
2263 m_lattice[i].release ();
2264 }
2265private:
2266 /* If true, we produce analysis for IPA mode. In this case escape points are
2267 collected. */
2268 bool m_ipa;
2269 /* Depth of recursion of analyze_ssa_name. */
2270 int m_depth;
2271 /* Propagation lattice for individual ssa names. */
2272 auto_vec<modref_lattice> m_lattice;
2273 auto_vec<tree> m_deferred_names;
2274 auto_vec<int> m_names_to_propagate;
2275
2276 void merge_with_ssa_name (tree dest, tree src, bool deref);
2277 void merge_call_lhs_flags (gcall *call, int arg, tree name, bool direct,
2278 bool deref);
2279};
2280
2281
2282/* Call statements may return their parameters. Consider argument number
2283 ARG of USE_STMT and determine flags that can needs to be cleared
2284 in case pointer possibly indirectly references from ARG I is returned.
2285 If DIRECT is true consider direct returns and if INDIRECT consider
2286 indirect returns.
2287 LATTICE, DEPTH and ipa are same as in analyze_ssa_name.
2288 ARG is set to -1 for static chain. */
2289
2290void
2291modref_eaf_analysis::merge_call_lhs_flags (gcall *call, int arg,
2292 tree name, bool direct,
2293 bool indirect)
2294{
2295 int index = SSA_NAME_VERSION (name);
2296 bool returned_directly = false;
2297
2298 /* If there is no return value, no flags are affected. */
2299 if (!gimple_call_lhs (gs: call))
2300 return;
2301
2302 /* If we know that function returns given argument and it is not ARG
2303 we can still be happy. */
2304 if (arg >= 0)
2305 {
2306 int flags = gimple_call_return_flags (call);
2307 if (flags & ERF_RETURNS_ARG)
2308 {
2309 if ((flags & ERF_RETURN_ARG_MASK) == arg)
2310 returned_directly = true;
2311 else
2312 return;
2313 }
2314 }
2315 /* Make ERF_RETURNS_ARG overwrite EAF_UNUSED. */
2316 if (returned_directly)
2317 {
2318 direct = true;
2319 indirect = false;
2320 }
2321 /* If value is not returned at all, do nothing. */
2322 else if (!direct && !indirect)
2323 return;
2324
2325 /* If return value is SSA name determine its flags. */
2326 if (TREE_CODE (gimple_call_lhs (call)) == SSA_NAME)
2327 {
2328 tree lhs = gimple_call_lhs (gs: call);
2329 if (direct)
2330 merge_with_ssa_name (dest: name, src: lhs, deref: false);
2331 if (indirect)
2332 merge_with_ssa_name (dest: name, src: lhs, deref: true);
2333 }
2334 /* In the case of memory store we can do nothing. */
2335 else if (!direct)
2336 m_lattice[index].merge (f: deref_flags (flags: 0, ignore_stores: false));
2337 else
2338 m_lattice[index].merge (f: 0);
2339}
2340
2341/* CALL_FLAGS are EAF_FLAGS of the argument. Turn them
2342 into flags for caller, update LATTICE of corresponding
2343 argument if needed. */
2344
2345static int
2346callee_to_caller_flags (int call_flags, bool ignore_stores,
2347 modref_lattice &lattice)
2348{
2349 /* call_flags is about callee returning a value
2350 that is not the same as caller returning it. */
2351 call_flags |= EAF_NOT_RETURNED_DIRECTLY
2352 | EAF_NOT_RETURNED_INDIRECTLY;
2353 if (!ignore_stores && !(call_flags & EAF_UNUSED))
2354 {
2355 /* If value escapes we are no longer able to track what happens
2356 with it because we can read it from the escaped location
2357 anytime. */
2358 if (!(call_flags & EAF_NO_DIRECT_ESCAPE))
2359 lattice.merge (f: 0);
2360 else if (!(call_flags & EAF_NO_INDIRECT_ESCAPE))
2361 lattice.merge (f: ~(EAF_NOT_RETURNED_INDIRECTLY
2362 | EAF_NO_DIRECT_READ
2363 | EAF_NO_INDIRECT_READ
2364 | EAF_NO_INDIRECT_CLOBBER
2365 | EAF_UNUSED));
2366 }
2367 else
2368 call_flags |= ignore_stores_eaf_flags;
2369 return call_flags;
2370}
2371
2372/* Analyze EAF flags for SSA name NAME and store result to LATTICE.
2373 LATTICE is an array of modref_lattices.
2374 DEPTH is a recursion depth used to make debug output prettier.
2375 If IPA is true we analyze for IPA propagation (and thus call escape points
2376 are processed later) */
2377
2378void
2379modref_eaf_analysis::analyze_ssa_name (tree name, bool deferred)
2380{
2381 imm_use_iterator ui;
2382 gimple *use_stmt;
2383 int index = SSA_NAME_VERSION (name);
2384
2385 if (!deferred)
2386 {
2387 /* See if value is already computed. */
2388 if (m_lattice[index].known || m_lattice[index].do_dataflow)
2389 return;
2390 if (m_lattice[index].open)
2391 {
2392 if (dump_file)
2393 fprintf (stream: dump_file,
2394 format: "%*sCycle in SSA graph\n",
2395 m_depth * 4, "");
2396 return;
2397 }
2398 /* Recursion guard. */
2399 m_lattice[index].init ();
2400 if (m_depth == param_modref_max_depth)
2401 {
2402 if (dump_file)
2403 fprintf (stream: dump_file,
2404 format: "%*sMax recursion depth reached; postponing\n",
2405 m_depth * 4, "");
2406 m_deferred_names.safe_push (obj: name);
2407 return;
2408 }
2409 }
2410
2411 if (dump_file)
2412 {
2413 fprintf (stream: dump_file,
2414 format: "%*sAnalyzing flags of ssa name: ", m_depth * 4, "");
2415 print_generic_expr (dump_file, name);
2416 fprintf (stream: dump_file, format: "\n");
2417 }
2418
2419 FOR_EACH_IMM_USE_STMT (use_stmt, ui, name)
2420 {
2421 if (m_lattice[index].flags == 0)
2422 break;
2423 if (is_gimple_debug (gs: use_stmt))
2424 continue;
2425 if (dump_file)
2426 {
2427 fprintf (stream: dump_file, format: "%*s Analyzing stmt: ", m_depth * 4, "");
2428 print_gimple_stmt (dump_file, use_stmt, 0);
2429 }
2430 /* If we see a direct non-debug use, clear unused bit.
2431 All dereferences should be accounted below using deref_flags. */
2432 m_lattice[index].merge (f: ~EAF_UNUSED);
2433
2434 /* Gimple return may load the return value.
2435 Returning name counts as an use by tree-ssa-structalias.cc */
2436 if (greturn *ret = dyn_cast <greturn *> (p: use_stmt))
2437 {
2438 /* Returning through return slot is seen as memory write earlier. */
2439 if (DECL_RESULT (current_function_decl)
2440 && DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
2441 ;
2442 else if (gimple_return_retval (gs: ret) == name)
2443 m_lattice[index].merge (f: ~(EAF_UNUSED | EAF_NOT_RETURNED_DIRECTLY
2444 | EAF_NOT_RETURNED_DIRECTLY));
2445 else if (memory_access_to (op: gimple_return_retval (gs: ret), ssa_name: name))
2446 {
2447 m_lattice[index].merge_direct_load ();
2448 m_lattice[index].merge (f: ~(EAF_UNUSED
2449 | EAF_NOT_RETURNED_INDIRECTLY));
2450 }
2451 }
2452 /* Account for LHS store, arg loads and flags from callee function. */
2453 else if (gcall *call = dyn_cast <gcall *> (p: use_stmt))
2454 {
2455 tree callee = gimple_call_fndecl (gs: call);
2456
2457 /* IPA PTA internally it treats calling a function as "writing" to
2458 the argument space of all functions the function pointer points to
2459 (PR101949). We can not drop EAF_NOCLOBBER only when ipa-pta
2460 is on since that would allow propagation of this from -fno-ipa-pta
2461 to -fipa-pta functions. */
2462 if (gimple_call_fn (gs: use_stmt) == name)
2463 m_lattice[index].merge (f: ~(EAF_NO_DIRECT_CLOBBER | EAF_UNUSED));
2464
2465 /* Recursion would require bit of propagation; give up for now. */
2466 if (callee && !m_ipa && recursive_call_p (current_function_decl,
2467 callee))
2468 m_lattice[index].merge (f: 0);
2469 else
2470 {
2471 int ecf_flags = gimple_call_flags (call);
2472 bool ignore_stores = ignore_stores_p (caller: current_function_decl,
2473 flags: ecf_flags);
2474 bool ignore_retval = ignore_retval_p (caller: current_function_decl,
2475 flags: ecf_flags);
2476
2477 /* Handle *name = func (...). */
2478 if (gimple_call_lhs (gs: call)
2479 && memory_access_to (op: gimple_call_lhs (gs: call), ssa_name: name))
2480 {
2481 m_lattice[index].merge_direct_store ();
2482 /* Return slot optimization passes address of
2483 LHS to callee via hidden parameter and this
2484 may make LHS to escape. See PR 98499. */
2485 if (gimple_call_return_slot_opt_p (s: call)
2486 && TREE_ADDRESSABLE (TREE_TYPE (gimple_call_lhs (call))))
2487 {
2488 int call_flags = gimple_call_retslot_flags (call);
2489 bool isretslot = false;
2490
2491 if (DECL_RESULT (current_function_decl)
2492 && DECL_BY_REFERENCE
2493 (DECL_RESULT (current_function_decl)))
2494 isretslot = ssa_default_def
2495 (cfun,
2496 DECL_RESULT (current_function_decl))
2497 == name;
2498
2499 /* Passing returnslot to return slot is special because
2500 not_returned and escape has same meaning.
2501 However passing arg to return slot is different. If
2502 the callee's return slot is returned it means that
2503 arg is written to itself which is an escape.
2504 Since we do not track the memory it is written to we
2505 need to give up on analyzing it. */
2506 if (!isretslot)
2507 {
2508 if (!(call_flags & (EAF_NOT_RETURNED_DIRECTLY
2509 | EAF_UNUSED)))
2510 m_lattice[index].merge (f: 0);
2511 else gcc_checking_assert
2512 (call_flags & (EAF_NOT_RETURNED_INDIRECTLY
2513 | EAF_UNUSED));
2514 call_flags = callee_to_caller_flags
2515 (call_flags, ignore_stores: false,
2516 lattice&: m_lattice[index]);
2517 }
2518 m_lattice[index].merge (f: call_flags);
2519 }
2520 }
2521
2522 if (gimple_call_chain (gs: call)
2523 && (gimple_call_chain (gs: call) == name))
2524 {
2525 int call_flags = gimple_call_static_chain_flags (call);
2526 if (!ignore_retval && !(call_flags & EAF_UNUSED))
2527 merge_call_lhs_flags
2528 (call, arg: -1, name,
2529 direct: !(call_flags & EAF_NOT_RETURNED_DIRECTLY),
2530 indirect: !(call_flags & EAF_NOT_RETURNED_INDIRECTLY));
2531 call_flags = callee_to_caller_flags
2532 (call_flags, ignore_stores,
2533 lattice&: m_lattice[index]);
2534 if (!(ecf_flags & (ECF_CONST | ECF_NOVOPS)))
2535 m_lattice[index].merge (f: call_flags);
2536 }
2537
2538 /* Process internal functions and right away. */
2539 bool record_ipa = m_ipa && !gimple_call_internal_p (gs: call);
2540
2541 /* Handle all function parameters. */
2542 for (unsigned i = 0;
2543 i < gimple_call_num_args (gs: call)
2544 && m_lattice[index].flags; i++)
2545 /* Name is directly passed to the callee. */
2546 if (gimple_call_arg (gs: call, index: i) == name)
2547 {
2548 int call_flags = gimple_call_arg_flags (call, i);
2549 if (!ignore_retval)
2550 merge_call_lhs_flags
2551 (call, arg: i, name,
2552 direct: !(call_flags & (EAF_NOT_RETURNED_DIRECTLY
2553 | EAF_UNUSED)),
2554 indirect: !(call_flags & (EAF_NOT_RETURNED_INDIRECTLY
2555 | EAF_UNUSED)));
2556 if (!(ecf_flags & (ECF_CONST | ECF_NOVOPS)))
2557 {
2558 call_flags = callee_to_caller_flags
2559 (call_flags, ignore_stores,
2560 lattice&: m_lattice[index]);
2561 if (!record_ipa)
2562 m_lattice[index].merge (f: call_flags);
2563 else
2564 m_lattice[index].add_escape_point (call, arg: i,
2565 min_flags: call_flags, direct: true);
2566 }
2567 }
2568 /* Name is dereferenced and passed to a callee. */
2569 else if (memory_access_to (op: gimple_call_arg (gs: call, index: i), ssa_name: name))
2570 {
2571 int call_flags = deref_flags
2572 (flags: gimple_call_arg_flags (call, i), ignore_stores);
2573 if (!ignore_retval && !(call_flags & EAF_UNUSED)
2574 && !(call_flags & EAF_NOT_RETURNED_DIRECTLY)
2575 && !(call_flags & EAF_NOT_RETURNED_INDIRECTLY))
2576 merge_call_lhs_flags (call, arg: i, name, direct: false, indirect: true);
2577 if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
2578 m_lattice[index].merge_direct_load ();
2579 else
2580 {
2581 call_flags = callee_to_caller_flags
2582 (call_flags, ignore_stores,
2583 lattice&: m_lattice[index]);
2584 if (!record_ipa)
2585 m_lattice[index].merge (f: call_flags);
2586 else
2587 m_lattice[index].add_escape_point (call, arg: i,
2588 min_flags: call_flags, direct: false);
2589 }
2590 }
2591 }
2592 }
2593 else if (gimple_assign_load_p (use_stmt))
2594 {
2595 gassign *assign = as_a <gassign *> (p: use_stmt);
2596 /* Memory to memory copy. */
2597 if (gimple_store_p (gs: assign))
2598 {
2599 /* Handle *lhs = *name.
2600
2601 We do not track memory locations, so assume that value
2602 is used arbitrarily. */
2603 if (memory_access_to (op: gimple_assign_rhs1 (gs: assign), ssa_name: name))
2604 m_lattice[index].merge (f: deref_flags (flags: 0, ignore_stores: false));
2605 /* Handle *name = *exp. */
2606 else if (memory_access_to (op: gimple_assign_lhs (gs: assign), ssa_name: name))
2607 m_lattice[index].merge_direct_store ();
2608 }
2609 /* Handle lhs = *name. */
2610 else if (memory_access_to (op: gimple_assign_rhs1 (gs: assign), ssa_name: name))
2611 {
2612 tree lhs = gimple_assign_lhs (gs: assign);
2613 merge_with_ssa_name (dest: name, src: lhs, deref: true);
2614 }
2615 }
2616 else if (gimple_store_p (gs: use_stmt))
2617 {
2618 gassign *assign = dyn_cast <gassign *> (p: use_stmt);
2619
2620 /* Handle *lhs = name. */
2621 if (assign && gimple_assign_rhs1 (gs: assign) == name)
2622 {
2623 if (dump_file)
2624 fprintf (stream: dump_file, format: "%*s ssa name saved to memory\n",
2625 m_depth * 4, "");
2626 m_lattice[index].merge (f: 0);
2627 }
2628 /* Handle *name = exp. */
2629 else if (assign
2630 && memory_access_to (op: gimple_assign_lhs (gs: assign), ssa_name: name))
2631 {
2632 /* In general we can not ignore clobbers because they are
2633 barriers for code motion, however after inlining it is safe to
2634 do because local optimization passes do not consider clobbers
2635 from other functions.
2636 Similar logic is in ipa-pure-const.cc. */
2637 if (!cfun->after_inlining || !gimple_clobber_p (s: assign))
2638 m_lattice[index].merge_direct_store ();
2639 }
2640 /* ASM statements etc. */
2641 else if (!assign)
2642 {
2643 if (dump_file)
2644 fprintf (stream: dump_file, format: "%*s Unhandled store\n", m_depth * 4, "");
2645 m_lattice[index].merge (f: 0);
2646 }
2647 }
2648 else if (gassign *assign = dyn_cast <gassign *> (p: use_stmt))
2649 {
2650 enum tree_code code = gimple_assign_rhs_code (gs: assign);
2651
2652 /* See if operation is a merge as considered by
2653 tree-ssa-structalias.cc:find_func_aliases. */
2654 if (!truth_value_p (code)
2655 && code != POINTER_DIFF_EXPR
2656 && (code != POINTER_PLUS_EXPR
2657 || gimple_assign_rhs1 (gs: assign) == name))
2658 {
2659 tree lhs = gimple_assign_lhs (gs: assign);
2660 merge_with_ssa_name (dest: name, src: lhs, deref: false);
2661 }
2662 }
2663 else if (gphi *phi = dyn_cast <gphi *> (p: use_stmt))
2664 {
2665 tree result = gimple_phi_result (gs: phi);
2666 merge_with_ssa_name (dest: name, src: result, deref: false);
2667 }
2668 /* Conditions are not considered escape points
2669 by tree-ssa-structalias. */
2670 else if (gimple_code (g: use_stmt) == GIMPLE_COND)
2671 ;
2672 else
2673 {
2674 if (dump_file)
2675 fprintf (stream: dump_file, format: "%*s Unhandled stmt\n", m_depth * 4, "");
2676 m_lattice[index].merge (f: 0);
2677 }
2678
2679 if (dump_file)
2680 {
2681 fprintf (stream: dump_file, format: "%*s current flags of ", m_depth * 4, "");
2682 print_generic_expr (dump_file, name);
2683 m_lattice[index].dump (out: dump_file, indent: m_depth * 4 + 4);
2684 }
2685 }
2686 if (dump_file)
2687 {
2688 fprintf (stream: dump_file, format: "%*sflags of ssa name ", m_depth * 4, "");
2689 print_generic_expr (dump_file, name);
2690 m_lattice[index].dump (out: dump_file, indent: m_depth * 4 + 2);
2691 }
2692 m_lattice[index].open = false;
2693 if (!m_lattice[index].do_dataflow)
2694 m_lattice[index].known = true;
2695}
2696
2697/* Propagate info from SRC to DEST. If DEREF it true, assume that SRC
2698 is dereferenced. */
2699
2700void
2701modref_eaf_analysis::merge_with_ssa_name (tree dest, tree src, bool deref)
2702{
2703 int index = SSA_NAME_VERSION (dest);
2704 int src_index = SSA_NAME_VERSION (src);
2705
2706 /* Merging lattice with itself is a no-op. */
2707 if (!deref && src == dest)
2708 return;
2709
2710 m_depth++;
2711 analyze_ssa_name (name: src);
2712 m_depth--;
2713 if (deref)
2714 m_lattice[index].merge_deref (with: m_lattice[src_index], ignore_stores: false);
2715 else
2716 m_lattice[index].merge (with: m_lattice[src_index]);
2717
2718 /* If we failed to produce final solution add an edge to the dataflow
2719 graph. */
2720 if (!m_lattice[src_index].known)
2721 {
2722 modref_lattice::propagate_edge e = {.ssa_name: index, .deref: deref};
2723
2724 if (!m_lattice[src_index].propagate_to.length ())
2725 m_names_to_propagate.safe_push (obj: src_index);
2726 m_lattice[src_index].propagate_to.safe_push (obj: e);
2727 m_lattice[src_index].changed = true;
2728 m_lattice[src_index].do_dataflow = true;
2729 if (dump_file)
2730 fprintf (stream: dump_file,
2731 format: "%*sWill propgate from ssa_name %i to %i%s\n",
2732 m_depth * 4 + 4,
2733 "", src_index, index, deref ? " (deref)" : "");
2734 }
2735}
2736
2737/* In the case we deferred some SSA names, reprocess them. In the case some
2738 dataflow edges were introduced, do the actual iterative dataflow. */
2739
2740void
2741modref_eaf_analysis::propagate ()
2742{
2743 int iterations = 0;
2744 size_t i;
2745 int index;
2746 bool changed = true;
2747
2748 while (m_deferred_names.length ())
2749 {
2750 tree name = m_deferred_names.pop ();
2751 if (dump_file)
2752 fprintf (stream: dump_file, format: "Analyzing deferred SSA name\n");
2753 analyze_ssa_name (name, deferred: true);
2754 }
2755
2756 if (!m_names_to_propagate.length ())
2757 return;
2758 if (dump_file)
2759 fprintf (stream: dump_file, format: "Propagating EAF flags\n");
2760
2761 /* Compute reverse postorder. */
2762 auto_vec <int> rpo;
2763 struct stack_entry
2764 {
2765 int name;
2766 unsigned pos;
2767 };
2768 auto_vec <struct stack_entry> stack;
2769 int pos = m_names_to_propagate.length () - 1;
2770
2771 rpo.safe_grow (len: m_names_to_propagate.length (), exact: true);
2772 stack.reserve_exact (nelems: m_names_to_propagate.length ());
2773
2774 /* We reuse known flag for RPO DFS walk bookkeeping. */
2775 if (flag_checking)
2776 FOR_EACH_VEC_ELT (m_names_to_propagate, i, index)
2777 gcc_assert (!m_lattice[index].known && m_lattice[index].changed);
2778
2779 FOR_EACH_VEC_ELT (m_names_to_propagate, i, index)
2780 {
2781 if (!m_lattice[index].known)
2782 {
2783 stack_entry e = {.name: index, .pos: 0};
2784
2785 stack.quick_push (obj: e);
2786 m_lattice[index].known = true;
2787 }
2788 while (stack.length ())
2789 {
2790 bool found = false;
2791 int index1 = stack.last ().name;
2792
2793 while (stack.last ().pos < m_lattice[index1].propagate_to.length ())
2794 {
2795 int index2 = m_lattice[index1]
2796 .propagate_to[stack.last ().pos].ssa_name;
2797
2798 stack.last ().pos++;
2799 if (!m_lattice[index2].known
2800 && m_lattice[index2].propagate_to.length ())
2801 {
2802 stack_entry e = {.name: index2, .pos: 0};
2803
2804 stack.quick_push (obj: e);
2805 m_lattice[index2].known = true;
2806 found = true;
2807 break;
2808 }
2809 }
2810 if (!found
2811 && stack.last ().pos == m_lattice[index1].propagate_to.length ())
2812 {
2813 rpo[pos--] = index1;
2814 stack.pop ();
2815 }
2816 }
2817 }
2818
2819 /* Perform iterative dataflow. */
2820 while (changed)
2821 {
2822 changed = false;
2823 iterations++;
2824 if (dump_file)
2825 fprintf (stream: dump_file, format: " iteration %i\n", iterations);
2826 FOR_EACH_VEC_ELT (rpo, i, index)
2827 {
2828 if (m_lattice[index].changed)
2829 {
2830 size_t j;
2831
2832 m_lattice[index].changed = false;
2833 if (dump_file)
2834 fprintf (stream: dump_file, format: " Visiting ssa name %i\n", index);
2835 for (j = 0; j < m_lattice[index].propagate_to.length (); j++)
2836 {
2837 bool ch;
2838 int target = m_lattice[index].propagate_to[j].ssa_name;
2839 bool deref = m_lattice[index].propagate_to[j].deref;
2840
2841 if (dump_file)
2842 fprintf (stream: dump_file, format: " Propagating flags of ssa name"
2843 " %i to %i%s\n",
2844 index, target, deref ? " (deref)" : "");
2845 m_lattice[target].known = true;
2846 if (!m_lattice[index].propagate_to[j].deref)
2847 ch = m_lattice[target].merge (with: m_lattice[index]);
2848 else
2849 ch = m_lattice[target].merge_deref (with: m_lattice[index],
2850 ignore_stores: false);
2851 if (!ch)
2852 continue;
2853 if (dump_file)
2854 {
2855 fprintf (stream: dump_file, format: " New lattice: ");
2856 m_lattice[target].dump (out: dump_file);
2857 }
2858 changed = true;
2859 m_lattice[target].changed = true;
2860 }
2861 }
2862 }
2863 }
2864 if (dump_file)
2865 fprintf (stream: dump_file, format: "EAF flags propagated in %i iterations\n", iterations);
2866}
2867
2868/* Record escape points of PARM_INDEX according to LATTICE. */
2869
2870void
2871modref_eaf_analysis::record_escape_points (tree name, int parm_index, int flags)
2872{
2873 modref_lattice &lattice = m_lattice[SSA_NAME_VERSION (name)];
2874
2875 if (lattice.escape_points.length ())
2876 {
2877 escape_point *ep;
2878 unsigned int ip;
2879 cgraph_node *node = cgraph_node::get (decl: current_function_decl);
2880
2881 gcc_assert (m_ipa);
2882 FOR_EACH_VEC_ELT (lattice.escape_points, ip, ep)
2883 if ((ep->min_flags & flags) != flags)
2884 {
2885 cgraph_edge *e = node->get_edge (call_stmt: ep->call);
2886 struct escape_entry ee = {.parm_index: parm_index, .arg: ep->arg,
2887 .min_flags: ep->min_flags, .direct: ep->direct};
2888
2889 escape_summaries->get_create (edge: e)->esc.safe_push (obj: ee);
2890 }
2891 }
2892}
2893
2894/* Determine EAF flags for function parameters
2895 and fill in SUMMARY/SUMMARY_LTO. If IPA is true work in IPA mode
2896 where we also collect escape points.
2897 PAST_FLAGS, PAST_RETSLOT_FLAGS, PAST_STATIC_CHAIN_FLAGS can be
2898 used to preserve flags from previous (IPA) run for cases where
2899 late optimizations changed code in a way we can no longer analyze
2900 it easily. */
2901
2902static void
2903analyze_parms (modref_summary *summary, modref_summary_lto *summary_lto,
2904 bool ipa, vec<eaf_flags_t> &past_flags,
2905 int past_retslot_flags, int past_static_chain_flags)
2906{
2907 unsigned int parm_index = 0;
2908 unsigned int count = 0;
2909 int ecf_flags = flags_from_decl_or_type (current_function_decl);
2910 tree retslot = NULL;
2911 tree static_chain = NULL;
2912
2913 /* If there is return slot, look up its SSA name. */
2914 if (DECL_RESULT (current_function_decl)
2915 && DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
2916 retslot = ssa_default_def (cfun, DECL_RESULT (current_function_decl));
2917 if (cfun->static_chain_decl)
2918 static_chain = ssa_default_def (cfun, cfun->static_chain_decl);
2919
2920 for (tree parm = DECL_ARGUMENTS (current_function_decl); parm;
2921 parm = TREE_CHAIN (parm))
2922 count++;
2923
2924 if (!count && !retslot && !static_chain)
2925 return;
2926
2927 modref_eaf_analysis eaf_analysis (ipa);
2928
2929 /* Determine all SSA names we need to know flags for. */
2930 for (tree parm = DECL_ARGUMENTS (current_function_decl); parm;
2931 parm = TREE_CHAIN (parm))
2932 {
2933 tree name = ssa_default_def (cfun, parm);
2934 if (name)
2935 eaf_analysis.analyze_ssa_name (name);
2936 }
2937 if (retslot)
2938 eaf_analysis.analyze_ssa_name (name: retslot);
2939 if (static_chain)
2940 eaf_analysis.analyze_ssa_name (name: static_chain);
2941
2942 /* Do the dataflow. */
2943 eaf_analysis.propagate ();
2944
2945 tree attr = lookup_attribute (attr_name: "fn spec",
2946 TYPE_ATTRIBUTES
2947 (TREE_TYPE (current_function_decl)));
2948 attr_fnspec fnspec (attr
2949 ? TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)))
2950 : "");
2951
2952
2953 /* Store results to summaries. */
2954 for (tree parm = DECL_ARGUMENTS (current_function_decl); parm; parm_index++,
2955 parm = TREE_CHAIN (parm))
2956 {
2957 tree name = ssa_default_def (cfun, parm);
2958 if (!name || has_zero_uses (var: name))
2959 {
2960 /* We do not track non-SSA parameters,
2961 but we want to track unused gimple_regs. */
2962 if (!is_gimple_reg (parm))
2963 continue;
2964 if (summary)
2965 {
2966 if (parm_index >= summary->arg_flags.length ())
2967 summary->arg_flags.safe_grow_cleared (len: count, exact: true);
2968 summary->arg_flags[parm_index] = EAF_UNUSED;
2969 }
2970 else if (summary_lto)
2971 {
2972 if (parm_index >= summary_lto->arg_flags.length ())
2973 summary_lto->arg_flags.safe_grow_cleared (len: count, exact: true);
2974 summary_lto->arg_flags[parm_index] = EAF_UNUSED;
2975 }
2976 continue;
2977 }
2978 int flags = eaf_analysis.get_ssa_name_flags (name);
2979 int attr_flags = fnspec.arg_eaf_flags (i: parm_index);
2980
2981 if (dump_file && (flags | attr_flags) != flags && !(flags & EAF_UNUSED))
2982 {
2983 fprintf (stream: dump_file,
2984 format: " Flags for param %i combined with fnspec flags:",
2985 (int)parm_index);
2986 dump_eaf_flags (out: dump_file, flags: attr_flags, newline: false);
2987 fprintf (stream: dump_file, format: " determined: ");
2988 dump_eaf_flags (out: dump_file, flags, newline: true);
2989 }
2990 flags |= attr_flags;
2991
2992 /* Eliminate useless flags so we do not end up storing unnecessary
2993 summaries. */
2994
2995 flags = remove_useless_eaf_flags
2996 (eaf_flags: flags, ecf_flags,
2997 VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))));
2998 if (past_flags.length () > parm_index)
2999 {
3000 int past = past_flags[parm_index];
3001 past = remove_useless_eaf_flags
3002 (eaf_flags: past, ecf_flags,
3003 VOID_TYPE_P (TREE_TYPE
3004 (TREE_TYPE (current_function_decl))));
3005 if (dump_file && (flags | past) != flags && !(flags & EAF_UNUSED))
3006 {
3007 fprintf (stream: dump_file,
3008 format: " Flags for param %i combined with IPA pass:",
3009 (int)parm_index);
3010 dump_eaf_flags (out: dump_file, flags: past, newline: false);
3011 fprintf (stream: dump_file, format: " determined: ");
3012 dump_eaf_flags (out: dump_file, flags, newline: true);
3013 }
3014 if (!(flags & EAF_UNUSED))
3015 flags |= past;
3016 }
3017
3018 if (flags)
3019 {
3020 if (summary)
3021 {
3022 if (parm_index >= summary->arg_flags.length ())
3023 summary->arg_flags.safe_grow_cleared (len: count, exact: true);
3024 summary->arg_flags[parm_index] = flags;
3025 }
3026 else if (summary_lto)
3027 {
3028 if (parm_index >= summary_lto->arg_flags.length ())
3029 summary_lto->arg_flags.safe_grow_cleared (len: count, exact: true);
3030 summary_lto->arg_flags[parm_index] = flags;
3031 }
3032 eaf_analysis.record_escape_points (name, parm_index, flags);
3033 }
3034 }
3035 if (retslot)
3036 {
3037 int flags = eaf_analysis.get_ssa_name_flags (name: retslot);
3038 int past = past_retslot_flags;
3039
3040 flags = remove_useless_eaf_flags (eaf_flags: flags, ecf_flags, returns_void: false);
3041 past = remove_useless_eaf_flags
3042 (eaf_flags: past, ecf_flags,
3043 VOID_TYPE_P (TREE_TYPE
3044 (TREE_TYPE (current_function_decl))));
3045 if (dump_file && (flags | past) != flags && !(flags & EAF_UNUSED))
3046 {
3047 fprintf (stream: dump_file,
3048 format: " Retslot flags combined with IPA pass:");
3049 dump_eaf_flags (out: dump_file, flags: past, newline: false);
3050 fprintf (stream: dump_file, format: " determined: ");
3051 dump_eaf_flags (out: dump_file, flags, newline: true);
3052 }
3053 if (!(flags & EAF_UNUSED))
3054 flags |= past;
3055 if (flags)
3056 {
3057 if (summary)
3058 summary->retslot_flags = flags;
3059 if (summary_lto)
3060 summary_lto->retslot_flags = flags;
3061 eaf_analysis.record_escape_points (name: retslot,
3062 parm_index: MODREF_RETSLOT_PARM, flags);
3063 }
3064 }
3065 if (static_chain)
3066 {
3067 int flags = eaf_analysis.get_ssa_name_flags (name: static_chain);
3068 int past = past_static_chain_flags;
3069
3070 flags = remove_useless_eaf_flags (eaf_flags: flags, ecf_flags, returns_void: false);
3071 past = remove_useless_eaf_flags
3072 (eaf_flags: past, ecf_flags,
3073 VOID_TYPE_P (TREE_TYPE
3074 (TREE_TYPE (current_function_decl))));
3075 if (dump_file && (flags | past) != flags && !(flags & EAF_UNUSED))
3076 {
3077 fprintf (stream: dump_file,
3078 format: " Static chain flags combined with IPA pass:");
3079 dump_eaf_flags (out: dump_file, flags: past, newline: false);
3080 fprintf (stream: dump_file, format: " determined: ");
3081 dump_eaf_flags (out: dump_file, flags, newline: true);
3082 }
3083 if (!(flags & EAF_UNUSED))
3084 flags |= past;
3085 if (flags)
3086 {
3087 if (summary)
3088 summary->static_chain_flags = flags;
3089 if (summary_lto)
3090 summary_lto->static_chain_flags = flags;
3091 eaf_analysis.record_escape_points (name: static_chain,
3092 parm_index: MODREF_STATIC_CHAIN_PARM,
3093 flags);
3094 }
3095 }
3096}
3097
3098/* Analyze function. IPA indicates whether we're running in local mode
3099 (false) or the IPA mode (true).
3100 Return true if fixup cfg is needed after the pass. */
3101
3102static bool
3103analyze_function (bool ipa)
3104{
3105 bool fixup_cfg = false;
3106 if (dump_file)
3107 fprintf (stream: dump_file, format: "\n\nmodref analyzing '%s' (ipa=%i)%s%s\n",
3108 cgraph_node::get (decl: current_function_decl)->dump_name (), ipa,
3109 TREE_READONLY (current_function_decl) ? " (const)" : "",
3110 DECL_PURE_P (current_function_decl) ? " (pure)" : "");
3111
3112 /* Don't analyze this function if it's compiled with -fno-strict-aliasing. */
3113 if (!flag_ipa_modref
3114 || lookup_attribute (attr_name: "noipa", DECL_ATTRIBUTES (current_function_decl)))
3115 return false;
3116
3117 /* Compute no-LTO summaries when local optimization is going to happen. */
3118 bool nolto = (!ipa || ((!flag_lto || flag_fat_lto_objects) && !in_lto_p)
3119 || (in_lto_p && !flag_wpa
3120 && flag_incremental_link != INCREMENTAL_LINK_LTO));
3121 /* Compute LTO when LTO streaming is going to happen. */
3122 bool lto = ipa && ((flag_lto && !in_lto_p)
3123 || flag_wpa
3124 || flag_incremental_link == INCREMENTAL_LINK_LTO);
3125 cgraph_node *fnode = cgraph_node::get (decl: current_function_decl);
3126
3127 modref_summary *summary = NULL;
3128 modref_summary_lto *summary_lto = NULL;
3129
3130 bool past_flags_known = false;
3131 auto_vec <eaf_flags_t> past_flags;
3132 int past_retslot_flags = 0;
3133 int past_static_chain_flags = 0;
3134
3135 /* Initialize the summary.
3136 If we run in local mode there is possibly pre-existing summary from
3137 IPA pass. Dump it so it is easy to compare if mod-ref info has
3138 improved. */
3139 if (!ipa)
3140 {
3141 if (!optimization_summaries)
3142 optimization_summaries = modref_summaries::create_ggc (symtab);
3143 else /* Remove existing summary if we are re-running the pass. */
3144 {
3145 summary = optimization_summaries->get (node: fnode);
3146 if (summary != NULL
3147 && summary->loads)
3148 {
3149 if (dump_file)
3150 {
3151 fprintf (stream: dump_file, format: "Past summary:\n");
3152 optimization_summaries->get (node: fnode)->dump (out: dump_file);
3153 }
3154 past_flags.reserve_exact (nelems: summary->arg_flags.length ());
3155 past_flags.splice (src: summary->arg_flags);
3156 past_retslot_flags = summary->retslot_flags;
3157 past_static_chain_flags = summary->static_chain_flags;
3158 past_flags_known = true;
3159 }
3160 optimization_summaries->remove (node: fnode);
3161 }
3162 summary = optimization_summaries->get_create (node: fnode);
3163 gcc_checking_assert (nolto && !lto);
3164 }
3165 /* In IPA mode we analyze every function precisely once. Assert that. */
3166 else
3167 {
3168 if (nolto)
3169 {
3170 if (!summaries)
3171 summaries = modref_summaries::create_ggc (symtab);
3172 else
3173 summaries->remove (node: fnode);
3174 summary = summaries->get_create (node: fnode);
3175 }
3176 if (lto)
3177 {
3178 if (!summaries_lto)
3179 summaries_lto = modref_summaries_lto::create_ggc (symtab);
3180 else
3181 summaries_lto->remove (node: fnode);
3182 summary_lto = summaries_lto->get_create (node: fnode);
3183 }
3184 if (!fnspec_summaries)
3185 fnspec_summaries = new fnspec_summaries_t (symtab);
3186 if (!escape_summaries)
3187 escape_summaries = new escape_summaries_t (symtab);
3188 }
3189
3190
3191 /* Create and initialize summary for F.
3192 Note that summaries may be already allocated from previous
3193 run of the pass. */
3194 if (nolto)
3195 {
3196 gcc_assert (!summary->loads);
3197 summary->loads = modref_records::create_ggc ();
3198 gcc_assert (!summary->stores);
3199 summary->stores = modref_records::create_ggc ();
3200 summary->writes_errno = false;
3201 summary->side_effects = false;
3202 summary->nondeterministic = false;
3203 summary->calls_interposable = false;
3204 }
3205 if (lto)
3206 {
3207 gcc_assert (!summary_lto->loads);
3208 summary_lto->loads = modref_records_lto::create_ggc ();
3209 gcc_assert (!summary_lto->stores);
3210 summary_lto->stores = modref_records_lto::create_ggc ();
3211 summary_lto->writes_errno = false;
3212 summary_lto->side_effects = false;
3213 summary_lto->nondeterministic = false;
3214 summary_lto->calls_interposable = false;
3215 }
3216
3217 analyze_parms (summary, summary_lto, ipa,
3218 past_flags, past_retslot_flags, past_static_chain_flags);
3219
3220 {
3221 modref_access_analysis analyzer (ipa, summary, summary_lto);
3222 analyzer.analyze ();
3223 }
3224
3225 if (!ipa && flag_ipa_pure_const)
3226 {
3227 if (!summary->stores->every_base && !summary->stores->bases
3228 && !summary->nondeterministic)
3229 {
3230 if (!summary->loads->every_base && !summary->loads->bases
3231 && !summary->calls_interposable)
3232 fixup_cfg = ipa_make_function_const (fnode,
3233 summary->side_effects, true);
3234 else
3235 fixup_cfg = ipa_make_function_pure (fnode,
3236 summary->side_effects, true);
3237 }
3238 }
3239 int ecf_flags = flags_from_decl_or_type (current_function_decl);
3240 if (summary && !summary->useful_p (ecf_flags))
3241 {
3242 if (!ipa)
3243 optimization_summaries->remove (node: fnode);
3244 else
3245 summaries->remove (node: fnode);
3246 summary = NULL;
3247 }
3248 if (summary)
3249 summary->finalize (fun: current_function_decl);
3250 if (summary_lto && !summary_lto->useful_p (ecf_flags))
3251 {
3252 summaries_lto->remove (node: fnode);
3253 summary_lto = NULL;
3254 }
3255
3256 if (ipa && !summary && !summary_lto)
3257 remove_modref_edge_summaries (node: fnode);
3258
3259 if (dump_file)
3260 {
3261 fprintf (stream: dump_file, format: " - modref done with result: tracked.\n");
3262 if (summary)
3263 summary->dump (out: dump_file);
3264 if (summary_lto)
3265 summary_lto->dump (out: dump_file);
3266 dump_modref_edge_summaries (out: dump_file, node: fnode, depth: 2);
3267 /* To simplify debugging, compare IPA and local solutions. */
3268 if (past_flags_known && summary)
3269 {
3270 size_t len = summary->arg_flags.length ();
3271
3272 if (past_flags.length () > len)
3273 len = past_flags.length ();
3274 for (size_t i = 0; i < len; i++)
3275 {
3276 int old_flags = i < past_flags.length () ? past_flags[i] : 0;
3277 int new_flags = i < summary->arg_flags.length ()
3278 ? summary->arg_flags[i] : 0;
3279 old_flags = remove_useless_eaf_flags
3280 (eaf_flags: old_flags, ecf_flags: flags_from_decl_or_type (current_function_decl),
3281 VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))));
3282 if (old_flags != new_flags)
3283 {
3284 if ((old_flags & ~new_flags) == 0
3285 || (new_flags & EAF_UNUSED))
3286 fprintf (stream: dump_file, format: " Flags for param %i improved:",
3287 (int)i);
3288 else
3289 gcc_unreachable ();
3290 dump_eaf_flags (out: dump_file, flags: old_flags, newline: false);
3291 fprintf (stream: dump_file, format: " -> ");
3292 dump_eaf_flags (out: dump_file, flags: new_flags, newline: true);
3293 }
3294 }
3295 past_retslot_flags = remove_useless_eaf_flags
3296 (eaf_flags: past_retslot_flags,
3297 ecf_flags: flags_from_decl_or_type (current_function_decl),
3298 VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))));
3299 if (past_retslot_flags != summary->retslot_flags)
3300 {
3301 if ((past_retslot_flags & ~summary->retslot_flags) == 0
3302 || (summary->retslot_flags & EAF_UNUSED))
3303 fprintf (stream: dump_file, format: " Flags for retslot improved:");
3304 else
3305 gcc_unreachable ();
3306 dump_eaf_flags (out: dump_file, flags: past_retslot_flags, newline: false);
3307 fprintf (stream: dump_file, format: " -> ");
3308 dump_eaf_flags (out: dump_file, flags: summary->retslot_flags, newline: true);
3309 }
3310 past_static_chain_flags = remove_useless_eaf_flags
3311 (eaf_flags: past_static_chain_flags,
3312 ecf_flags: flags_from_decl_or_type (current_function_decl),
3313 VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))));
3314 if (past_static_chain_flags != summary->static_chain_flags)
3315 {
3316 if ((past_static_chain_flags & ~summary->static_chain_flags) == 0
3317 || (summary->static_chain_flags & EAF_UNUSED))
3318 fprintf (stream: dump_file, format: " Flags for static chain improved:");
3319 else
3320 gcc_unreachable ();
3321 dump_eaf_flags (out: dump_file, flags: past_static_chain_flags, newline: false);
3322 fprintf (stream: dump_file, format: " -> ");
3323 dump_eaf_flags (out: dump_file, flags: summary->static_chain_flags, newline: true);
3324 }
3325 }
3326 else if (past_flags_known && !summary)
3327 {
3328 for (size_t i = 0; i < past_flags.length (); i++)
3329 {
3330 int old_flags = past_flags[i];
3331 old_flags = remove_useless_eaf_flags
3332 (eaf_flags: old_flags, ecf_flags: flags_from_decl_or_type (current_function_decl),
3333 VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))));
3334 if (old_flags)
3335 {
3336 fprintf (stream: dump_file, format: " Flags for param %i worsened:",
3337 (int)i);
3338 dump_eaf_flags (out: dump_file, flags: old_flags, newline: false);
3339 fprintf (stream: dump_file, format: " -> \n");
3340 }
3341 }
3342 past_retslot_flags = remove_useless_eaf_flags
3343 (eaf_flags: past_retslot_flags,
3344 ecf_flags: flags_from_decl_or_type (current_function_decl),
3345 VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))));
3346 if (past_retslot_flags)
3347 {
3348 fprintf (stream: dump_file, format: " Flags for retslot worsened:");
3349 dump_eaf_flags (out: dump_file, flags: past_retslot_flags, newline: false);
3350 fprintf (stream: dump_file, format: " ->\n");
3351 }
3352 past_static_chain_flags = remove_useless_eaf_flags
3353 (eaf_flags: past_static_chain_flags,
3354 ecf_flags: flags_from_decl_or_type (current_function_decl),
3355 VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))));
3356 if (past_static_chain_flags)
3357 {
3358 fprintf (stream: dump_file, format: " Flags for static chain worsened:");
3359 dump_eaf_flags (out: dump_file, flags: past_static_chain_flags, newline: false);
3360 fprintf (stream: dump_file, format: " ->\n");
3361 }
3362 }
3363 }
3364 return fixup_cfg;
3365}
3366
3367/* Callback for generate_summary. */
3368
3369static void
3370modref_generate (void)
3371{
3372 struct cgraph_node *node;
3373 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
3374 {
3375 function *f = DECL_STRUCT_FUNCTION (node->decl);
3376 if (!f)
3377 continue;
3378 push_cfun (new_cfun: f);
3379 analyze_function (ipa: true);
3380 pop_cfun ();
3381 }
3382}
3383
3384} /* ANON namespace. */
3385
3386/* Debugging helper. */
3387
3388void
3389debug_eaf_flags (int flags)
3390{
3391 dump_eaf_flags (stderr, flags, newline: true);
3392}
3393
3394/* Called when a new function is inserted to callgraph late. */
3395
3396void
3397modref_summaries::insert (struct cgraph_node *node, modref_summary *)
3398{
3399 /* Local passes ought to be executed by the pass manager. */
3400 if (this == optimization_summaries)
3401 {
3402 optimization_summaries->remove (node);
3403 return;
3404 }
3405 if (!DECL_STRUCT_FUNCTION (node->decl)
3406 || !opt_for_fn (node->decl, flag_ipa_modref))
3407 {
3408 summaries->remove (node);
3409 return;
3410 }
3411 push_cfun (DECL_STRUCT_FUNCTION (node->decl));
3412 analyze_function (ipa: true);
3413 pop_cfun ();
3414}
3415
3416/* Called when a new function is inserted to callgraph late. */
3417
3418void
3419modref_summaries_lto::insert (struct cgraph_node *node, modref_summary_lto *)
3420{
3421 /* We do not support adding new function when IPA information is already
3422 propagated. This is done only by SIMD cloning that is not very
3423 critical. */
3424 if (!DECL_STRUCT_FUNCTION (node->decl)
3425 || !opt_for_fn (node->decl, flag_ipa_modref)
3426 || propagated)
3427 {
3428 summaries_lto->remove (node);
3429 return;
3430 }
3431 push_cfun (DECL_STRUCT_FUNCTION (node->decl));
3432 analyze_function (ipa: true);
3433 pop_cfun ();
3434}
3435
3436/* Called when new clone is inserted to callgraph late. */
3437
3438void
3439modref_summaries::duplicate (cgraph_node *, cgraph_node *dst,
3440 modref_summary *src_data,
3441 modref_summary *dst_data)
3442{
3443 /* Do not duplicate optimization summaries; we do not handle parameter
3444 transforms on them. */
3445 if (this == optimization_summaries)
3446 {
3447 optimization_summaries->remove (node: dst);
3448 return;
3449 }
3450 dst_data->stores = modref_records::create_ggc ();
3451 dst_data->stores->copy_from (other: src_data->stores);
3452 dst_data->loads = modref_records::create_ggc ();
3453 dst_data->loads->copy_from (other: src_data->loads);
3454 dst_data->kills.reserve_exact (nelems: src_data->kills.length ());
3455 dst_data->kills.splice (src: src_data->kills);
3456 dst_data->writes_errno = src_data->writes_errno;
3457 dst_data->side_effects = src_data->side_effects;
3458 dst_data->nondeterministic = src_data->nondeterministic;
3459 dst_data->calls_interposable = src_data->calls_interposable;
3460 if (src_data->arg_flags.length ())
3461 dst_data->arg_flags = src_data->arg_flags.copy ();
3462 dst_data->retslot_flags = src_data->retslot_flags;
3463 dst_data->static_chain_flags = src_data->static_chain_flags;
3464}
3465
3466/* Called when new clone is inserted to callgraph late. */
3467
3468void
3469modref_summaries_lto::duplicate (cgraph_node *, cgraph_node *,
3470 modref_summary_lto *src_data,
3471 modref_summary_lto *dst_data)
3472{
3473 /* Be sure that no further cloning happens after ipa-modref. If it does
3474 we will need to update signatures for possible param changes. */
3475 gcc_checking_assert (!((modref_summaries_lto *)summaries_lto)->propagated);
3476 dst_data->stores = modref_records_lto::create_ggc ();
3477 dst_data->stores->copy_from (other: src_data->stores);
3478 dst_data->loads = modref_records_lto::create_ggc ();
3479 dst_data->loads->copy_from (other: src_data->loads);
3480 dst_data->kills.reserve_exact (nelems: src_data->kills.length ());
3481 dst_data->kills.splice (src: src_data->kills);
3482 dst_data->writes_errno = src_data->writes_errno;
3483 dst_data->side_effects = src_data->side_effects;
3484 dst_data->nondeterministic = src_data->nondeterministic;
3485 dst_data->calls_interposable = src_data->calls_interposable;
3486 if (src_data->arg_flags.length ())
3487 dst_data->arg_flags = src_data->arg_flags.copy ();
3488 dst_data->retslot_flags = src_data->retslot_flags;
3489 dst_data->static_chain_flags = src_data->static_chain_flags;
3490}
3491
3492namespace
3493{
3494/* Definition of the modref pass on GIMPLE. */
3495const pass_data pass_data_modref = {
3496 .type: GIMPLE_PASS,
3497 .name: "modref",
3498 .optinfo_flags: OPTGROUP_IPA,
3499 .tv_id: TV_TREE_MODREF,
3500 .properties_required: (PROP_cfg | PROP_ssa),
3501 .properties_provided: 0,
3502 .properties_destroyed: 0,
3503 .todo_flags_start: 0,
3504 .todo_flags_finish: 0,
3505};
3506
3507class pass_modref : public gimple_opt_pass
3508{
3509 public:
3510 pass_modref (gcc::context *ctxt)
3511 : gimple_opt_pass (pass_data_modref, ctxt) {}
3512
3513 /* opt_pass methods: */
3514 opt_pass *clone () final override
3515 {
3516 return new pass_modref (m_ctxt);
3517 }
3518 bool gate (function *) final override
3519 {
3520 return flag_ipa_modref;
3521 }
3522 unsigned int execute (function *) final override;
3523};
3524
3525/* Encode TT to the output block OB using the summary streaming API. */
3526
3527static void
3528write_modref_records (modref_records_lto *tt, struct output_block *ob)
3529{
3530 streamer_write_uhwi (ob, tt->every_base);
3531 streamer_write_uhwi (ob, vec_safe_length (v: tt->bases));
3532 for (auto base_node : tt->bases)
3533 {
3534 stream_write_tree (ob, base_node->base, true);
3535
3536 streamer_write_uhwi (ob, base_node->every_ref);
3537 streamer_write_uhwi (ob, vec_safe_length (v: base_node->refs));
3538
3539 for (auto ref_node : base_node->refs)
3540 {
3541 stream_write_tree (ob, ref_node->ref, true);
3542 streamer_write_uhwi (ob, ref_node->every_access);
3543 streamer_write_uhwi (ob, vec_safe_length (v: ref_node->accesses));
3544
3545 for (auto access_node : ref_node->accesses)
3546 access_node.stream_out (ob);
3547 }
3548 }
3549}
3550
3551/* Read a modref_tree from the input block IB using the data from DATA_IN.
3552 This assumes that the tree was encoded using write_modref_tree.
3553 Either nolto_ret or lto_ret is initialized by the tree depending whether
3554 LTO streaming is expected or not. */
3555
3556static void
3557read_modref_records (tree decl,
3558 lto_input_block *ib, struct data_in *data_in,
3559 modref_records **nolto_ret,
3560 modref_records_lto **lto_ret)
3561{
3562 size_t max_bases = opt_for_fn (decl, param_modref_max_bases);
3563 size_t max_refs = opt_for_fn (decl, param_modref_max_refs);
3564 size_t max_accesses = opt_for_fn (decl, param_modref_max_accesses);
3565
3566 if (lto_ret)
3567 *lto_ret = modref_records_lto::create_ggc ();
3568 if (nolto_ret)
3569 *nolto_ret = modref_records::create_ggc ();
3570 gcc_checking_assert (lto_ret || nolto_ret);
3571
3572 size_t every_base = streamer_read_uhwi (ib);
3573 size_t nbase = streamer_read_uhwi (ib);
3574
3575 gcc_assert (!every_base || nbase == 0);
3576 if (every_base)
3577 {
3578 if (nolto_ret)
3579 (*nolto_ret)->collapse ();
3580 if (lto_ret)
3581 (*lto_ret)->collapse ();
3582 }
3583 for (size_t i = 0; i < nbase; i++)
3584 {
3585 tree base_tree = stream_read_tree (ib, data_in);
3586 modref_base_node <alias_set_type> *nolto_base_node = NULL;
3587 modref_base_node <tree> *lto_base_node = NULL;
3588
3589 /* At stream in time we have LTO alias info. Check if we streamed in
3590 something obviously unnecessary. Do not glob types by alias sets;
3591 it is not 100% clear that ltrans types will get merged same way.
3592 Types may get refined based on ODR type conflicts. */
3593 if (base_tree && !get_alias_set (base_tree))
3594 {
3595 if (dump_file)
3596 {
3597 fprintf (stream: dump_file, format: "Streamed in alias set 0 type ");
3598 print_generic_expr (dump_file, base_tree);
3599 fprintf (stream: dump_file, format: "\n");
3600 }
3601 base_tree = NULL;
3602 }
3603
3604 if (nolto_ret)
3605 nolto_base_node = (*nolto_ret)->insert_base (base: base_tree
3606 ? get_alias_set (base_tree)
3607 : 0, ref: 0, INT_MAX);
3608 if (lto_ret)
3609 lto_base_node = (*lto_ret)->insert_base (base: base_tree, ref: 0, max_bases);
3610 size_t every_ref = streamer_read_uhwi (ib);
3611 size_t nref = streamer_read_uhwi (ib);
3612
3613 gcc_assert (!every_ref || nref == 0);
3614 if (every_ref)
3615 {
3616 if (nolto_base_node)
3617 nolto_base_node->collapse ();
3618 if (lto_base_node)
3619 lto_base_node->collapse ();
3620 }
3621 for (size_t j = 0; j < nref; j++)
3622 {
3623 tree ref_tree = stream_read_tree (ib, data_in);
3624
3625 if (ref_tree && !get_alias_set (ref_tree))
3626 {
3627 if (dump_file)
3628 {
3629 fprintf (stream: dump_file, format: "Streamed in alias set 0 type ");
3630 print_generic_expr (dump_file, ref_tree);
3631 fprintf (stream: dump_file, format: "\n");
3632 }
3633 ref_tree = NULL;
3634 }
3635
3636 modref_ref_node <alias_set_type> *nolto_ref_node = NULL;
3637 modref_ref_node <tree> *lto_ref_node = NULL;
3638
3639 if (nolto_base_node)
3640 nolto_ref_node
3641 = nolto_base_node->insert_ref (ref: ref_tree
3642 ? get_alias_set (ref_tree) : 0,
3643 max_refs);
3644 if (lto_base_node)
3645 lto_ref_node = lto_base_node->insert_ref (ref: ref_tree, max_refs);
3646
3647 size_t every_access = streamer_read_uhwi (ib);
3648 size_t naccesses = streamer_read_uhwi (ib);
3649
3650 if (nolto_ref_node && every_access)
3651 nolto_ref_node->collapse ();
3652 if (lto_ref_node && every_access)
3653 lto_ref_node->collapse ();
3654
3655 for (size_t k = 0; k < naccesses; k++)
3656 {
3657 modref_access_node a = modref_access_node::stream_in (ib);
3658 if (nolto_ref_node)
3659 nolto_ref_node->insert_access (a, max_accesses, record_adjustments: false);
3660 if (lto_ref_node)
3661 lto_ref_node->insert_access (a, max_accesses, record_adjustments: false);
3662 }
3663 }
3664 }
3665 if (lto_ret)
3666 (*lto_ret)->cleanup ();
3667 if (nolto_ret)
3668 (*nolto_ret)->cleanup ();
3669}
3670
3671/* Write ESUM to BP. */
3672
3673static void
3674modref_write_escape_summary (struct bitpack_d *bp, escape_summary *esum)
3675{
3676 if (!esum)
3677 {
3678 bp_pack_var_len_unsigned (bp, 0);
3679 return;
3680 }
3681 bp_pack_var_len_unsigned (bp, esum->esc.length ());
3682 unsigned int i;
3683 escape_entry *ee;
3684 FOR_EACH_VEC_ELT (esum->esc, i, ee)
3685 {
3686 bp_pack_var_len_int (bp, ee->parm_index);
3687 bp_pack_var_len_unsigned (bp, ee->arg);
3688 bp_pack_var_len_unsigned (bp, ee->min_flags);
3689 bp_pack_value (bp, val: ee->direct, nbits: 1);
3690 }
3691}
3692
3693/* Read escape summary for E from BP. */
3694
3695static void
3696modref_read_escape_summary (struct bitpack_d *bp, cgraph_edge *e)
3697{
3698 unsigned int n = bp_unpack_var_len_unsigned (bp);
3699 if (!n)
3700 return;
3701 escape_summary *esum = escape_summaries->get_create (edge: e);
3702 esum->esc.reserve_exact (nelems: n);
3703 for (unsigned int i = 0; i < n; i++)
3704 {
3705 escape_entry ee;
3706 ee.parm_index = bp_unpack_var_len_int (bp);
3707 ee.arg = bp_unpack_var_len_unsigned (bp);
3708 ee.min_flags = bp_unpack_var_len_unsigned (bp);
3709 ee.direct = bp_unpack_value (bp, nbits: 1);
3710 esum->esc.quick_push (obj: ee);
3711 }
3712}
3713
3714/* Callback for write_summary. */
3715
3716static void
3717modref_write ()
3718{
3719 struct output_block *ob = create_output_block (LTO_section_ipa_modref);
3720 lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
3721 unsigned int count = 0;
3722 int i;
3723
3724 if (!summaries_lto)
3725 {
3726 streamer_write_uhwi (ob, 0);
3727 streamer_write_char_stream (obs: ob->main_stream, c: 0);
3728 produce_asm (ob, NULL);
3729 destroy_output_block (ob);
3730 return;
3731 }
3732
3733 for (i = 0; i < lto_symtab_encoder_size (encoder); i++)
3734 {
3735 symtab_node *snode = lto_symtab_encoder_deref (encoder, ref: i);
3736 cgraph_node *cnode = dyn_cast <cgraph_node *> (p: snode);
3737 modref_summary_lto *r;
3738
3739 if (cnode && cnode->definition && !cnode->alias
3740 && (r = summaries_lto->get (node: cnode))
3741 && r->useful_p (ecf_flags: flags_from_decl_or_type (cnode->decl)))
3742 count++;
3743 }
3744 streamer_write_uhwi (ob, count);
3745
3746 for (i = 0; i < lto_symtab_encoder_size (encoder); i++)
3747 {
3748 symtab_node *snode = lto_symtab_encoder_deref (encoder, ref: i);
3749 cgraph_node *cnode = dyn_cast <cgraph_node *> (p: snode);
3750
3751 if (cnode && cnode->definition && !cnode->alias)
3752 {
3753 modref_summary_lto *r = summaries_lto->get (node: cnode);
3754
3755 if (!r || !r->useful_p (ecf_flags: flags_from_decl_or_type (cnode->decl)))
3756 continue;
3757
3758 streamer_write_uhwi (ob, lto_symtab_encoder_encode (encoder, cnode));
3759
3760 streamer_write_uhwi (ob, r->arg_flags.length ());
3761 for (unsigned int i = 0; i < r->arg_flags.length (); i++)
3762 streamer_write_uhwi (ob, r->arg_flags[i]);
3763 streamer_write_uhwi (ob, r->retslot_flags);
3764 streamer_write_uhwi (ob, r->static_chain_flags);
3765
3766 write_modref_records (tt: r->loads, ob);
3767 write_modref_records (tt: r->stores, ob);
3768 streamer_write_uhwi (ob, r->kills.length ());
3769 for (auto kill : r->kills)
3770 kill.stream_out (ob);
3771
3772 struct bitpack_d bp = bitpack_create (s: ob->main_stream);
3773 bp_pack_value (bp: &bp, val: r->writes_errno, nbits: 1);
3774 bp_pack_value (bp: &bp, val: r->side_effects, nbits: 1);
3775 bp_pack_value (bp: &bp, val: r->nondeterministic, nbits: 1);
3776 bp_pack_value (bp: &bp, val: r->calls_interposable, nbits: 1);
3777 if (!flag_wpa)
3778 {
3779 for (cgraph_edge *e = cnode->indirect_calls;
3780 e; e = e->next_callee)
3781 {
3782 class fnspec_summary *sum = fnspec_summaries->get (edge: e);
3783 bp_pack_value (bp: &bp, val: sum != NULL, nbits: 1);
3784 if (sum)
3785 bp_pack_string (ob, &bp, sum->fnspec, true);
3786 class escape_summary *esum = escape_summaries->get (edge: e);
3787 modref_write_escape_summary (bp: &bp,esum);
3788 }
3789 for (cgraph_edge *e = cnode->callees; e; e = e->next_callee)
3790 {
3791 class fnspec_summary *sum = fnspec_summaries->get (edge: e);
3792 bp_pack_value (bp: &bp, val: sum != NULL, nbits: 1);
3793 if (sum)
3794 bp_pack_string (ob, &bp, sum->fnspec, true);
3795 class escape_summary *esum = escape_summaries->get (edge: e);
3796 modref_write_escape_summary (bp: &bp,esum);
3797 }
3798 }
3799 streamer_write_bitpack (bp: &bp);
3800 }
3801 }
3802 streamer_write_char_stream (obs: ob->main_stream, c: 0);
3803 produce_asm (ob, NULL);
3804 destroy_output_block (ob);
3805}
3806
3807static void
3808read_section (struct lto_file_decl_data *file_data, const char *data,
3809 size_t len)
3810{
3811 const struct lto_function_header *header
3812 = (const struct lto_function_header *) data;
3813 const int cfg_offset = sizeof (struct lto_function_header);
3814 const int main_offset = cfg_offset + header->cfg_size;
3815 const int string_offset = main_offset + header->main_size;
3816 struct data_in *data_in;
3817 unsigned int i;
3818 unsigned int f_count;
3819
3820 lto_input_block ib ((const char *) data + main_offset, header->main_size,
3821 file_data);
3822
3823 data_in
3824 = lto_data_in_create (file_data, (const char *) data + string_offset,
3825 header->string_size, vNULL);
3826 f_count = streamer_read_uhwi (&ib);
3827 for (i = 0; i < f_count; i++)
3828 {
3829 struct cgraph_node *node;
3830 lto_symtab_encoder_t encoder;
3831
3832 unsigned int index = streamer_read_uhwi (&ib);
3833 encoder = file_data->symtab_node_encoder;
3834 node = dyn_cast <cgraph_node *> (p: lto_symtab_encoder_deref (encoder,
3835 ref: index));
3836
3837 modref_summary *modref_sum = summaries
3838 ? summaries->get_create (node) : NULL;
3839 modref_summary_lto *modref_sum_lto = summaries_lto
3840 ? summaries_lto->get_create (node)
3841 : NULL;
3842 if (optimization_summaries)
3843 modref_sum = optimization_summaries->get_create (node);
3844
3845 if (modref_sum)
3846 {
3847 modref_sum->writes_errno = false;
3848 modref_sum->side_effects = false;
3849 modref_sum->nondeterministic = false;
3850 modref_sum->calls_interposable = false;
3851 }
3852 if (modref_sum_lto)
3853 {
3854 modref_sum_lto->writes_errno = false;
3855 modref_sum_lto->side_effects = false;
3856 modref_sum_lto->nondeterministic = false;
3857 modref_sum_lto->calls_interposable = false;
3858 }
3859
3860 gcc_assert (!modref_sum || (!modref_sum->loads
3861 && !modref_sum->stores));
3862 gcc_assert (!modref_sum_lto || (!modref_sum_lto->loads
3863 && !modref_sum_lto->stores));
3864 unsigned int args = streamer_read_uhwi (&ib);
3865 if (args && modref_sum)
3866 modref_sum->arg_flags.reserve_exact (nelems: args);
3867 if (args && modref_sum_lto)
3868 modref_sum_lto->arg_flags.reserve_exact (nelems: args);
3869 for (unsigned int i = 0; i < args; i++)
3870 {
3871 eaf_flags_t flags = streamer_read_uhwi (&ib);
3872 if (modref_sum)
3873 modref_sum->arg_flags.quick_push (obj: flags);
3874 if (modref_sum_lto)
3875 modref_sum_lto->arg_flags.quick_push (obj: flags);
3876 }
3877 eaf_flags_t flags = streamer_read_uhwi (&ib);
3878 if (modref_sum)
3879 modref_sum->retslot_flags = flags;
3880 if (modref_sum_lto)
3881 modref_sum_lto->retslot_flags = flags;
3882
3883 flags = streamer_read_uhwi (&ib);
3884 if (modref_sum)
3885 modref_sum->static_chain_flags = flags;
3886 if (modref_sum_lto)
3887 modref_sum_lto->static_chain_flags = flags;
3888
3889 read_modref_records (decl: node->decl, ib: &ib, data_in,
3890 nolto_ret: modref_sum ? &modref_sum->loads : NULL,
3891 lto_ret: modref_sum_lto ? &modref_sum_lto->loads : NULL);
3892 read_modref_records (decl: node->decl, ib: &ib, data_in,
3893 nolto_ret: modref_sum ? &modref_sum->stores : NULL,
3894 lto_ret: modref_sum_lto ? &modref_sum_lto->stores : NULL);
3895 int j = streamer_read_uhwi (&ib);
3896 if (j && modref_sum)
3897 modref_sum->kills.reserve_exact (nelems: j);
3898 if (j && modref_sum_lto)
3899 modref_sum_lto->kills.reserve_exact (nelems: j);
3900 for (int k = 0; k < j; k++)
3901 {
3902 modref_access_node a = modref_access_node::stream_in (ib: &ib);
3903
3904 if (modref_sum)
3905 modref_sum->kills.quick_push (obj: a);
3906 if (modref_sum_lto)
3907 modref_sum_lto->kills.quick_push (obj: a);
3908 }
3909 struct bitpack_d bp = streamer_read_bitpack (ib: &ib);
3910 if (bp_unpack_value (bp: &bp, nbits: 1))
3911 {
3912 if (modref_sum)
3913 modref_sum->writes_errno = true;
3914 if (modref_sum_lto)
3915 modref_sum_lto->writes_errno = true;
3916 }
3917 if (bp_unpack_value (bp: &bp, nbits: 1))
3918 {
3919 if (modref_sum)
3920 modref_sum->side_effects = true;
3921 if (modref_sum_lto)
3922 modref_sum_lto->side_effects = true;
3923 }
3924 if (bp_unpack_value (bp: &bp, nbits: 1))
3925 {
3926 if (modref_sum)
3927 modref_sum->nondeterministic = true;
3928 if (modref_sum_lto)
3929 modref_sum_lto->nondeterministic = true;
3930 }
3931 if (bp_unpack_value (bp: &bp, nbits: 1))
3932 {
3933 if (modref_sum)
3934 modref_sum->calls_interposable = true;
3935 if (modref_sum_lto)
3936 modref_sum_lto->calls_interposable = true;
3937 }
3938 if (!flag_ltrans)
3939 {
3940 for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
3941 {
3942 if (bp_unpack_value (bp: &bp, nbits: 1))
3943 {
3944 class fnspec_summary *sum = fnspec_summaries->get_create (edge: e);
3945 sum->fnspec = xstrdup (bp_unpack_string (data_in, &bp));
3946 }
3947 modref_read_escape_summary (bp: &bp, e);
3948 }
3949 for (cgraph_edge *e = node->callees; e; e = e->next_callee)
3950 {
3951 if (bp_unpack_value (bp: &bp, nbits: 1))
3952 {
3953 class fnspec_summary *sum = fnspec_summaries->get_create (edge: e);
3954 sum->fnspec = xstrdup (bp_unpack_string (data_in, &bp));
3955 }
3956 modref_read_escape_summary (bp: &bp, e);
3957 }
3958 }
3959 if (flag_ltrans)
3960 modref_sum->finalize (fun: node->decl);
3961 if (dump_file)
3962 {
3963 fprintf (stream: dump_file, format: "Read modref for %s\n",
3964 node->dump_name ());
3965 if (modref_sum)
3966 modref_sum->dump (out: dump_file);
3967 if (modref_sum_lto)
3968 modref_sum_lto->dump (out: dump_file);
3969 dump_modref_edge_summaries (out: dump_file, node, depth: 4);
3970 }
3971 }
3972
3973 lto_free_section_data (file_data, LTO_section_ipa_modref, NULL, data,
3974 len);
3975 lto_data_in_delete (data_in);
3976}
3977
3978/* Callback for read_summary. */
3979
3980static void
3981modref_read (void)
3982{
3983 struct lto_file_decl_data **file_data_vec = lto_get_file_decl_data ();
3984 struct lto_file_decl_data *file_data;
3985 unsigned int j = 0;
3986
3987 gcc_checking_assert (!optimization_summaries && !summaries && !summaries_lto);
3988 if (flag_ltrans)
3989 optimization_summaries = modref_summaries::create_ggc (symtab);
3990 else
3991 {
3992 if (flag_wpa || flag_incremental_link == INCREMENTAL_LINK_LTO)
3993 summaries_lto = modref_summaries_lto::create_ggc (symtab);
3994 if (!flag_wpa
3995 || (flag_incremental_link == INCREMENTAL_LINK_LTO
3996 && flag_fat_lto_objects))
3997 summaries = modref_summaries::create_ggc (symtab);
3998 if (!fnspec_summaries)
3999 fnspec_summaries = new fnspec_summaries_t (symtab);
4000 if (!escape_summaries)
4001 escape_summaries = new escape_summaries_t (symtab);
4002 }
4003
4004 while ((file_data = file_data_vec[j++]))
4005 {
4006 size_t len;
4007 const char *data = lto_get_summary_section_data (file_data,
4008 LTO_section_ipa_modref,
4009 &len);
4010 if (data)
4011 read_section (file_data, data, len);
4012 else
4013 /* Fatal error here. We do not want to support compiling ltrans units
4014 with different version of compiler or different flags than the WPA
4015 unit, so this should never happen. */
4016 fatal_error (input_location,
4017 "IPA modref summary is missing in input file");
4018 }
4019}
4020
4021/* Recompute arg_flags for param adjustments in INFO. */
4022
4023static void
4024remap_arg_flags (auto_vec <eaf_flags_t> &arg_flags, clone_info *info)
4025{
4026 auto_vec<eaf_flags_t> old = arg_flags.copy ();
4027 int max = -1;
4028 size_t i;
4029 ipa_adjusted_param *p;
4030
4031 arg_flags.release ();
4032
4033 FOR_EACH_VEC_SAFE_ELT (info->param_adjustments->m_adj_params, i, p)
4034 {
4035 int o = info->param_adjustments->get_original_index (newidx: i);
4036 if (o >= 0 && (int)old.length () > o && old[o])
4037 max = i;
4038 }
4039 if (max >= 0)
4040 arg_flags.safe_grow_cleared (len: max + 1, exact: true);
4041 FOR_EACH_VEC_SAFE_ELT (info->param_adjustments->m_adj_params, i, p)
4042 {
4043 int o = info->param_adjustments->get_original_index (newidx: i);
4044 if (o >= 0 && (int)old.length () > o && old[o])
4045 arg_flags[i] = old[o];
4046 }
4047}
4048
4049/* Update kills according to the parm map MAP. */
4050
4051static void
4052remap_kills (vec <modref_access_node> &kills, const vec <int> &map)
4053{
4054 for (size_t i = 0; i < kills.length ();)
4055 if (kills[i].parm_index >= 0)
4056 {
4057 if (kills[i].parm_index < (int)map.length ()
4058 && map[kills[i].parm_index] != MODREF_UNKNOWN_PARM)
4059 {
4060 kills[i].parm_index = map[kills[i].parm_index];
4061 i++;
4062 }
4063 else
4064 kills.unordered_remove (ix: i);
4065 }
4066 else
4067 i++;
4068}
4069
4070/* Return true if the V can overlap with KILL. */
4071
4072static bool
4073ipcp_argagg_and_kill_overlap_p (const ipa_argagg_value &v,
4074 const modref_access_node &kill)
4075{
4076 if (kill.parm_index == v.index)
4077 {
4078 gcc_assert (kill.parm_offset_known);
4079 gcc_assert (known_eq (kill.max_size, kill.size));
4080 poly_int64 repl_size;
4081 bool ok = poly_int_tree_p (TYPE_SIZE (TREE_TYPE (v.value)),
4082 value: &repl_size);
4083 gcc_assert (ok);
4084 poly_int64 repl_offset (v.unit_offset);
4085 repl_offset <<= LOG2_BITS_PER_UNIT;
4086 poly_int64 combined_offset
4087 = (kill.parm_offset << LOG2_BITS_PER_UNIT) + kill.offset;
4088 if (ranges_maybe_overlap_p (pos1: repl_offset, size1: repl_size,
4089 pos2: combined_offset, size2: kill.size))
4090 return true;
4091 }
4092 return false;
4093}
4094
4095/* If signature changed, update the summary. */
4096
4097static void
4098update_signature (struct cgraph_node *node)
4099{
4100 modref_summary *r = optimization_summaries
4101 ? optimization_summaries->get (node) : NULL;
4102 modref_summary_lto *r_lto = summaries_lto
4103 ? summaries_lto->get (node) : NULL;
4104 if (!r && !r_lto)
4105 return;
4106
4107 /* Propagating constants in killed memory can lead to eliminated stores in
4108 both callees (because they are considered redundant) and callers, leading
4109 to missing them altogether. */
4110 ipcp_transformation *ipcp_ts = ipcp_get_transformation_summary (node);
4111 if (ipcp_ts)
4112 {
4113 for (auto &v : ipcp_ts->m_agg_values)
4114 {
4115 if (!v.by_ref)
4116 continue;
4117 if (r)
4118 for (const modref_access_node &kill : r->kills)
4119 if (ipcp_argagg_and_kill_overlap_p (v, kill))
4120 {
4121 v.killed = true;
4122 break;
4123 }
4124 if (!v.killed && r_lto)
4125 for (const modref_access_node &kill : r_lto->kills)
4126 if (ipcp_argagg_and_kill_overlap_p (v, kill))
4127 {
4128 v.killed = true;
4129 break;
4130 }
4131 }
4132 }
4133
4134 clone_info *info = clone_info::get (node);
4135 if (!info || !info->param_adjustments)
4136 return;
4137
4138 if (dump_file)
4139 {
4140 fprintf (stream: dump_file, format: "Updating summary for %s from:\n",
4141 node->dump_name ());
4142 if (r)
4143 r->dump (out: dump_file);
4144 if (r_lto)
4145 r_lto->dump (out: dump_file);
4146 }
4147
4148 size_t i, max = 0;
4149 ipa_adjusted_param *p;
4150
4151 FOR_EACH_VEC_SAFE_ELT (info->param_adjustments->m_adj_params, i, p)
4152 {
4153 int idx = info->param_adjustments->get_original_index (newidx: i);
4154 if (idx > (int)max)
4155 max = idx;
4156 }
4157
4158 auto_vec <int, 32> map;
4159
4160 map.reserve (nelems: max + 1);
4161 for (i = 0; i <= max; i++)
4162 map.quick_push (obj: MODREF_UNKNOWN_PARM);
4163 FOR_EACH_VEC_SAFE_ELT (info->param_adjustments->m_adj_params, i, p)
4164 {
4165 int idx = info->param_adjustments->get_original_index (newidx: i);
4166 if (idx >= 0)
4167 map[idx] = i;
4168 }
4169 if (r)
4170 {
4171 r->loads->remap_params (map: &map);
4172 r->stores->remap_params (map: &map);
4173 remap_kills (kills&: r->kills, map);
4174 if (r->arg_flags.length ())
4175 remap_arg_flags (arg_flags&: r->arg_flags, info);
4176 }
4177 if (r_lto)
4178 {
4179 r_lto->loads->remap_params (map: &map);
4180 r_lto->stores->remap_params (map: &map);
4181 remap_kills (kills&: r_lto->kills, map);
4182 if (r_lto->arg_flags.length ())
4183 remap_arg_flags (arg_flags&: r_lto->arg_flags, info);
4184 }
4185 if (dump_file)
4186 {
4187 fprintf (stream: dump_file, format: "to:\n");
4188 if (r)
4189 r->dump (out: dump_file);
4190 if (r_lto)
4191 r_lto->dump (out: dump_file);
4192 }
4193 if (r)
4194 r->finalize (fun: node->decl);
4195 return;
4196}
4197
4198/* Definition of the modref IPA pass. */
4199const pass_data pass_data_ipa_modref =
4200{
4201 .type: IPA_PASS, /* type */
4202 .name: "modref", /* name */
4203 .optinfo_flags: OPTGROUP_IPA, /* optinfo_flags */
4204 .tv_id: TV_IPA_MODREF, /* tv_id */
4205 .properties_required: 0, /* properties_required */
4206 .properties_provided: 0, /* properties_provided */
4207 .properties_destroyed: 0, /* properties_destroyed */
4208 .todo_flags_start: 0, /* todo_flags_start */
4209 .todo_flags_finish: ( TODO_dump_symtab ), /* todo_flags_finish */
4210};
4211
4212class pass_ipa_modref : public ipa_opt_pass_d
4213{
4214public:
4215 pass_ipa_modref (gcc::context *ctxt)
4216 : ipa_opt_pass_d (pass_data_ipa_modref, ctxt,
4217 modref_generate, /* generate_summary */
4218 modref_write, /* write_summary */
4219 modref_read, /* read_summary */
4220 modref_write, /* write_optimization_summary */
4221 modref_read, /* read_optimization_summary */
4222 NULL, /* stmt_fixup */
4223 0, /* function_transform_todo_flags_start */
4224 NULL, /* function_transform */
4225 NULL) /* variable_transform */
4226 {}
4227
4228 /* opt_pass methods: */
4229 opt_pass *clone () final override { return new pass_ipa_modref (m_ctxt); }
4230 bool gate (function *) final override
4231 {
4232 return true;
4233 }
4234 unsigned int execute (function *) final override;
4235
4236};
4237
4238}
4239
4240unsigned int pass_modref::execute (function *)
4241{
4242 if (analyze_function (ipa: false))
4243 return execute_fixup_cfg ();
4244 return 0;
4245}
4246
4247gimple_opt_pass *
4248make_pass_modref (gcc::context *ctxt)
4249{
4250 return new pass_modref (ctxt);
4251}
4252
4253ipa_opt_pass_d *
4254make_pass_ipa_modref (gcc::context *ctxt)
4255{
4256 return new pass_ipa_modref (ctxt);
4257}
4258
4259namespace {
4260
4261/* Skip edges from and to nodes without ipa_pure_const enabled.
4262 Ignore not available symbols. */
4263
4264static bool
4265ignore_edge (struct cgraph_edge *e)
4266{
4267 /* We merge summaries of inline clones into summaries of functions they
4268 are inlined to. For that reason the complete function bodies must
4269 act as unit. */
4270 if (!e->inline_failed)
4271 return false;
4272 enum availability avail;
4273 cgraph_node *callee = e->callee->ultimate_alias_target
4274 (availability: &avail, ref: e->caller);
4275
4276 return (avail <= AVAIL_INTERPOSABLE
4277 || ((!optimization_summaries || !optimization_summaries->get (node: callee))
4278 && (!summaries_lto || !summaries_lto->get (node: callee))));
4279}
4280
4281/* Compute parm_map for CALLEE_EDGE. */
4282
4283static bool
4284compute_parm_map (cgraph_edge *callee_edge, vec<modref_parm_map> *parm_map)
4285{
4286 class ipa_edge_args *args;
4287 if (ipa_node_params_sum
4288 && !callee_edge->call_stmt_cannot_inline_p
4289 && (args = ipa_edge_args_sum->get (edge: callee_edge)) != NULL)
4290 {
4291 int i, count = ipa_get_cs_argument_count (args);
4292 class ipa_node_params *caller_parms_info, *callee_pi;
4293 class ipa_call_summary *es
4294 = ipa_call_summaries->get (edge: callee_edge);
4295 cgraph_node *callee
4296 = callee_edge->callee->ultimate_alias_target
4297 (NULL, ref: callee_edge->caller);
4298
4299 caller_parms_info
4300 = ipa_node_params_sum->get (node: callee_edge->caller->inlined_to
4301 ? callee_edge->caller->inlined_to
4302 : callee_edge->caller);
4303 callee_pi = ipa_node_params_sum->get (node: callee);
4304
4305 (*parm_map).safe_grow_cleared (len: count, exact: true);
4306
4307 for (i = 0; i < count; i++)
4308 {
4309 if (es && es->param[i].points_to_local_or_readonly_memory)
4310 {
4311 (*parm_map)[i].parm_index = MODREF_LOCAL_MEMORY_PARM;
4312 continue;
4313 }
4314
4315 struct ipa_jump_func *jf
4316 = ipa_get_ith_jump_func (args, i);
4317 if (jf && callee_pi)
4318 {
4319 tree cst = ipa_value_from_jfunc (info: caller_parms_info,
4320 jfunc: jf,
4321 type: ipa_get_type
4322 (info: callee_pi, i));
4323 if (cst && points_to_local_or_readonly_memory_p (cst))
4324 {
4325 (*parm_map)[i].parm_index = MODREF_LOCAL_MEMORY_PARM;
4326 continue;
4327 }
4328 }
4329 if (jf && jf->type == IPA_JF_PASS_THROUGH)
4330 {
4331 (*parm_map)[i].parm_index
4332 = ipa_get_jf_pass_through_formal_id (jfunc: jf);
4333 if (ipa_get_jf_pass_through_operation (jfunc: jf) == NOP_EXPR)
4334 {
4335 (*parm_map)[i].parm_offset_known = true;
4336 (*parm_map)[i].parm_offset = 0;
4337 }
4338 else if (ipa_get_jf_pass_through_operation (jfunc: jf)
4339 == POINTER_PLUS_EXPR
4340 && ptrdiff_tree_p (ipa_get_jf_pass_through_operand (jfunc: jf),
4341 &(*parm_map)[i].parm_offset))
4342 (*parm_map)[i].parm_offset_known = true;
4343 else
4344 (*parm_map)[i].parm_offset_known = false;
4345 continue;
4346 }
4347 if (jf && jf->type == IPA_JF_ANCESTOR)
4348 {
4349 (*parm_map)[i].parm_index = ipa_get_jf_ancestor_formal_id (jfunc: jf);
4350 (*parm_map)[i].parm_offset_known = true;
4351 gcc_checking_assert
4352 (!(ipa_get_jf_ancestor_offset (jf) & (BITS_PER_UNIT - 1)));
4353 (*parm_map)[i].parm_offset
4354 = ipa_get_jf_ancestor_offset (jfunc: jf) >> LOG2_BITS_PER_UNIT;
4355 }
4356 else
4357 (*parm_map)[i].parm_index = -1;
4358 }
4359 if (dump_file)
4360 {
4361 fprintf (stream: dump_file, format: " Parm map: ");
4362 for (i = 0; i < count; i++)
4363 fprintf (stream: dump_file, format: " %i", (*parm_map)[i].parm_index);
4364 fprintf (stream: dump_file, format: "\n");
4365 }
4366 return true;
4367 }
4368 return false;
4369}
4370
4371/* Map used to translate escape infos. */
4372
4373struct escape_map
4374{
4375 int parm_index;
4376 bool direct;
4377};
4378
4379/* Update escape map for E. */
4380
4381static void
4382update_escape_summary_1 (cgraph_edge *e,
4383 vec <vec <escape_map>> &map,
4384 bool ignore_stores)
4385{
4386 escape_summary *sum = escape_summaries->get (edge: e);
4387 if (!sum)
4388 return;
4389 auto_vec <escape_entry> old = sum->esc.copy ();
4390 sum->esc.release ();
4391
4392 unsigned int i;
4393 escape_entry *ee;
4394 FOR_EACH_VEC_ELT (old, i, ee)
4395 {
4396 unsigned int j;
4397 struct escape_map *em;
4398 /* TODO: We do not have jump functions for return slots, so we
4399 never propagate them to outer function. */
4400 if (ee->parm_index >= (int)map.length ()
4401 || ee->parm_index < 0)
4402 continue;
4403 FOR_EACH_VEC_ELT (map[ee->parm_index], j, em)
4404 {
4405 int min_flags = ee->min_flags;
4406 if (ee->direct && !em->direct)
4407 min_flags = deref_flags (flags: min_flags, ignore_stores);
4408 struct escape_entry entry = {.parm_index: em->parm_index, .arg: ee->arg,
4409 .min_flags: min_flags,
4410 .direct: ee->direct & em->direct};
4411 sum->esc.safe_push (obj: entry);
4412 }
4413 }
4414 if (!sum->esc.length ())
4415 escape_summaries->remove (edge: e);
4416}
4417
4418/* Update escape map for NODE. */
4419
4420static void
4421update_escape_summary (cgraph_node *node,
4422 vec <vec <escape_map>> &map,
4423 bool ignore_stores)
4424{
4425 if (!escape_summaries)
4426 return;
4427 for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
4428 update_escape_summary_1 (e, map, ignore_stores);
4429 for (cgraph_edge *e = node->callees; e; e = e->next_callee)
4430 {
4431 if (!e->inline_failed)
4432 update_escape_summary (node: e->callee, map, ignore_stores);
4433 else
4434 update_escape_summary_1 (e, map, ignore_stores);
4435 }
4436}
4437
4438/* Get parameter type from DECL. This is only safe for special cases
4439 like builtins we create fnspec for because the type match is checked
4440 at fnspec creation time. */
4441
4442static tree
4443get_parm_type (tree decl, unsigned int i)
4444{
4445 tree t = TYPE_ARG_TYPES (TREE_TYPE (decl));
4446
4447 for (unsigned int p = 0; p < i; p++)
4448 t = TREE_CHAIN (t);
4449 return TREE_VALUE (t);
4450}
4451
4452/* Return access mode for argument I of call E with FNSPEC. */
4453
4454static modref_access_node
4455get_access_for_fnspec (cgraph_edge *e, attr_fnspec &fnspec,
4456 unsigned int i, modref_parm_map &map)
4457{
4458 tree size = NULL_TREE;
4459 unsigned int size_arg;
4460
4461 if (!fnspec.arg_specified_p (i))
4462 ;
4463 else if (fnspec.arg_max_access_size_given_by_arg_p (i, arg: &size_arg))
4464 {
4465 cgraph_node *node = e->caller->inlined_to
4466 ? e->caller->inlined_to : e->caller;
4467 ipa_node_params *caller_parms_info = ipa_node_params_sum->get (node);
4468 ipa_edge_args *args = ipa_edge_args_sum->get (edge: e);
4469 struct ipa_jump_func *jf = ipa_get_ith_jump_func (args, i: size_arg);
4470
4471 if (jf)
4472 size = ipa_value_from_jfunc (info: caller_parms_info, jfunc: jf,
4473 type: get_parm_type (decl: e->callee->decl, i: size_arg));
4474 }
4475 else if (fnspec.arg_access_size_given_by_type_p (i))
4476 size = TYPE_SIZE_UNIT (get_parm_type (e->callee->decl, i));
4477 modref_access_node a = {.offset: 0, .size: -1, .max_size: -1,
4478 .parm_offset: map.parm_offset, .parm_index: map.parm_index,
4479 .parm_offset_known: map.parm_offset_known, .adjustments: 0};
4480 poly_int64 size_hwi;
4481 if (size
4482 && poly_int_tree_p (t: size, value: &size_hwi)
4483 && coeffs_in_range_p (a: size_hwi, b: 0,
4484 HOST_WIDE_INT_MAX / BITS_PER_UNIT))
4485 {
4486 a.size = -1;
4487 a.max_size = size_hwi << LOG2_BITS_PER_UNIT;
4488 }
4489 return a;
4490}
4491
4492 /* Collapse loads and return true if something changed. */
4493static bool
4494collapse_loads (modref_summary *cur_summary,
4495 modref_summary_lto *cur_summary_lto)
4496{
4497 bool changed = false;
4498
4499 if (cur_summary && !cur_summary->loads->every_base)
4500 {
4501 cur_summary->loads->collapse ();
4502 changed = true;
4503 }
4504 if (cur_summary_lto
4505 && !cur_summary_lto->loads->every_base)
4506 {
4507 cur_summary_lto->loads->collapse ();
4508 changed = true;
4509 }
4510 return changed;
4511}
4512
4513/* Collapse loads and return true if something changed. */
4514
4515static bool
4516collapse_stores (modref_summary *cur_summary,
4517 modref_summary_lto *cur_summary_lto)
4518{
4519 bool changed = false;
4520
4521 if (cur_summary && !cur_summary->stores->every_base)
4522 {
4523 cur_summary->stores->collapse ();
4524 changed = true;
4525 }
4526 if (cur_summary_lto
4527 && !cur_summary_lto->stores->every_base)
4528 {
4529 cur_summary_lto->stores->collapse ();
4530 changed = true;
4531 }
4532 return changed;
4533}
4534
4535/* Call E in NODE with ECF_FLAGS has no summary; update MODREF_SUMMARY and
4536 CUR_SUMMARY_LTO accordingly. Return true if something changed. */
4537
4538static bool
4539propagate_unknown_call (cgraph_node *node,
4540 cgraph_edge *e, int ecf_flags,
4541 modref_summary *cur_summary,
4542 modref_summary_lto *cur_summary_lto,
4543 bool nontrivial_scc)
4544{
4545 bool changed = false;
4546 class fnspec_summary *fnspec_sum = fnspec_summaries->get (edge: e);
4547 auto_vec <modref_parm_map, 32> parm_map;
4548 bool looping;
4549
4550 if (e->callee
4551 && builtin_safe_for_const_function_p (&looping, e->callee->decl))
4552 {
4553 if (looping && cur_summary && !cur_summary->side_effects)
4554 {
4555 cur_summary->side_effects = true;
4556 changed = true;
4557 }
4558 if (looping && cur_summary_lto && !cur_summary_lto->side_effects)
4559 {
4560 cur_summary_lto->side_effects = true;
4561 changed = true;
4562 }
4563 return changed;
4564 }
4565
4566 if (!(ecf_flags & (ECF_CONST | ECF_NOVOPS | ECF_PURE))
4567 || (ecf_flags & ECF_LOOPING_CONST_OR_PURE)
4568 || nontrivial_scc)
4569 {
4570 if (cur_summary && !cur_summary->side_effects)
4571 {
4572 cur_summary->side_effects = true;
4573 changed = true;
4574 }
4575 if (cur_summary_lto && !cur_summary_lto->side_effects)
4576 {
4577 cur_summary_lto->side_effects = true;
4578 changed = true;
4579 }
4580 if (cur_summary && !cur_summary->nondeterministic
4581 && !ignore_nondeterminism_p (caller: node->decl, flags: ecf_flags))
4582 {
4583 cur_summary->nondeterministic = true;
4584 changed = true;
4585 }
4586 if (cur_summary_lto && !cur_summary_lto->nondeterministic
4587 && !ignore_nondeterminism_p (caller: node->decl, flags: ecf_flags))
4588 {
4589 cur_summary_lto->nondeterministic = true;
4590 changed = true;
4591 }
4592 }
4593 if (ecf_flags & (ECF_CONST | ECF_NOVOPS))
4594 return changed;
4595
4596 if (fnspec_sum
4597 && compute_parm_map (callee_edge: e, parm_map: &parm_map))
4598 {
4599 attr_fnspec fnspec (fnspec_sum->fnspec);
4600
4601 gcc_checking_assert (fnspec.known_p ());
4602 if (fnspec.global_memory_read_p ())
4603 collapse_loads (cur_summary, cur_summary_lto);
4604 else
4605 {
4606 tree t = TYPE_ARG_TYPES (TREE_TYPE (e->callee->decl));
4607 for (unsigned i = 0; i < parm_map.length () && t;
4608 i++, t = TREE_CHAIN (t))
4609 if (!POINTER_TYPE_P (TREE_VALUE (t)))
4610 ;
4611 else if (!fnspec.arg_specified_p (i)
4612 || fnspec.arg_maybe_read_p (i))
4613 {
4614 modref_parm_map map = parm_map[i];
4615 if (map.parm_index == MODREF_LOCAL_MEMORY_PARM)
4616 continue;
4617 if (map.parm_index == MODREF_UNKNOWN_PARM)
4618 {
4619 collapse_loads (cur_summary, cur_summary_lto);
4620 break;
4621 }
4622 if (cur_summary)
4623 changed |= cur_summary->loads->insert
4624 (fndecl: node->decl, base: 0, ref: 0,
4625 a: get_access_for_fnspec (e, fnspec, i, map), record_adjustments: false);
4626 if (cur_summary_lto)
4627 changed |= cur_summary_lto->loads->insert
4628 (fndecl: node->decl, base: 0, ref: 0,
4629 a: get_access_for_fnspec (e, fnspec, i, map), record_adjustments: false);
4630 }
4631 }
4632 if (ignore_stores_p (caller: node->decl, flags: ecf_flags))
4633 ;
4634 else if (fnspec.global_memory_written_p ())
4635 collapse_stores (cur_summary, cur_summary_lto);
4636 else
4637 {
4638 tree t = TYPE_ARG_TYPES (TREE_TYPE (e->callee->decl));
4639 for (unsigned i = 0; i < parm_map.length () && t;
4640 i++, t = TREE_CHAIN (t))
4641 if (!POINTER_TYPE_P (TREE_VALUE (t)))
4642 ;
4643 else if (!fnspec.arg_specified_p (i)
4644 || fnspec.arg_maybe_written_p (i))
4645 {
4646 modref_parm_map map = parm_map[i];
4647 if (map.parm_index == MODREF_LOCAL_MEMORY_PARM)
4648 continue;
4649 if (map.parm_index == MODREF_UNKNOWN_PARM)
4650 {
4651 collapse_stores (cur_summary, cur_summary_lto);
4652 break;
4653 }
4654 if (cur_summary)
4655 changed |= cur_summary->stores->insert
4656 (fndecl: node->decl, base: 0, ref: 0,
4657 a: get_access_for_fnspec (e, fnspec, i, map), record_adjustments: false);
4658 if (cur_summary_lto)
4659 changed |= cur_summary_lto->stores->insert
4660 (fndecl: node->decl, base: 0, ref: 0,
4661 a: get_access_for_fnspec (e, fnspec, i, map), record_adjustments: false);
4662 }
4663 }
4664 if (fnspec.errno_maybe_written_p () && flag_errno_math)
4665 {
4666 if (cur_summary && !cur_summary->writes_errno)
4667 {
4668 cur_summary->writes_errno = true;
4669 changed = true;
4670 }
4671 if (cur_summary_lto && !cur_summary_lto->writes_errno)
4672 {
4673 cur_summary_lto->writes_errno = true;
4674 changed = true;
4675 }
4676 }
4677 return changed;
4678 }
4679 if (dump_file)
4680 fprintf (stream: dump_file, format: " collapsing loads\n");
4681 changed |= collapse_loads (cur_summary, cur_summary_lto);
4682 if (!ignore_stores_p (caller: node->decl, flags: ecf_flags))
4683 {
4684 if (dump_file)
4685 fprintf (stream: dump_file, format: " collapsing stores\n");
4686 changed |= collapse_stores (cur_summary, cur_summary_lto);
4687 }
4688 return changed;
4689}
4690
4691/* Maybe remove summaries of NODE pointed to by CUR_SUMMARY_PTR
4692 and CUR_SUMMARY_LTO_PTR if they are useless according to ECF_FLAGS. */
4693
4694static void
4695remove_useless_summaries (cgraph_node *node,
4696 modref_summary **cur_summary_ptr,
4697 modref_summary_lto **cur_summary_lto_ptr,
4698 int ecf_flags)
4699{
4700 if (*cur_summary_ptr && !(*cur_summary_ptr)->useful_p (ecf_flags, check_flags: false))
4701 {
4702 optimization_summaries->remove (node);
4703 *cur_summary_ptr = NULL;
4704 }
4705 if (*cur_summary_lto_ptr
4706 && !(*cur_summary_lto_ptr)->useful_p (ecf_flags, check_flags: false))
4707 {
4708 summaries_lto->remove (node);
4709 *cur_summary_lto_ptr = NULL;
4710 }
4711}
4712
4713/* Perform iterative dataflow on SCC component starting in COMPONENT_NODE
4714 and propagate loads/stores. */
4715
4716static bool
4717modref_propagate_in_scc (cgraph_node *component_node)
4718{
4719 bool changed = true;
4720 bool first = true;
4721 int iteration = 0;
4722
4723 while (changed)
4724 {
4725 bool nontrivial_scc
4726 = ((struct ipa_dfs_info *) component_node->aux)->next_cycle;
4727 changed = false;
4728 for (struct cgraph_node *cur = component_node; cur;
4729 cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
4730 {
4731 cgraph_node *node = cur->inlined_to ? cur->inlined_to : cur;
4732 modref_summary *cur_summary = optimization_summaries
4733 ? optimization_summaries->get (node)
4734 : NULL;
4735 modref_summary_lto *cur_summary_lto = summaries_lto
4736 ? summaries_lto->get (node)
4737 : NULL;
4738
4739 if (!cur_summary && !cur_summary_lto)
4740 continue;
4741
4742 int cur_ecf_flags = flags_from_decl_or_type (node->decl);
4743
4744 if (dump_file)
4745 fprintf (stream: dump_file, format: " Processing %s%s%s\n",
4746 cur->dump_name (),
4747 TREE_READONLY (cur->decl) ? " (const)" : "",
4748 DECL_PURE_P (cur->decl) ? " (pure)" : "");
4749
4750 for (cgraph_edge *e = cur->indirect_calls; e; e = e->next_callee)
4751 {
4752 if (dump_file)
4753 fprintf (stream: dump_file, format: " Indirect call\n");
4754 if (propagate_unknown_call
4755 (node, e, ecf_flags: e->indirect_info->ecf_flags,
4756 cur_summary, cur_summary_lto,
4757 nontrivial_scc))
4758 {
4759 changed = true;
4760 remove_useless_summaries (node, cur_summary_ptr: &cur_summary,
4761 cur_summary_lto_ptr: &cur_summary_lto,
4762 ecf_flags: cur_ecf_flags);
4763 if (!cur_summary && !cur_summary_lto)
4764 break;
4765 }
4766 }
4767
4768 if (!cur_summary && !cur_summary_lto)
4769 continue;
4770
4771 for (cgraph_edge *callee_edge = cur->callees; callee_edge;
4772 callee_edge = callee_edge->next_callee)
4773 {
4774 int flags = flags_from_decl_or_type (callee_edge->callee->decl);
4775 modref_summary *callee_summary = NULL;
4776 modref_summary_lto *callee_summary_lto = NULL;
4777 struct cgraph_node *callee;
4778
4779 if (!callee_edge->inline_failed
4780 || ((flags & (ECF_CONST | ECF_NOVOPS))
4781 && !(flags & ECF_LOOPING_CONST_OR_PURE)))
4782 continue;
4783
4784 /* Get the callee and its summary. */
4785 enum availability avail;
4786 callee = callee_edge->callee->ultimate_alias_target
4787 (availability: &avail, ref: cur);
4788
4789 /* It is not necessary to re-process calls outside of the
4790 SCC component. */
4791 if (iteration > 0
4792 && (!callee->aux
4793 || ((struct ipa_dfs_info *)cur->aux)->scc_no
4794 != ((struct ipa_dfs_info *)callee->aux)->scc_no))
4795 continue;
4796
4797 if (dump_file)
4798 fprintf (stream: dump_file, format: " Call to %s\n",
4799 callee_edge->callee->dump_name ());
4800
4801 bool ignore_stores = ignore_stores_p (caller: cur->decl, flags);
4802
4803 if (avail <= AVAIL_INTERPOSABLE)
4804 {
4805 if (dump_file)
4806 fprintf (stream: dump_file, format: " Call target interposable"
4807 " or not available\n");
4808 changed |= propagate_unknown_call
4809 (node, e: callee_edge, ecf_flags: flags,
4810 cur_summary, cur_summary_lto,
4811 nontrivial_scc);
4812 if (!cur_summary && !cur_summary_lto)
4813 break;
4814 continue;
4815 }
4816
4817 /* We don't know anything about CALLEE, hence we cannot tell
4818 anything about the entire component. */
4819
4820 if (cur_summary
4821 && !(callee_summary = optimization_summaries->get (node: callee)))
4822 {
4823 if (dump_file)
4824 fprintf (stream: dump_file, format: " No call target summary\n");
4825 changed |= propagate_unknown_call
4826 (node, e: callee_edge, ecf_flags: flags,
4827 cur_summary, NULL,
4828 nontrivial_scc);
4829 }
4830 if (cur_summary_lto
4831 && !(callee_summary_lto = summaries_lto->get (node: callee)))
4832 {
4833 if (dump_file)
4834 fprintf (stream: dump_file, format: " No call target summary\n");
4835 changed |= propagate_unknown_call
4836 (node, e: callee_edge, ecf_flags: flags,
4837 NULL, cur_summary_lto,
4838 nontrivial_scc);
4839 }
4840
4841 if (callee_summary && !cur_summary->side_effects
4842 && (callee_summary->side_effects
4843 || callee_edge->recursive_p ()))
4844 {
4845 cur_summary->side_effects = true;
4846 changed = true;
4847 }
4848 if (callee_summary_lto && !cur_summary_lto->side_effects
4849 && (callee_summary_lto->side_effects
4850 || callee_edge->recursive_p ()))
4851 {
4852 cur_summary_lto->side_effects = true;
4853 changed = true;
4854 }
4855 if (callee_summary && !cur_summary->nondeterministic
4856 && callee_summary->nondeterministic
4857 && !ignore_nondeterminism_p (caller: cur->decl, flags))
4858 {
4859 cur_summary->nondeterministic = true;
4860 changed = true;
4861 }
4862 if (callee_summary_lto && !cur_summary_lto->nondeterministic
4863 && callee_summary_lto->nondeterministic
4864 && !ignore_nondeterminism_p (caller: cur->decl, flags))
4865 {
4866 cur_summary_lto->nondeterministic = true;
4867 changed = true;
4868 }
4869 if (flags & (ECF_CONST | ECF_NOVOPS))
4870 continue;
4871
4872 /* We can not safely optimize based on summary of callee if it
4873 does not always bind to current def: it is possible that
4874 memory load was optimized out earlier which may not happen in
4875 the interposed variant. */
4876 if (!callee_edge->binds_to_current_def_p ())
4877 {
4878 if (cur_summary && !cur_summary->calls_interposable)
4879 {
4880 cur_summary->calls_interposable = true;
4881 changed = true;
4882 }
4883 if (cur_summary_lto && !cur_summary_lto->calls_interposable)
4884 {
4885 cur_summary_lto->calls_interposable = true;
4886 changed = true;
4887 }
4888 if (dump_file)
4889 fprintf (stream: dump_file, format: " May not bind local;"
4890 " collapsing loads\n");
4891 }
4892
4893
4894 auto_vec <modref_parm_map, 32> parm_map;
4895 modref_parm_map chain_map;
4896 /* TODO: Once we get jump functions for static chains we could
4897 compute this. */
4898 chain_map.parm_index = MODREF_UNKNOWN_PARM;
4899
4900 compute_parm_map (callee_edge, parm_map: &parm_map);
4901
4902 /* Merge in callee's information. */
4903 if (callee_summary)
4904 {
4905 changed |= cur_summary->loads->merge
4906 (fndecl: node->decl, other: callee_summary->loads,
4907 parm_map: &parm_map, static_chain_map: &chain_map, record_accesses: !first);
4908 if (!ignore_stores)
4909 {
4910 changed |= cur_summary->stores->merge
4911 (fndecl: node->decl, other: callee_summary->stores,
4912 parm_map: &parm_map, static_chain_map: &chain_map, record_accesses: !first);
4913 if (!cur_summary->writes_errno
4914 && callee_summary->writes_errno)
4915 {
4916 cur_summary->writes_errno = true;
4917 changed = true;
4918 }
4919 }
4920 }
4921 if (callee_summary_lto)
4922 {
4923 changed |= cur_summary_lto->loads->merge
4924 (fndecl: node->decl, other: callee_summary_lto->loads,
4925 parm_map: &parm_map, static_chain_map: &chain_map, record_accesses: !first);
4926 if (!ignore_stores)
4927 {
4928 changed |= cur_summary_lto->stores->merge
4929 (fndecl: node->decl, other: callee_summary_lto->stores,
4930 parm_map: &parm_map, static_chain_map: &chain_map, record_accesses: !first);
4931 if (!cur_summary_lto->writes_errno
4932 && callee_summary_lto->writes_errno)
4933 {
4934 cur_summary_lto->writes_errno = true;
4935 changed = true;
4936 }
4937 }
4938 }
4939 if (changed)
4940 remove_useless_summaries (node, cur_summary_ptr: &cur_summary,
4941 cur_summary_lto_ptr: &cur_summary_lto,
4942 ecf_flags: cur_ecf_flags);
4943 if (!cur_summary && !cur_summary_lto)
4944 break;
4945 if (dump_file && changed)
4946 {
4947 if (cur_summary)
4948 cur_summary->dump (out: dump_file);
4949 if (cur_summary_lto)
4950 cur_summary_lto->dump (out: dump_file);
4951 dump_modref_edge_summaries (out: dump_file, node, depth: 4);
4952 }
4953 }
4954 }
4955 iteration++;
4956 first = false;
4957 }
4958 if (dump_file)
4959 fprintf (stream: dump_file,
4960 format: "Propagation finished in %i iterations\n", iteration);
4961 bool pureconst = false;
4962 for (struct cgraph_node *cur = component_node; cur;
4963 cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
4964 if (!cur->inlined_to && opt_for_fn (cur->decl, flag_ipa_pure_const))
4965 {
4966 modref_summary *summary = optimization_summaries
4967 ? optimization_summaries->get (node: cur)
4968 : NULL;
4969 modref_summary_lto *summary_lto = summaries_lto
4970 ? summaries_lto->get (node: cur)
4971 : NULL;
4972 if (summary && !summary->stores->every_base && !summary->stores->bases
4973 && !summary->nondeterministic)
4974 {
4975 if (!summary->loads->every_base && !summary->loads->bases
4976 && !summary->calls_interposable)
4977 pureconst |= ipa_make_function_const
4978 (cur, summary->side_effects, false);
4979 else
4980 pureconst |= ipa_make_function_pure
4981 (cur, summary->side_effects, false);
4982 }
4983 if (summary_lto && !summary_lto->stores->every_base
4984 && !summary_lto->stores->bases && !summary_lto->nondeterministic)
4985 {
4986 if (!summary_lto->loads->every_base && !summary_lto->loads->bases
4987 && !summary_lto->calls_interposable)
4988 pureconst |= ipa_make_function_const
4989 (cur, summary_lto->side_effects, false);
4990 else
4991 pureconst |= ipa_make_function_pure
4992 (cur, summary_lto->side_effects, false);
4993 }
4994 }
4995 return pureconst;
4996}
4997
4998/* Dump results of propagation in SCC rooted in COMPONENT_NODE. */
4999
5000static void
5001modref_propagate_dump_scc (cgraph_node *component_node)
5002{
5003 for (struct cgraph_node *cur = component_node; cur;
5004 cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
5005 if (!cur->inlined_to)
5006 {
5007 modref_summary *cur_summary = optimization_summaries
5008 ? optimization_summaries->get (node: cur)
5009 : NULL;
5010 modref_summary_lto *cur_summary_lto = summaries_lto
5011 ? summaries_lto->get (node: cur)
5012 : NULL;
5013
5014 fprintf (stream: dump_file, format: "Propagated modref for %s%s%s\n",
5015 cur->dump_name (),
5016 TREE_READONLY (cur->decl) ? " (const)" : "",
5017 DECL_PURE_P (cur->decl) ? " (pure)" : "");
5018 if (optimization_summaries)
5019 {
5020 if (cur_summary)
5021 cur_summary->dump (out: dump_file);
5022 else
5023 fprintf (stream: dump_file, format: " Not tracked\n");
5024 }
5025 if (summaries_lto)
5026 {
5027 if (cur_summary_lto)
5028 cur_summary_lto->dump (out: dump_file);
5029 else
5030 fprintf (stream: dump_file, format: " Not tracked (lto)\n");
5031 }
5032 }
5033}
5034
5035/* Determine EAF flags know for call E with CALLEE_ECF_FLAGS and ARG. */
5036
5037int
5038implicit_eaf_flags_for_edge_and_arg (cgraph_edge *e, int callee_ecf_flags,
5039 bool ignore_stores, int arg)
5040{
5041 /* Returning the value is already accounted to at local propagation. */
5042 int implicit_flags = EAF_NOT_RETURNED_DIRECTLY
5043 | EAF_NOT_RETURNED_INDIRECTLY;
5044 if (ignore_stores)
5045 implicit_flags |= ignore_stores_eaf_flags;
5046 if (callee_ecf_flags & ECF_PURE)
5047 implicit_flags |= implicit_pure_eaf_flags;
5048 if (callee_ecf_flags & (ECF_CONST | ECF_NOVOPS))
5049 implicit_flags |= implicit_const_eaf_flags;
5050 class fnspec_summary *fnspec_sum = fnspec_summaries->get (edge: e);
5051 if (fnspec_sum)
5052 {
5053 attr_fnspec fnspec (fnspec_sum->fnspec);
5054 implicit_flags |= fnspec.arg_eaf_flags (i: arg);
5055 }
5056 return implicit_flags;
5057}
5058
5059/* Process escapes in SUM and merge SUMMARY to CUR_SUMMARY
5060 and SUMMARY_LTO to CUR_SUMMARY_LTO.
5061 Return true if something changed. */
5062
5063static bool
5064modref_merge_call_site_flags (escape_summary *sum,
5065 modref_summary *cur_summary,
5066 modref_summary_lto *cur_summary_lto,
5067 modref_summary *summary,
5068 modref_summary_lto *summary_lto,
5069 tree caller,
5070 cgraph_edge *e,
5071 int caller_ecf_flags,
5072 int callee_ecf_flags,
5073 bool binds_to_current_def)
5074{
5075 escape_entry *ee;
5076 unsigned int i;
5077 bool changed = false;
5078 bool ignore_stores = ignore_stores_p (caller, flags: callee_ecf_flags);
5079
5080 /* Return early if we have no useful info to propagate. */
5081 if ((!cur_summary
5082 || (!cur_summary->arg_flags.length ()
5083 && !cur_summary->static_chain_flags
5084 && !cur_summary->retslot_flags))
5085 && (!cur_summary_lto
5086 || (!cur_summary_lto->arg_flags.length ()
5087 && !cur_summary_lto->static_chain_flags
5088 && !cur_summary_lto->retslot_flags)))
5089 return false;
5090
5091 FOR_EACH_VEC_ELT (sum->esc, i, ee)
5092 {
5093 int flags = 0;
5094 int flags_lto = 0;
5095 int implicit_flags = implicit_eaf_flags_for_edge_and_arg
5096 (e, callee_ecf_flags, ignore_stores, arg: ee->arg);
5097
5098 if (summary && ee->arg < summary->arg_flags.length ())
5099 flags = summary->arg_flags[ee->arg];
5100 if (summary_lto
5101 && ee->arg < summary_lto->arg_flags.length ())
5102 flags_lto = summary_lto->arg_flags[ee->arg];
5103 if (!ee->direct)
5104 {
5105 flags = deref_flags (flags, ignore_stores);
5106 flags_lto = deref_flags (flags: flags_lto, ignore_stores);
5107 }
5108 if (ignore_stores)
5109 implicit_flags |= ignore_stores_eaf_flags;
5110 if (callee_ecf_flags & ECF_PURE)
5111 implicit_flags |= implicit_pure_eaf_flags;
5112 if (callee_ecf_flags & (ECF_CONST | ECF_NOVOPS))
5113 implicit_flags |= implicit_const_eaf_flags;
5114 class fnspec_summary *fnspec_sum = fnspec_summaries->get (edge: e);
5115 if (fnspec_sum)
5116 {
5117 attr_fnspec fnspec (fnspec_sum->fnspec);
5118 implicit_flags |= fnspec.arg_eaf_flags (i: ee->arg);
5119 }
5120 if (!ee->direct)
5121 implicit_flags = deref_flags (flags: implicit_flags, ignore_stores);
5122 flags |= implicit_flags;
5123 flags_lto |= implicit_flags;
5124 if (!binds_to_current_def && (flags || flags_lto))
5125 {
5126 flags = interposable_eaf_flags (modref_flags: flags, flags: implicit_flags);
5127 flags_lto = interposable_eaf_flags (modref_flags: flags_lto, flags: implicit_flags);
5128 }
5129 if (!(flags & EAF_UNUSED)
5130 && cur_summary && ee->parm_index < (int)cur_summary->arg_flags.length ())
5131 {
5132 eaf_flags_t &f = ee->parm_index == MODREF_RETSLOT_PARM
5133 ? cur_summary->retslot_flags
5134 : ee->parm_index == MODREF_STATIC_CHAIN_PARM
5135 ? cur_summary->static_chain_flags
5136 : cur_summary->arg_flags[ee->parm_index];
5137 if ((f & flags) != f)
5138 {
5139 f = remove_useless_eaf_flags
5140 (eaf_flags: f & flags, ecf_flags: caller_ecf_flags,
5141 VOID_TYPE_P (TREE_TYPE (TREE_TYPE (caller))));
5142 changed = true;
5143 }
5144 }
5145 if (!(flags_lto & EAF_UNUSED)
5146 && cur_summary_lto
5147 && ee->parm_index < (int)cur_summary_lto->arg_flags.length ())
5148 {
5149 eaf_flags_t &f = ee->parm_index == MODREF_RETSLOT_PARM
5150 ? cur_summary_lto->retslot_flags
5151 : ee->parm_index == MODREF_STATIC_CHAIN_PARM
5152 ? cur_summary_lto->static_chain_flags
5153 : cur_summary_lto->arg_flags[ee->parm_index];
5154 if ((f & flags_lto) != f)
5155 {
5156 f = remove_useless_eaf_flags
5157 (eaf_flags: f & flags_lto, ecf_flags: caller_ecf_flags,
5158 VOID_TYPE_P (TREE_TYPE (TREE_TYPE (caller))));
5159 changed = true;
5160 }
5161 }
5162 }
5163 return changed;
5164}
5165
5166/* Perform iterative dataflow on SCC component starting in COMPONENT_NODE
5167 and propagate arg flags. */
5168
5169static void
5170modref_propagate_flags_in_scc (cgraph_node *component_node)
5171{
5172 bool changed = true;
5173 int iteration = 0;
5174
5175 while (changed)
5176 {
5177 changed = false;
5178 for (struct cgraph_node *cur = component_node; cur;
5179 cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
5180 {
5181 cgraph_node *node = cur->inlined_to ? cur->inlined_to : cur;
5182 modref_summary *cur_summary = optimization_summaries
5183 ? optimization_summaries->get (node)
5184 : NULL;
5185 modref_summary_lto *cur_summary_lto = summaries_lto
5186 ? summaries_lto->get (node)
5187 : NULL;
5188
5189 if (!cur_summary && !cur_summary_lto)
5190 continue;
5191 int caller_ecf_flags = flags_from_decl_or_type (cur->decl);
5192
5193 if (dump_file)
5194 fprintf (stream: dump_file, format: " Processing %s%s%s\n",
5195 cur->dump_name (),
5196 TREE_READONLY (cur->decl) ? " (const)" : "",
5197 DECL_PURE_P (cur->decl) ? " (pure)" : "");
5198
5199 for (cgraph_edge *e = cur->indirect_calls; e; e = e->next_callee)
5200 {
5201 escape_summary *sum = escape_summaries->get (edge: e);
5202
5203 if (!sum || (e->indirect_info->ecf_flags
5204 & (ECF_CONST | ECF_NOVOPS)))
5205 continue;
5206
5207 changed |= modref_merge_call_site_flags
5208 (sum, cur_summary, cur_summary_lto,
5209 NULL, NULL,
5210 caller: node->decl,
5211 e,
5212 caller_ecf_flags,
5213 callee_ecf_flags: e->indirect_info->ecf_flags,
5214 binds_to_current_def: false);
5215 }
5216
5217 if (!cur_summary && !cur_summary_lto)
5218 continue;
5219
5220 for (cgraph_edge *callee_edge = cur->callees; callee_edge;
5221 callee_edge = callee_edge->next_callee)
5222 {
5223 int ecf_flags = flags_from_decl_or_type
5224 (callee_edge->callee->decl);
5225 modref_summary *callee_summary = NULL;
5226 modref_summary_lto *callee_summary_lto = NULL;
5227 struct cgraph_node *callee;
5228
5229 if (ecf_flags & (ECF_CONST | ECF_NOVOPS)
5230 || !callee_edge->inline_failed)
5231 continue;
5232
5233 /* Get the callee and its summary. */
5234 enum availability avail;
5235 callee = callee_edge->callee->ultimate_alias_target
5236 (availability: &avail, ref: cur);
5237
5238 /* It is not necessary to re-process calls outside of the
5239 SCC component. */
5240 if (iteration > 0
5241 && (!callee->aux
5242 || ((struct ipa_dfs_info *)cur->aux)->scc_no
5243 != ((struct ipa_dfs_info *)callee->aux)->scc_no))
5244 continue;
5245
5246 escape_summary *sum = escape_summaries->get (edge: callee_edge);
5247 if (!sum)
5248 continue;
5249
5250 if (dump_file)
5251 fprintf (stream: dump_file, format: " Call to %s\n",
5252 callee_edge->callee->dump_name ());
5253
5254 if (avail <= AVAIL_INTERPOSABLE
5255 || callee_edge->call_stmt_cannot_inline_p)
5256 ;
5257 else
5258 {
5259 if (cur_summary)
5260 callee_summary = optimization_summaries->get (node: callee);
5261 if (cur_summary_lto)
5262 callee_summary_lto = summaries_lto->get (node: callee);
5263 }
5264 changed |= modref_merge_call_site_flags
5265 (sum, cur_summary, cur_summary_lto,
5266 summary: callee_summary, summary_lto: callee_summary_lto,
5267 caller: node->decl,
5268 e: callee_edge,
5269 caller_ecf_flags,
5270 callee_ecf_flags: ecf_flags,
5271 binds_to_current_def: callee->binds_to_current_def_p ());
5272 if (dump_file && changed)
5273 {
5274 if (cur_summary)
5275 cur_summary->dump (out: dump_file);
5276 if (cur_summary_lto)
5277 cur_summary_lto->dump (out: dump_file);
5278 }
5279 }
5280 }
5281 iteration++;
5282 }
5283 if (dump_file)
5284 fprintf (stream: dump_file,
5285 format: "Propagation of flags finished in %i iterations\n", iteration);
5286}
5287
5288} /* ANON namespace. */
5289
5290/* Call EDGE was inlined; merge summary from callee to the caller. */
5291
5292void
5293ipa_merge_modref_summary_after_inlining (cgraph_edge *edge)
5294{
5295 if (!summaries && !summaries_lto)
5296 return;
5297
5298 struct cgraph_node *to = (edge->caller->inlined_to
5299 ? edge->caller->inlined_to : edge->caller);
5300 class modref_summary *to_info = summaries ? summaries->get (node: to) : NULL;
5301 class modref_summary_lto *to_info_lto = summaries_lto
5302 ? summaries_lto->get (node: to) : NULL;
5303
5304 if (!to_info && !to_info_lto)
5305 {
5306 if (summaries)
5307 summaries->remove (node: edge->callee);
5308 if (summaries_lto)
5309 summaries_lto->remove (node: edge->callee);
5310 remove_modref_edge_summaries (node: edge->callee);
5311 return;
5312 }
5313
5314 class modref_summary *callee_info = summaries ? summaries->get (node: edge->callee)
5315 : NULL;
5316 class modref_summary_lto *callee_info_lto
5317 = summaries_lto ? summaries_lto->get (node: edge->callee) : NULL;
5318 int flags = flags_from_decl_or_type (edge->callee->decl);
5319 /* Combine in outer flags. */
5320 cgraph_node *n;
5321 for (n = edge->caller; n->inlined_to; n = n->callers->caller)
5322 flags |= flags_from_decl_or_type (n->decl);
5323 flags |= flags_from_decl_or_type (n->decl);
5324 bool ignore_stores = ignore_stores_p (caller: edge->caller->decl, flags);
5325
5326 if (!callee_info && to_info)
5327 {
5328 if (!(flags & (ECF_CONST | ECF_NOVOPS)))
5329 to_info->loads->collapse ();
5330 if (!ignore_stores)
5331 to_info->stores->collapse ();
5332 }
5333 if (!callee_info_lto && to_info_lto)
5334 {
5335 if (!(flags & (ECF_CONST | ECF_NOVOPS)))
5336 to_info_lto->loads->collapse ();
5337 if (!ignore_stores)
5338 to_info_lto->stores->collapse ();
5339 }
5340 /* Merge side effects and non-determinism.
5341 PURE/CONST flags makes functions deterministic and if there is
5342 no LOOPING_CONST_OR_PURE they also have no side effects. */
5343 if (!(flags & (ECF_CONST | ECF_NOVOPS | ECF_PURE))
5344 || (flags & ECF_LOOPING_CONST_OR_PURE))
5345 {
5346 if (to_info)
5347 {
5348 if (!callee_info || callee_info->side_effects)
5349 to_info->side_effects = true;
5350 if ((!callee_info || callee_info->nondeterministic)
5351 && !ignore_nondeterminism_p (caller: edge->caller->decl, flags))
5352 to_info->nondeterministic = true;
5353 }
5354 if (to_info_lto)
5355 {
5356 if (!callee_info_lto || callee_info_lto->side_effects)
5357 to_info_lto->side_effects = true;
5358 if ((!callee_info_lto || callee_info_lto->nondeterministic)
5359 && !ignore_nondeterminism_p (caller: edge->caller->decl, flags))
5360 to_info_lto->nondeterministic = true;
5361 }
5362 }
5363 if (callee_info || callee_info_lto)
5364 {
5365 auto_vec <modref_parm_map, 32> parm_map;
5366 modref_parm_map chain_map;
5367 /* TODO: Once we get jump functions for static chains we could
5368 compute parm_index. */
5369
5370 compute_parm_map (callee_edge: edge, parm_map: &parm_map);
5371
5372 if (!ignore_stores)
5373 {
5374 if (to_info && callee_info)
5375 to_info->stores->merge (fndecl: to->decl, other: callee_info->stores, parm_map: &parm_map,
5376 static_chain_map: &chain_map, record_accesses: false);
5377 if (to_info_lto && callee_info_lto)
5378 to_info_lto->stores->merge (fndecl: to->decl, other: callee_info_lto->stores,
5379 parm_map: &parm_map, static_chain_map: &chain_map, record_accesses: false);
5380 }
5381 if (!(flags & (ECF_CONST | ECF_NOVOPS)))
5382 {
5383 if (to_info && callee_info)
5384 to_info->loads->merge (fndecl: to->decl, other: callee_info->loads, parm_map: &parm_map,
5385 static_chain_map: &chain_map, record_accesses: false);
5386 if (to_info_lto && callee_info_lto)
5387 to_info_lto->loads->merge (fndecl: to->decl, other: callee_info_lto->loads,
5388 parm_map: &parm_map, static_chain_map: &chain_map, record_accesses: false);
5389 }
5390 }
5391
5392 /* Now merge escape summaries.
5393 For every escape to the callee we need to merge callee flags
5394 and remap callee's escapes. */
5395 class escape_summary *sum = escape_summaries->get (edge);
5396 int max_escape = -1;
5397 escape_entry *ee;
5398 unsigned int i;
5399
5400 if (sum && !(flags & (ECF_CONST | ECF_NOVOPS)))
5401 FOR_EACH_VEC_ELT (sum->esc, i, ee)
5402 if ((int)ee->arg > max_escape)
5403 max_escape = ee->arg;
5404
5405 auto_vec <vec <struct escape_map>, 32> emap (max_escape + 1);
5406 emap.safe_grow (len: max_escape + 1, exact: true);
5407 for (i = 0; (int)i < max_escape + 1; i++)
5408 emap[i] = vNULL;
5409
5410 if (sum && !(flags & (ECF_CONST | ECF_NOVOPS)))
5411 FOR_EACH_VEC_ELT (sum->esc, i, ee)
5412 {
5413 bool needed = false;
5414 int implicit_flags = implicit_eaf_flags_for_edge_and_arg
5415 (e: edge, callee_ecf_flags: flags, ignore_stores,
5416 arg: ee->arg);
5417 if (!ee->direct)
5418 implicit_flags = deref_flags (flags: implicit_flags, ignore_stores);
5419 if (to_info && (int)to_info->arg_flags.length () > ee->parm_index)
5420 {
5421 int flags = callee_info
5422 && callee_info->arg_flags.length () > ee->arg
5423 ? callee_info->arg_flags[ee->arg] : 0;
5424 if (!ee->direct)
5425 flags = deref_flags (flags, ignore_stores);
5426 flags |= ee->min_flags | implicit_flags;
5427 eaf_flags_t &f = ee->parm_index == MODREF_RETSLOT_PARM
5428 ? to_info->retslot_flags
5429 : ee->parm_index == MODREF_STATIC_CHAIN_PARM
5430 ? to_info->static_chain_flags
5431 : to_info->arg_flags[ee->parm_index];
5432 f &= flags;
5433 if (f)
5434 needed = true;
5435 }
5436 if (to_info_lto
5437 && (int)to_info_lto->arg_flags.length () > ee->parm_index)
5438 {
5439 int flags = callee_info_lto
5440 && callee_info_lto->arg_flags.length () > ee->arg
5441 ? callee_info_lto->arg_flags[ee->arg] : 0;
5442 if (!ee->direct)
5443 flags = deref_flags (flags, ignore_stores);
5444 flags |= ee->min_flags | implicit_flags;
5445 eaf_flags_t &f = ee->parm_index == MODREF_RETSLOT_PARM
5446 ? to_info_lto->retslot_flags
5447 : ee->parm_index == MODREF_STATIC_CHAIN_PARM
5448 ? to_info_lto->static_chain_flags
5449 : to_info_lto->arg_flags[ee->parm_index];
5450 f &= flags;
5451 if (f)
5452 needed = true;
5453 }
5454 struct escape_map entry = {.parm_index: ee->parm_index, .direct: ee->direct};
5455 if (needed)
5456 emap[ee->arg].safe_push (obj: entry);
5457 }
5458 update_escape_summary (node: edge->callee, map&: emap, ignore_stores);
5459 for (i = 0; (int)i < max_escape + 1; i++)
5460 emap[i].release ();
5461 if (sum)
5462 escape_summaries->remove (edge);
5463
5464 if (summaries)
5465 {
5466 if (to_info && !to_info->useful_p (ecf_flags: flags))
5467 {
5468 if (dump_file)
5469 fprintf (stream: dump_file, format: "Removed mod-ref summary for %s\n",
5470 to->dump_name ());
5471 summaries->remove (node: to);
5472 to_info = NULL;
5473 }
5474 else if (to_info && dump_file)
5475 {
5476 if (dump_file)
5477 fprintf (stream: dump_file, format: "Updated mod-ref summary for %s\n",
5478 to->dump_name ());
5479 to_info->dump (out: dump_file);
5480 }
5481 if (callee_info)
5482 summaries->remove (node: edge->callee);
5483 }
5484 if (summaries_lto)
5485 {
5486 if (to_info_lto && !to_info_lto->useful_p (ecf_flags: flags))
5487 {
5488 if (dump_file)
5489 fprintf (stream: dump_file, format: "Removed mod-ref summary for %s\n",
5490 to->dump_name ());
5491 summaries_lto->remove (node: to);
5492 to_info_lto = NULL;
5493 }
5494 else if (to_info_lto && dump_file)
5495 {
5496 if (dump_file)
5497 fprintf (stream: dump_file, format: "Updated mod-ref summary for %s\n",
5498 to->dump_name ());
5499 to_info_lto->dump (out: dump_file);
5500 }
5501 if (callee_info_lto)
5502 summaries_lto->remove (node: edge->callee);
5503 }
5504 if (!to_info && !to_info_lto)
5505 remove_modref_edge_summaries (node: to);
5506 return;
5507}
5508
5509/* Run the IPA pass. This will take a function's summaries and calls and
5510 construct new summaries which represent a transitive closure. So that
5511 summary of an analyzed function contains information about the loads and
5512 stores that the function or any function that it calls does. */
5513
5514unsigned int
5515pass_ipa_modref::execute (function *)
5516{
5517 if (!summaries && !summaries_lto)
5518 return 0;
5519 bool pureconst = false;
5520
5521 if (optimization_summaries)
5522 ggc_delete (ptr: optimization_summaries);
5523 optimization_summaries = summaries;
5524 summaries = NULL;
5525
5526 struct cgraph_node **order = XCNEWVEC (struct cgraph_node *,
5527 symtab->cgraph_count);
5528 int order_pos;
5529 order_pos = ipa_reduced_postorder (order, true, ignore_edge);
5530 int i;
5531
5532 /* Iterate over all strongly connected components in post-order. */
5533 for (i = 0; i < order_pos; i++)
5534 {
5535 /* Get the component's representative. That's just any node in the
5536 component from which we can traverse the entire component. */
5537 struct cgraph_node *component_node = order[i];
5538
5539 if (dump_file)
5540 fprintf (stream: dump_file, format: "\n\nStart of SCC component\n");
5541
5542 pureconst |= modref_propagate_in_scc (component_node);
5543 modref_propagate_flags_in_scc (component_node);
5544 if (optimization_summaries)
5545 for (struct cgraph_node *cur = component_node; cur;
5546 cur = ((struct ipa_dfs_info *) cur->aux)->next_cycle)
5547 if (modref_summary *sum = optimization_summaries->get (node: cur))
5548 sum->finalize (fun: cur->decl);
5549 if (dump_file)
5550 modref_propagate_dump_scc (component_node);
5551 }
5552 cgraph_node *node;
5553 FOR_EACH_FUNCTION (node)
5554 update_signature (node);
5555 if (summaries_lto)
5556 ((modref_summaries_lto *)summaries_lto)->propagated = true;
5557 ipa_free_postorder_info ();
5558 free (ptr: order);
5559 delete fnspec_summaries;
5560 fnspec_summaries = NULL;
5561 delete escape_summaries;
5562 escape_summaries = NULL;
5563
5564 /* If we possibly made constructors const/pure we may need to remove
5565 them. */
5566 return pureconst ? TODO_remove_functions : 0;
5567}
5568
5569/* Summaries must stay alive until end of compilation. */
5570
5571void
5572ipa_modref_cc_finalize ()
5573{
5574 if (optimization_summaries)
5575 ggc_delete (ptr: optimization_summaries);
5576 optimization_summaries = NULL;
5577 if (summaries_lto)
5578 ggc_delete (ptr: summaries_lto);
5579 summaries_lto = NULL;
5580 if (fnspec_summaries)
5581 delete fnspec_summaries;
5582 fnspec_summaries = NULL;
5583 if (escape_summaries)
5584 delete escape_summaries;
5585 escape_summaries = NULL;
5586}
5587
5588#include "gt-ipa-modref.h"
5589

source code of gcc/ipa-modref.cc