1 /* Data structure for the modref pass.
2 Copyright (C) 2020-2021 Free Software Foundation, Inc.
3 Contributed by David Cepelik and Jan Hubicka
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
21 /* modref_tree represent a decision tree that can be used by alias analysis
22 oracle to determine whether given memory access can be affected by a function
23 call. For every function we collect two trees, one for loads and other
24 for stores. Tree consist of following levels:
26 1) Base: this level represent base alias set of the access and refers
27 to sons (ref nodes). Flag all_refs means that all possible references
30 Because for LTO streaming we need to stream types rather than alias sets
31 modref_base_node is implemented as a template.
32 2) Ref: this level represent ref alias set and links to accesses unless
34 Again ref is an template to allow LTO streaming.
35 3) Access: this level represent info about individual accesses. Presently
36 we record whether access is through a dereference of a function parameter
39 #ifndef GCC_MODREF_TREE_H
40 #define GCC_MODREF_TREE_H
42 struct ipa_modref_summary
;
45 struct GTY(()) modref_access_node
48 /* Access range information (in bits). */
53 /* Offset from parameter pointer to the base of the access (in bytes). */
54 poly_int64 parm_offset
;
56 /* Index of parameter which specifies the base of access. -1 if base is not
57 a function parameter. */
59 bool parm_offset_known
;
61 /* Return true if access node holds no useful info. */
62 bool useful_p () const
64 return parm_index
!= -1;
66 /* Return true if range info is useful. */
67 bool range_info_useful_p () const
69 return parm_index
!= -1 && parm_offset_known
;
71 /* Return true if both accesses are the same. */
72 bool operator == (modref_access_node
&a
) const
74 if (parm_index
!= a
.parm_index
)
78 if (parm_offset_known
!= a
.parm_offset_known
)
81 && !known_eq (parm_offset
, a
.parm_offset
))
84 if (range_info_useful_p ()
85 && (!known_eq (a
.offset
, offset
)
86 || !known_eq (a
.size
, size
)
87 || !known_eq (a
.max_size
, max_size
)))
93 /* Access node specifying no useful info. */
94 const modref_access_node unspecified_modref_access_node
95 = {0, -1, -1, 0, -1, false};
98 struct GTY((user
)) modref_ref_node
102 vec
<modref_access_node
, va_gc
> *accesses
;
104 modref_ref_node (T ref
):
106 every_access (false),
110 /* Search REF; return NULL if failed. */
111 modref_access_node
*search (modref_access_node access
)
114 modref_access_node
*a
;
115 FOR_EACH_VEC_SAFE_ELT (accesses
, i
, a
)
121 /* Collapse the tree. */
129 /* Insert access with OFFSET and SIZE.
130 Collapse tree if it has more than MAX_ACCESSES entries.
131 Return true if record was changed. */
132 bool insert_access (modref_access_node a
, size_t max_accesses
)
134 /* If this base->ref pair has no access information, bail out. */
138 /* Otherwise, insert a node for the ref of the access under the base. */
139 modref_access_node
*access_node
= search (a
);
143 /* If this base->ref pair has too many accesses stored, we will clear
144 all accesses and bail out. */
145 if ((accesses
&& accesses
->length () >= max_accesses
)
148 if (dump_file
&& a
.useful_p ())
150 "--param param=modref-max-accesses limit reached\n");
154 vec_safe_push (accesses
, a
);
159 /* Base of an access. */
160 template <typename T
>
161 struct GTY((user
)) modref_base_node
164 vec
<modref_ref_node
<T
> *, va_gc
> *refs
;
167 modref_base_node (T base
):
172 /* Search REF; return NULL if failed. */
173 modref_ref_node
<T
> *search (T ref
)
176 modref_ref_node
<T
> *n
;
177 FOR_EACH_VEC_SAFE_ELT (refs
, i
, n
)
183 /* Insert REF; collapse tree if there are more than MAX_REFS.
184 Return inserted ref and if CHANGED is non-null set it to true if
185 something changed. */
186 modref_ref_node
<T
> *insert_ref (T ref
, size_t max_refs
,
187 bool *changed
= NULL
)
189 modref_ref_node
<T
> *ref_node
;
191 /* If the node is collapsed, don't do anything. */
195 /* Otherwise, insert a node for the ref of the access under the base. */
196 ref_node
= search (ref
);
203 /* Collapse the node if too full already. */
204 if (refs
&& refs
->length () >= max_refs
)
207 fprintf (dump_file
, "--param param=modref-max-refs limit reached\n");
212 ref_node
= new (ggc_alloc
<modref_ref_node
<T
> > ())modref_ref_node
<T
>
214 vec_safe_push (refs
, ref_node
);
221 modref_ref_node
<T
> *r
;
225 FOR_EACH_VEC_SAFE_ELT (refs
, i
, r
)
237 /* Map translating parameters across function call. */
239 struct modref_parm_map
241 /* Index of parameter we translate to.
242 -1 indicates that parameter is unknown
243 -2 indicates that parameter points to local memory and access can be
246 bool parm_offset_known
;
247 poly_int64 parm_offset
;
250 /* Access tree for a single function. */
251 template <typename T
>
252 struct GTY((user
)) modref_tree
254 vec
<modref_base_node
<T
> *, va_gc
> *bases
;
260 modref_tree (size_t max_bases
, size_t max_refs
, size_t max_accesses
):
262 max_bases (max_bases
),
264 max_accesses (max_accesses
),
265 every_base (false) {}
267 /* Insert BASE; collapse tree if there are more than MAX_REFS.
268 Return inserted base and if CHANGED is non-null set it to true if
269 something changed. */
271 modref_base_node
<T
> *insert_base (T base
, bool *changed
= NULL
)
273 modref_base_node
<T
> *base_node
;
275 /* If the node is collapsed, don't do anything. */
279 /* Otherwise, insert a node for the base of the access into the tree. */
280 base_node
= search (base
);
287 /* Collapse the node if too full already. */
288 if (bases
&& bases
->length () >= max_bases
)
291 fprintf (dump_file
, "--param param=modref-max-bases limit reached\n");
296 base_node
= new (ggc_alloc
<modref_base_node
<T
> > ())
297 modref_base_node
<T
> (base
);
298 vec_safe_push (bases
, base_node
);
302 /* Insert memory access to the tree.
303 Return true if something changed. */
304 bool insert (T base
, T ref
, modref_access_node a
)
309 bool changed
= false;
311 /* No useful information tracked; collapse everything. */
312 if (!base
&& !ref
&& !a
.useful_p ())
318 modref_base_node
<T
> *base_node
= insert_base (base
, &changed
);
319 if (!base_node
|| base_node
->every_ref
)
321 gcc_checking_assert (search (base
) != NULL
);
323 /* No useful ref info tracked; collapse base. */
324 if (!ref
&& !a
.useful_p ())
326 base_node
->collapse ();
330 modref_ref_node
<T
> *ref_node
= base_node
->insert_ref (ref
, max_refs
,
333 /* If we failed to insert ref, just see if there is a cleanup possible. */
336 /* No useful ref information and no useful base; collapse everything. */
337 if (!base
&& base_node
->every_ref
)
340 gcc_checking_assert (changed
);
347 if (ref_node
->every_access
)
349 changed
|= ref_node
->insert_access (a
, max_accesses
);
350 /* See if we failed to add useful access. */
351 if (ref_node
->every_access
)
353 /* Collapse everything if there is no useful base and ref. */
357 gcc_checking_assert (changed
);
359 /* Collapse base if there is no useful ref. */
362 base_node
->collapse ();
363 gcc_checking_assert (changed
);
370 /* Remove tree branches that are not useful (i.e. they will always pass). */
375 modref_base_node
<T
> *base_node
;
376 modref_ref_node
<T
> *ref_node
;
381 for (i
= 0; vec_safe_iterate (bases
, i
, &base_node
);)
384 for (j
= 0; vec_safe_iterate (base_node
->refs
, j
, &ref_node
);)
386 if (!ref_node
->every_access
387 && (!ref_node
->accesses
388 || !ref_node
->accesses
->length ()))
390 base_node
->refs
->unordered_remove (j
);
391 vec_free (ref_node
->accesses
);
392 ggc_delete (ref_node
);
397 if (!base_node
->every_ref
398 && (!base_node
->refs
|| !base_node
->refs
->length ()))
400 bases
->unordered_remove (i
);
401 vec_free (base_node
->refs
);
402 ggc_delete (base_node
);
407 if (bases
&& !bases
->length ())
414 /* Merge OTHER into the tree.
415 PARM_MAP, if non-NULL, maps parm indexes of callee to caller. -2 is used
416 to signalize that parameter is local and does not need to be tracked.
417 Return true if something has changed. */
418 bool merge (modref_tree
<T
> *other
, vec
<modref_parm_map
> *parm_map
)
420 if (!other
|| every_base
)
422 if (other
->every_base
)
428 bool changed
= false;
430 modref_base_node
<T
> *base_node
, *my_base_node
;
431 modref_ref_node
<T
> *ref_node
;
432 modref_access_node
*access_node
;
433 bool release
= false;
435 /* For self-recursive functions we may end up merging summary into itself;
436 produce copy first so we do not modify summary under our own hands. */
440 other
= modref_tree
<T
>::create_ggc (max_bases
, max_refs
, max_accesses
);
441 other
->copy_from (this);
444 FOR_EACH_VEC_SAFE_ELT (other
->bases
, i
, base_node
)
446 if (base_node
->every_ref
)
448 my_base_node
= insert_base (base_node
->base
, &changed
);
449 if (my_base_node
&& !my_base_node
->every_ref
)
451 my_base_node
->collapse ();
457 FOR_EACH_VEC_SAFE_ELT (base_node
->refs
, j
, ref_node
)
459 if (ref_node
->every_access
)
461 changed
|= insert (base_node
->base
,
463 unspecified_modref_access_node
);
466 FOR_EACH_VEC_SAFE_ELT (ref_node
->accesses
, k
, access_node
)
468 modref_access_node a
= *access_node
;
470 if (a
.parm_index
!= -1 && parm_map
)
472 if (a
.parm_index
>= (int)parm_map
->length ())
474 else if ((*parm_map
) [a
.parm_index
].parm_index
== -2)
479 += (*parm_map
) [a
.parm_index
].parm_offset
;
482 [a
.parm_index
].parm_offset_known
;
484 = (*parm_map
) [a
.parm_index
].parm_index
;
487 changed
|= insert (base_node
->base
, ref_node
->ref
, a
);
496 /* Copy OTHER to THIS. */
497 void copy_from (modref_tree
<T
> *other
)
502 /* Search BASE in tree; return NULL if failed. */
503 modref_base_node
<T
> *search (T base
)
506 modref_base_node
<T
> *n
;
507 FOR_EACH_VEC_SAFE_ELT (bases
, i
, n
)
513 /* Return ggc allocated instance. We explicitly call destructors via
514 ggc_delete and do not want finalizers to be registered and
515 called at the garbage collection time. */
516 static modref_tree
<T
> *create_ggc (size_t max_bases
, size_t max_refs
,
519 return new (ggc_alloc_no_dtor
<modref_tree
<T
>> ())
520 modref_tree
<T
> (max_bases
, max_refs
, max_accesses
);
523 /* Remove all records and mark tree to alias with everything. */
527 modref_base_node
<T
> *n
;
531 FOR_EACH_VEC_SAFE_ELT (bases
, i
, n
)
542 /* Release memory. */
548 /* Update parameter indexes in TT according to MAP. */
550 remap_params (vec
<int> *map
)
553 modref_base_node
<T
> *base_node
;
554 FOR_EACH_VEC_SAFE_ELT (bases
, i
, base_node
)
557 modref_ref_node
<T
> *ref_node
;
558 FOR_EACH_VEC_SAFE_ELT (base_node
->refs
, j
, ref_node
)
561 modref_access_node
*access_node
;
562 FOR_EACH_VEC_SAFE_ELT (ref_node
->accesses
, k
, access_node
)
563 if (access_node
->parm_index
> 0)
565 if (access_node
->parm_index
< (int)map
->length ())
566 access_node
->parm_index
= (*map
)[access_node
->parm_index
];
568 access_node
->parm_index
= -1;
575 void modref_c_tests ();
577 void gt_ggc_mx (modref_tree
<int>* const&);
578 void gt_ggc_mx (modref_tree
<tree_node
*>* const&);
579 void gt_pch_nx (modref_tree
<int>* const&);
580 void gt_pch_nx (modref_tree
<tree_node
*>* const&);
581 void gt_pch_nx (modref_tree
<int>* const&, gt_pointer_operator op
, void *cookie
);
582 void gt_pch_nx (modref_tree
<tree_node
*>* const&, gt_pointer_operator op
,
585 void gt_ggc_mx (modref_base_node
<int>*);
586 void gt_ggc_mx (modref_base_node
<tree_node
*>* &);
587 void gt_pch_nx (modref_base_node
<int>* const&);
588 void gt_pch_nx (modref_base_node
<tree_node
*>* const&);
589 void gt_pch_nx (modref_base_node
<int>* const&, gt_pointer_operator op
,
591 void gt_pch_nx (modref_base_node
<tree_node
*>* const&, gt_pointer_operator op
,
594 void gt_ggc_mx (modref_ref_node
<int>*);
595 void gt_ggc_mx (modref_ref_node
<tree_node
*>* &);
596 void gt_pch_nx (modref_ref_node
<int>* const&);
597 void gt_pch_nx (modref_ref_node
<tree_node
*>* const&);
598 void gt_pch_nx (modref_ref_node
<int>* const&, gt_pointer_operator op
,
600 void gt_pch_nx (modref_ref_node
<tree_node
*>* const&, gt_pointer_operator op
,