Branch data Line data Source code
1 : : /* Classes for purging state at function_points.
2 : : Copyright (C) 2019-2024 Free Software Foundation, Inc.
3 : : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 : :
5 : : This file is part of GCC.
6 : :
7 : : GCC is free software; you can redistribute it and/or modify it
8 : : under the terms of the GNU General Public License as published by
9 : : the Free Software Foundation; either version 3, or (at your option)
10 : : any later version.
11 : :
12 : : GCC is distributed in the hope that it will be useful, but
13 : : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : General Public License for more details.
16 : :
17 : : You should have received a copy of the GNU General Public License
18 : : along with GCC; see the file COPYING3. If not see
19 : : <http://www.gnu.org/licenses/>. */
20 : :
21 : : #include "config.h"
22 : : #define INCLUDE_VECTOR
23 : : #include "system.h"
24 : : #include "coretypes.h"
25 : : #include "tree.h"
26 : : #include "timevar.h"
27 : : #include "tree-ssa-alias.h"
28 : : #include "function.h"
29 : : #include "basic-block.h"
30 : : #include "gimple.h"
31 : : #include "stringpool.h"
32 : : #include "tree-vrp.h"
33 : : #include "gimple-ssa.h"
34 : : #include "tree-ssanames.h"
35 : : #include "tree-phinodes.h"
36 : : #include "options.h"
37 : : #include "ssa-iterators.h"
38 : : #include "diagnostic-core.h"
39 : : #include "gimple-pretty-print.h"
40 : : #include "analyzer/analyzer.h"
41 : : #include "analyzer/call-string.h"
42 : : #include "analyzer/supergraph.h"
43 : : #include "analyzer/program-point.h"
44 : : #include "analyzer/analyzer-logging.h"
45 : : #include "analyzer/state-purge.h"
46 : : #include "analyzer/store.h"
47 : : #include "analyzer/region-model.h"
48 : : #include "gimple-walk.h"
49 : : #include "cgraph.h"
50 : :
51 : : #if ENABLE_ANALYZER
52 : :
53 : : /* Given NODE at an access, determine if this access is within
54 : : a decl that could be consider for purging, and if so, return the decl. */
55 : :
56 : : static tree
57 : 35805 : get_candidate_for_purging (tree node)
58 : : {
59 : 35805 : tree iter = node;
60 : 49671 : while (1)
61 : 42738 : switch (TREE_CODE (iter))
62 : : {
63 : : default:
64 : : return NULL_TREE;
65 : :
66 : 6933 : case ADDR_EXPR:
67 : 6933 : case MEM_REF:
68 : 6933 : case COMPONENT_REF:
69 : 6933 : iter = TREE_OPERAND (iter, 0);
70 : 6933 : continue;
71 : :
72 : 24914 : case VAR_DECL:
73 : 24914 : if (is_global_var (iter))
74 : : return NULL_TREE;
75 : : else
76 : : return iter;
77 : :
78 : : case PARM_DECL:
79 : : case RESULT_DECL:
80 : : return iter;
81 : : }
82 : : }
83 : :
84 : : /* Class-based handler for walk_stmt_load_store_addr_ops at a particular
85 : : function_point, for populating the worklists within a state_purge_map. */
86 : :
87 : 239960 : class gimple_op_visitor : public log_user
88 : : {
89 : : public:
90 : 119980 : gimple_op_visitor (state_purge_map *map,
91 : : const function_point &point,
92 : : const function &fun)
93 : 119980 : : log_user (map->get_logger ()),
94 : 119980 : m_map (map),
95 : 119980 : m_point (point),
96 : 239960 : m_fun (fun)
97 : : {}
98 : :
99 : 13850 : bool on_load (gimple *stmt, tree base, tree op)
100 : : {
101 : 13850 : LOG_FUNC (get_logger ());
102 : 13850 : if (get_logger ())
103 : : {
104 : 4 : pretty_printer pp;
105 : 4 : pp_gimple_stmt_1 (&pp, stmt, 0, (dump_flags_t)0);
106 : 4 : log ("on_load: %s; base: %qE, op: %qE",
107 : : pp_formatted_text (&pp), base, op);
108 : 4 : }
109 : 13850 : if (tree node = get_candidate_for_purging (base))
110 : 4024 : add_needed (node);
111 : 27700 : return true;
112 : 13850 : }
113 : :
114 : 10445 : bool on_store (gimple *stmt, tree base, tree op)
115 : : {
116 : 10445 : LOG_FUNC (get_logger ());
117 : 10445 : if (get_logger ())
118 : : {
119 : 1 : pretty_printer pp;
120 : 1 : pp_gimple_stmt_1 (&pp, stmt, 0, (dump_flags_t)0);
121 : 1 : log ("on_store: %s; base: %qE, op: %qE",
122 : : pp_formatted_text (&pp), base, op);
123 : 1 : }
124 : 20890 : return true;
125 : 10445 : }
126 : :
127 : 11076 : bool on_addr (gimple *stmt, tree base, tree op)
128 : : {
129 : 11076 : LOG_FUNC (get_logger ());
130 : 11076 : if (get_logger ())
131 : : {
132 : 1 : pretty_printer pp;
133 : 1 : pp_gimple_stmt_1 (&pp, stmt, 0, (dump_flags_t)0);
134 : 1 : log ("on_addr: %s; base: %qE, op: %qE",
135 : : pp_formatted_text (&pp), base, op);
136 : 1 : }
137 : 11076 : if (TREE_CODE (op) != ADDR_EXPR)
138 : : return true;
139 : 11035 : if (tree node = get_candidate_for_purging (base))
140 : : {
141 : 3448 : add_needed (node);
142 : 3448 : add_pointed_to (node);
143 : : }
144 : : return true;
145 : 11076 : }
146 : :
147 : : private:
148 : 7472 : void add_needed (tree decl)
149 : : {
150 : 7472 : gcc_assert (get_candidate_for_purging (decl) == decl);
151 : 7472 : state_purge_per_decl &data
152 : 7472 : = get_or_create_data_for_decl (decl);
153 : 7472 : data.add_needed_at (m_point);
154 : :
155 : : /* Handle calls: if we're seeing a use at a call, then add a use at the
156 : : "after-supernode" point (in case of interprocedural call superedges). */
157 : 7472 : if (m_point.final_stmt_p ())
158 : 1740 : data.add_needed_at (m_point.get_next ());
159 : 7472 : }
160 : :
161 : 3448 : void add_pointed_to (tree decl)
162 : : {
163 : 3448 : gcc_assert (get_candidate_for_purging (decl) == decl);
164 : 3448 : get_or_create_data_for_decl (decl).add_pointed_to_at (m_point);
165 : 3448 : }
166 : :
167 : : state_purge_per_decl &
168 : 10920 : get_or_create_data_for_decl (tree decl)
169 : : {
170 : 10920 : return m_map->get_or_create_data_for_decl (m_fun, decl);
171 : : }
172 : :
173 : : state_purge_map *m_map;
174 : : const function_point &m_point;
175 : : const function &m_fun;
176 : : };
177 : :
178 : : static bool
179 : 13850 : my_load_cb (gimple *stmt, tree base, tree op, void *user_data)
180 : : {
181 : 13850 : gimple_op_visitor *x = (gimple_op_visitor *)user_data;
182 : 13850 : return x->on_load (stmt, base, op);
183 : : }
184 : :
185 : : static bool
186 : 10445 : my_store_cb (gimple *stmt, tree base, tree op, void *user_data)
187 : : {
188 : 10445 : gimple_op_visitor *x = (gimple_op_visitor *)user_data;
189 : 10445 : return x->on_store (stmt, base, op);
190 : : }
191 : :
192 : : static bool
193 : 11076 : my_addr_cb (gimple *stmt, tree base, tree op, void *user_data)
194 : : {
195 : 11076 : gimple_op_visitor *x = (gimple_op_visitor *)user_data;
196 : 11076 : return x->on_addr (stmt, base, op);
197 : : }
198 : :
199 : : /* state_purge_map's ctor. Walk all SSA names in all functions, building
200 : : a state_purge_per_ssa_name instance for each.
201 : : Also, walk all loads and address-taken ops of local variables, building
202 : : a state_purge_per_decl as appropriate. */
203 : :
204 : 3189 : state_purge_map::state_purge_map (const supergraph &sg,
205 : : region_model_manager *mgr,
206 : 3189 : logger *logger)
207 : 3189 : : log_user (logger), m_sg (sg)
208 : : {
209 : 3189 : LOG_FUNC (logger);
210 : :
211 : 3189 : auto_timevar tv (TV_ANALYZER_STATE_PURGE);
212 : :
213 : 3189 : cgraph_node *node;
214 : 12918 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
215 : : {
216 : 9729 : function *fun = node->get_fun ();
217 : 9729 : gcc_assert (fun);
218 : 9729 : if (logger)
219 : 2 : log ("function: %s", function_name (fun));
220 : : tree name;
221 : : unsigned int i;;
222 : 139959 : FOR_EACH_SSA_NAME (i, name, fun)
223 : : {
224 : : /* For now, don't bother tracking the .MEM SSA names. */
225 : 130198 : if (tree var = SSA_NAME_VAR (name))
226 : 87732 : if (TREE_CODE (var) == VAR_DECL)
227 : 78859 : if (VAR_DECL_IS_VIRTUAL_OPERAND (var))
228 : 62005 : continue;
229 : 68193 : m_ssa_map.put (name, new state_purge_per_ssa_name (*this, name, *fun));
230 : : }
231 : : }
232 : :
233 : : /* Find all uses of local vars.
234 : : We iterate through all function points, finding loads, stores, and
235 : : address-taken operations on locals, building a pair of worklists. */
236 : 69002 : for (auto snode : sg.m_nodes)
237 : : {
238 : 59443 : if (logger)
239 : 13 : log ("SN: %i", snode->m_index);
240 : : /* We ignore m_returning_call and phi nodes. */
241 : : gimple *stmt;
242 : : unsigned i;
243 : 217725 : FOR_EACH_VEC_ELT (snode->m_stmts, i, stmt)
244 : : {
245 : 119980 : function *fun = snode->get_function ();
246 : 119980 : gcc_assert (fun);
247 : 119980 : function_point point (function_point::before_stmt (snode, i));
248 : 119980 : gimple_op_visitor v (this, point, *fun);
249 : 119980 : walk_stmt_load_store_addr_ops (stmt, &v,
250 : : my_load_cb, my_store_cb, my_addr_cb);
251 : 119980 : }
252 : : }
253 : :
254 : : /* Now iterate through the m_decl_map, processing each pair of worklists. */
255 : 3189 : for (state_purge_map::decl_iterator iter = begin_decls ();
256 : 10260 : iter != end_decls ();
257 : 3018 : ++iter)
258 : : {
259 : 3018 : state_purge_per_decl *per_decl_data = (*iter).second;
260 : 3018 : per_decl_data->process_worklists (*this, mgr);
261 : : }
262 : 3189 : }
263 : :
264 : : /* state_purge_map's dtor. */
265 : :
266 : 3189 : state_purge_map::~state_purge_map ()
267 : : {
268 : 74275 : for (auto iter : m_ssa_map)
269 : 136386 : delete iter.second;
270 : 7242 : for (auto iter : m_decl_map)
271 : 6036 : delete iter.second;
272 : 3189 : }
273 : :
274 : : /* Get the state_purge_per_decl for local DECL within FUN, creating it
275 : : if necessary. */
276 : :
277 : : state_purge_per_decl &
278 : 10920 : state_purge_map::get_or_create_data_for_decl (const function &fun, tree decl)
279 : : {
280 : 21840 : if (state_purge_per_decl **slot
281 : 10920 : = const_cast <decl_map_t&> (m_decl_map).get (decl))
282 : 7902 : return **slot;
283 : 3018 : state_purge_per_decl *result = new state_purge_per_decl (*this, decl, fun);
284 : 3018 : m_decl_map.put (decl, result);
285 : 3018 : return *result;
286 : : }
287 : :
288 : : /* class state_purge_per_ssa_name : public state_purge_per_tree. */
289 : :
290 : : /* state_purge_per_ssa_name's ctor.
291 : :
292 : : Locate all uses of VAR within FUN.
293 : : Walk backwards from each use, marking program points, until
294 : : we reach the def stmt, populating m_points_needing_var.
295 : :
296 : : We have to track program points rather than
297 : : just stmts since there could be empty basic blocks on the way. */
298 : :
299 : 68193 : state_purge_per_ssa_name::state_purge_per_ssa_name (const state_purge_map &map,
300 : : tree name,
301 : 68193 : const function &fun)
302 : 68193 : : state_purge_per_tree (fun), m_points_needing_name (), m_name (name)
303 : : {
304 : 68193 : LOG_FUNC (map.get_logger ());
305 : :
306 : 68193 : if (map.get_logger ())
307 : : {
308 : 16 : map.log ("SSA name: %qE within %qD", name, fun.decl);
309 : :
310 : : /* Show def stmt. */
311 : 16 : const gimple *def_stmt = SSA_NAME_DEF_STMT (name);
312 : 16 : pretty_printer pp;
313 : 16 : pp_gimple_stmt_1 (&pp, def_stmt, 0, (dump_flags_t)0);
314 : 16 : map.log ("def stmt: %s", pp_formatted_text (&pp));
315 : 16 : }
316 : :
317 : 68193 : auto_vec<function_point> worklist;
318 : :
319 : : /* Add all immediate uses of name to the worklist.
320 : : Compare with debug_immediate_uses. */
321 : 68193 : imm_use_iterator iter;
322 : 68193 : use_operand_p use_p;
323 : 158374 : FOR_EACH_IMM_USE_FAST (use_p, iter, name)
324 : : {
325 : 90181 : if (USE_STMT (use_p))
326 : : {
327 : 90181 : const gimple *use_stmt = USE_STMT (use_p);
328 : 90181 : if (map.get_logger ())
329 : : {
330 : 22 : pretty_printer pp;
331 : 22 : pp_gimple_stmt_1 (&pp, use_stmt, 0, (dump_flags_t)0);
332 : 22 : map.log ("used by stmt: %s", pp_formatted_text (&pp));
333 : 22 : }
334 : :
335 : 90181 : if (is_gimple_debug (use_stmt))
336 : : {
337 : : /* We skipped debug stmts when building the supergraph,
338 : : so ignore them now. */
339 : 303 : if (map.get_logger ())
340 : 0 : map.log ("skipping debug stmt");
341 : 303 : continue;
342 : : }
343 : :
344 : 89878 : const supernode *snode
345 : 89878 : = map.get_sg ().get_supernode_for_stmt (use_stmt);
346 : :
347 : : /* If it's a use within a phi node, then we care about
348 : : which in-edge we came from. */
349 : 89878 : if (use_stmt->code == GIMPLE_PHI)
350 : : {
351 : 8715 : for (gphi_iterator gpi
352 : 8715 : = const_cast<supernode *> (snode)->start_phis ();
353 : 27768 : !gsi_end_p (gpi); gsi_next (&gpi))
354 : : {
355 : 19053 : gphi *phi = gpi.phi ();
356 : 19053 : if (phi == use_stmt)
357 : : {
358 : : /* Find arguments (and thus in-edges) which use NAME. */
359 : 25369 : for (unsigned arg_idx = 0;
360 : 34084 : arg_idx < gimple_phi_num_args (phi);
361 : : ++arg_idx)
362 : : {
363 : 25369 : if (name == gimple_phi_arg (phi, arg_idx)->def)
364 : : {
365 : 10293 : edge in_edge = gimple_phi_arg_edge (phi, arg_idx);
366 : 10293 : const superedge *in_sedge
367 : 10293 : = map.get_sg ().get_edge_for_cfg_edge (in_edge);
368 : 10293 : function_point point
369 : : = function_point::before_supernode
370 : 10293 : (snode, in_sedge);
371 : 10293 : add_to_worklist (point, &worklist,
372 : : map.get_logger ());
373 : 10293 : m_points_needing_name.add (point);
374 : : }
375 : : }
376 : : }
377 : : }
378 : : }
379 : : else
380 : : {
381 : 81163 : function_point point = before_use_stmt (map, use_stmt);
382 : 81163 : add_to_worklist (point, &worklist, map.get_logger ());
383 : 81163 : m_points_needing_name.add (point);
384 : :
385 : : /* We also need to add uses for conditionals and switches,
386 : : where the stmt "happens" at the after_supernode, for filtering
387 : : the out-edges. */
388 : 81163 : if (use_stmt == snode->get_last_stmt ())
389 : : {
390 : 24227 : if (map.get_logger ())
391 : 6 : map.log ("last stmt in BB");
392 : 24227 : function_point point
393 : 24227 : = function_point::after_supernode (snode);
394 : 24227 : add_to_worklist (point, &worklist, map.get_logger ());
395 : 24227 : m_points_needing_name.add (point);
396 : : }
397 : : else
398 : 56936 : if (map.get_logger ())
399 : 13 : map.log ("not last stmt in BB");
400 : : }
401 : : }
402 : : }
403 : :
404 : : /* Process worklist by walking backwards until we reach the def stmt. */
405 : 68193 : {
406 : 68193 : log_scope s (map.get_logger (), "processing worklist");
407 : 554221 : while (worklist.length () > 0)
408 : : {
409 : 417835 : function_point point = worklist.pop ();
410 : 417835 : process_point (point, &worklist, map);
411 : : }
412 : 68193 : }
413 : :
414 : 68193 : if (map.get_logger ())
415 : : {
416 : 16 : map.log ("%qE in %qD is needed to process:", name, fun.decl);
417 : : /* Log m_points_needing_name, sorting it to avoid churn when comparing
418 : : dumps. */
419 : 16 : auto_vec<function_point> points;
420 : 16 : for (point_set_t::iterator iter = m_points_needing_name.begin ();
421 : 116 : iter != m_points_needing_name.end ();
422 : 100 : ++iter)
423 : 100 : points.safe_push (*iter);
424 : 16 : points.qsort (function_point::cmp_ptr);
425 : : unsigned i;
426 : : function_point *point;
427 : 132 : FOR_EACH_VEC_ELT (points, i, point)
428 : : {
429 : 100 : map.start_log_line ();
430 : 100 : map.get_logger ()->log_partial (" point: ");
431 : 100 : point->print (map.get_logger ()->get_printer (), format (false));
432 : 200 : map.end_log_line ();
433 : : }
434 : 16 : }
435 : 68193 : }
436 : :
437 : : /* Return true if the SSA name is needed at POINT. */
438 : :
439 : : bool
440 : 1122004 : state_purge_per_ssa_name::needed_at_point_p (const function_point &point) const
441 : : {
442 : 1122004 : return const_cast <point_set_t &> (m_points_needing_name).contains (point);
443 : : }
444 : :
445 : : /* Get the function_point representing immediately before USE_STMT.
446 : : Subroutine of ctor. */
447 : :
448 : : function_point
449 : 81163 : state_purge_per_ssa_name::before_use_stmt (const state_purge_map &map,
450 : : const gimple *use_stmt)
451 : : {
452 : 81163 : gcc_assert (use_stmt->code != GIMPLE_PHI);
453 : :
454 : 81163 : const supernode *supernode
455 : 81163 : = map.get_sg ().get_supernode_for_stmt (use_stmt);
456 : 81163 : unsigned int stmt_idx = supernode->get_stmt_index (use_stmt);
457 : 81163 : return function_point::before_stmt (supernode, stmt_idx);
458 : : }
459 : :
460 : : /* Add POINT to *WORKLIST if the point has not already been seen.
461 : : Subroutine of ctor. */
462 : :
463 : : void
464 : 482093 : state_purge_per_ssa_name::add_to_worklist (const function_point &point,
465 : : auto_vec<function_point> *worklist,
466 : : logger *logger)
467 : : {
468 : 482093 : LOG_FUNC (logger);
469 : 482093 : if (logger)
470 : : {
471 : 114 : logger->start_log_line ();
472 : 114 : logger->log_partial ("point: '");
473 : 114 : point.print (logger->get_printer (), format (false));
474 : 114 : logger->log_partial ("' for worklist for %qE", m_name);
475 : 114 : logger->end_log_line ();
476 : : }
477 : :
478 : 482093 : gcc_assert (point.get_function () == &get_function ());
479 : 482093 : if (point.get_from_edge ())
480 : 71278 : gcc_assert (point.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE);
481 : :
482 : 482093 : if (m_points_needing_name.contains (point))
483 : : {
484 : 64258 : if (logger)
485 : 14 : logger->log ("already seen for %qE", m_name);
486 : : }
487 : : else
488 : : {
489 : 417835 : if (logger)
490 : 100 : logger->log ("not seen; adding to worklist for %qE", m_name);
491 : 417835 : m_points_needing_name.add (point);
492 : 417835 : worklist->safe_push (point);
493 : : }
494 : 482093 : }
495 : :
496 : : /* Return true iff NAME is used by any of the phi nodes in SNODE
497 : : when processing the in-edge with PHI_ARG_IDX. */
498 : :
499 : : static bool
500 : 10896 : name_used_by_phis_p (tree name, const supernode *snode,
501 : : size_t phi_arg_idx)
502 : : {
503 : 10896 : gcc_assert (TREE_CODE (name) == SSA_NAME);
504 : :
505 : 10896 : for (gphi_iterator gpi
506 : 10896 : = const_cast<supernode *> (snode)->start_phis ();
507 : 32374 : !gsi_end_p (gpi); gsi_next (&gpi))
508 : : {
509 : 21609 : gphi *phi = gpi.phi ();
510 : 21609 : if (gimple_phi_arg_def (phi, phi_arg_idx) == name)
511 : 10896 : return true;
512 : : }
513 : : return false;
514 : : }
515 : :
516 : : /* Process POINT, popped from WORKLIST.
517 : : Iterate over predecessors of POINT, adding to WORKLIST. */
518 : :
519 : : void
520 : 417835 : state_purge_per_ssa_name::process_point (const function_point &point,
521 : : auto_vec<function_point> *worklist,
522 : : const state_purge_map &map)
523 : : {
524 : 417835 : logger *logger = map.get_logger ();
525 : 417835 : LOG_FUNC (logger);
526 : 417835 : if (logger)
527 : : {
528 : 100 : logger->start_log_line ();
529 : 100 : logger->log_partial ("considering point: '");
530 : 100 : point.print (logger->get_printer (), format (false));
531 : 100 : logger->log_partial ("' for %qE", m_name);
532 : 100 : logger->end_log_line ();
533 : : }
534 : :
535 : 417835 : gimple *def_stmt = SSA_NAME_DEF_STMT (m_name);
536 : :
537 : 417835 : const supernode *snode = point.get_supernode ();
538 : :
539 : 417835 : switch (point.get_kind ())
540 : : {
541 : 0 : default:
542 : 0 : gcc_unreachable ();
543 : :
544 : : case PK_ORIGIN:
545 : : break;
546 : :
547 : 83074 : case PK_BEFORE_SUPERNODE:
548 : 83074 : {
549 : 83074 : for (gphi_iterator gpi
550 : 83074 : = const_cast<supernode *> (snode)->start_phis ();
551 : 129328 : !gsi_end_p (gpi); gsi_next (&gpi))
552 : : {
553 : 57019 : gcc_assert (point.get_from_edge ());
554 : 57019 : const cfg_superedge *cfg_sedge
555 : 57019 : = point.get_from_edge ()->dyn_cast_cfg_superedge ();
556 : 57019 : gcc_assert (cfg_sedge);
557 : :
558 : 57019 : gphi *phi = gpi.phi ();
559 : : /* Are we at the def-stmt for m_name? */
560 : 57019 : if (phi == def_stmt)
561 : : {
562 : 10896 : if (name_used_by_phis_p (m_name, snode,
563 : : cfg_sedge->get_phi_arg_idx ()))
564 : : {
565 : 131 : if (logger)
566 : 0 : logger->log ("name in def stmt used within phis;"
567 : : " continuing");
568 : : }
569 : : else
570 : : {
571 : 10765 : if (logger)
572 : 4 : logger->log ("name in def stmt not used within phis;"
573 : : " terminating");
574 : 10765 : return;
575 : : }
576 : : }
577 : : }
578 : :
579 : : /* Add given pred to worklist. */
580 : 72309 : if (point.get_from_edge ())
581 : : {
582 : 58784 : gcc_assert (point.get_from_edge ()->m_src);
583 : 58784 : add_to_worklist
584 : 58784 : (function_point::after_supernode (point.get_from_edge ()->m_src),
585 : : worklist, logger);
586 : : }
587 : : else
588 : : {
589 : : /* Add any intraprocedually edge for a call. */
590 : 13525 : if (snode->m_returning_call)
591 : : {
592 : 4992 : gcall *returning_call = snode->m_returning_call;
593 : 4992 : cgraph_edge *cedge
594 : 4992 : = supergraph_call_edge (snode->m_fun,
595 : : returning_call);
596 : 4992 : if(cedge)
597 : : {
598 : 3911 : superedge *sedge
599 : 3911 : = map.get_sg ().get_intraprocedural_edge_for_call (cedge);
600 : 3911 : gcc_assert (sedge);
601 : 3911 : add_to_worklist
602 : 3911 : (function_point::after_supernode (sedge->m_src),
603 : : worklist, logger);
604 : : }
605 : : else
606 : : {
607 : 1081 : supernode *callernode
608 : 1081 : = map.get_sg ().get_supernode_for_stmt (returning_call);
609 : :
610 : 1081 : gcc_assert (callernode);
611 : 1081 : add_to_worklist
612 : 1081 : (function_point::after_supernode (callernode),
613 : : worklist, logger);
614 : : }
615 : : }
616 : : }
617 : : }
618 : : break;
619 : :
620 : 261900 : case PK_BEFORE_STMT:
621 : 261900 : {
622 : 261900 : if (def_stmt == point.get_stmt ())
623 : : {
624 : 54282 : if (logger)
625 : 14 : logger->log ("def stmt; terminating");
626 : 54282 : return;
627 : : }
628 : 207618 : if (point.get_stmt_idx () > 0)
629 : 317040 : add_to_worklist (function_point::before_stmt
630 : 158520 : (snode, point.get_stmt_idx () - 1),
631 : : worklist, logger);
632 : : else
633 : : {
634 : : /* Add before_supernode to worklist. This captures the in-edge,
635 : : so we have to do it once per in-edge. */
636 : : unsigned i;
637 : : superedge *pred;
638 : 116204 : FOR_EACH_VEC_ELT (snode->m_preds, i, pred)
639 : 67106 : add_to_worklist (function_point::before_supernode (snode,
640 : : pred),
641 : : worklist, logger);
642 : : }
643 : : }
644 : : break;
645 : :
646 : 72861 : case PK_AFTER_SUPERNODE:
647 : 72861 : {
648 : 135218 : if (snode->m_stmts.length ())
649 : 62357 : add_to_worklist
650 : 62357 : (function_point::before_stmt (snode,
651 : 62357 : snode->m_stmts.length () - 1),
652 : : worklist, logger);
653 : : else
654 : : {
655 : : /* Add before_supernode to worklist. This captures the in-edge,
656 : : so we have to do it once per in-edge. */
657 : : unsigned i;
658 : : superedge *pred;
659 : 16622 : FOR_EACH_VEC_ELT (snode->m_preds, i, pred)
660 : 6118 : add_to_worklist (function_point::before_supernode (snode,
661 : : pred),
662 : : worklist, logger);
663 : : /* If it's the initial BB, add it, to ensure that we
664 : : have "before supernode" for the initial ENTRY block, and don't
665 : : erroneously purge SSA names for initial values of parameters. */
666 : 10504 : if (snode->entry_p ())
667 : : {
668 : 8533 : add_to_worklist
669 : 8533 : (function_point::before_supernode (snode, NULL),
670 : : worklist, logger);
671 : : }
672 : : }
673 : : }
674 : : break;
675 : : }
676 : 417835 : }
677 : :
678 : : /* class state_purge_per_decl : public state_purge_per_tree. */
679 : :
680 : : /* state_purge_per_decl's ctor. */
681 : :
682 : 3018 : state_purge_per_decl::state_purge_per_decl (const state_purge_map &map,
683 : : tree decl,
684 : 3018 : const function &fun)
685 : : : state_purge_per_tree (fun),
686 : 3018 : m_decl (decl)
687 : : {
688 : : /* The RESULT_DECL is always needed at the end of its function. */
689 : 3018 : if (TREE_CODE (decl) == RESULT_DECL)
690 : : {
691 : 26 : supernode *exit_snode = map.get_sg ().get_node_for_function_exit (fun);
692 : 26 : add_needed_at (function_point::after_supernode (exit_snode));
693 : : }
694 : 3018 : }
695 : :
696 : : /* Mark the value of the decl (or a subvalue within it) as being needed
697 : : at POINT. */
698 : :
699 : : void
700 : 9238 : state_purge_per_decl::add_needed_at (const function_point &point)
701 : : {
702 : 9238 : m_points_needing_decl.add (point);
703 : 9238 : }
704 : :
705 : : /* Mark that a pointer to the decl (or a region within it) is taken
706 : : at POINT. */
707 : :
708 : : void
709 : 3448 : state_purge_per_decl::add_pointed_to_at (const function_point &point)
710 : : {
711 : 3448 : m_points_taking_address.add (point);
712 : 3448 : }
713 : :
714 : : /* Process the worklists for this decl:
715 : : (a) walk backwards from points where we know the value of the decl
716 : : is needed, marking points until we get to a stmt that fully overwrites
717 : : the decl.
718 : : (b) walk forwards from points where the address of the decl is taken,
719 : : marking points as potentially needing the value of the decl. */
720 : :
721 : : void
722 : 3018 : state_purge_per_decl::process_worklists (const state_purge_map &map,
723 : : region_model_manager *mgr)
724 : : {
725 : 3018 : logger *logger = map.get_logger ();
726 : 3018 : LOG_SCOPE (logger);
727 : 3018 : if (logger)
728 : 0 : logger->log ("decl: %qE within %qD", m_decl, get_fndecl ());
729 : :
730 : : /* Worklist for walking backwards from uses. */
731 : 3018 : {
732 : 3018 : auto_vec<function_point> worklist;
733 : 3018 : point_set_t seen;
734 : :
735 : : /* Add all uses of the decl to the worklist. */
736 : 12242 : for (auto iter : m_points_needing_decl)
737 : 9224 : worklist.safe_push (iter);
738 : :
739 : 3018 : region_model model (mgr);
740 : 3018 : model.push_frame (get_function (), NULL, NULL);
741 : :
742 : : /* Process worklist by walking backwards until we reach a stmt
743 : : that fully overwrites the decl. */
744 : 3018 : {
745 : 3018 : log_scope s (logger, "processing worklist");
746 : 74696 : while (worklist.length () > 0)
747 : : {
748 : 68660 : function_point point = worklist.pop ();
749 : 68660 : process_point_backwards (point, &worklist, &seen, map, model);
750 : : }
751 : 3018 : }
752 : 3018 : }
753 : :
754 : : /* Worklist for walking forwards from address-taken points. */
755 : 3018 : {
756 : 3018 : auto_vec<function_point> worklist;
757 : 3018 : point_set_t seen;
758 : :
759 : : /* Add all uses of the decl to the worklist. */
760 : 9914 : for (auto iter : m_points_taking_address)
761 : : {
762 : 3448 : worklist.safe_push (iter);
763 : :
764 : : /* Add to m_points_needing_decl (now that we traversed
765 : : it above for the backward worklist). */
766 : 3448 : m_points_needing_decl.add (iter);
767 : : }
768 : :
769 : : /* Process worklist by walking forwards. */
770 : 3018 : {
771 : 3018 : log_scope s (logger, "processing worklist");
772 : 70861 : while (worklist.length () > 0)
773 : : {
774 : 64825 : function_point point = worklist.pop ();
775 : 64825 : process_point_forwards (point, &worklist, &seen, map);
776 : : }
777 : 3018 : }
778 : 3018 : }
779 : 3018 : }
780 : :
781 : : /* Add POINT to *WORKLIST if the point is not already in *seen.
782 : : Add newly seen points to *SEEN and to m_points_needing_name. */
783 : :
784 : : void
785 : 135161 : state_purge_per_decl::add_to_worklist (const function_point &point,
786 : : auto_vec<function_point> *worklist,
787 : : point_set_t *seen,
788 : : logger *logger)
789 : : {
790 : 135161 : LOG_FUNC (logger);
791 : 135161 : if (logger)
792 : : {
793 : 0 : logger->start_log_line ();
794 : 0 : logger->log_partial ("point: '");
795 : 0 : point.print (logger->get_printer (), format (false));
796 : 0 : logger->log_partial ("' for worklist for %qE", m_decl);
797 : 0 : logger->end_log_line ();
798 : : }
799 : :
800 : 135161 : gcc_assert (point.get_function () == &get_function ());
801 : 135161 : if (point.get_from_edge ())
802 : 28420 : gcc_assert (point.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE);
803 : :
804 : 135161 : if (seen->contains (point))
805 : : {
806 : 14348 : if (logger)
807 : 0 : logger->log ("already seen for %qE", m_decl);
808 : : }
809 : : else
810 : : {
811 : 120813 : if (logger)
812 : 0 : logger->log ("not seen; adding to worklist for %qE", m_decl);
813 : 120813 : m_points_needing_decl.add (point);
814 : 120813 : seen->add (point);
815 : 120813 : worklist->safe_push (point);
816 : : }
817 : 135161 : }
818 : :
819 : : /* Determine if REG_A and REG_B express writing to exactly the same
820 : : set of bits.
821 : : For example, when "s.field" is the only field of "s", and there's no
822 : : padding, this should return true. */
823 : :
824 : : static bool
825 : 31488 : same_binding_p (const region *reg_a, const region *reg_b,
826 : : store_manager *store_mgr)
827 : : {
828 : 31488 : if (reg_a->get_base_region () != reg_b->get_base_region ())
829 : : return false;
830 : 2319 : if (reg_a->empty_p ())
831 : : return false;
832 : 2319 : const binding_key *bind_key_a = binding_key::make (store_mgr, reg_a);
833 : 2319 : if (reg_b->empty_p ())
834 : : return false;
835 : 2319 : const binding_key *bind_key_b = binding_key::make (store_mgr, reg_b);
836 : 2319 : return bind_key_a == bind_key_b;
837 : : }
838 : :
839 : : /* Return true if STMT fully overwrites DECL. */
840 : :
841 : : static bool
842 : 41974 : fully_overwrites_p (const gimple *stmt, tree decl,
843 : : const region_model &model)
844 : : {
845 : 41974 : if (tree lhs = gimple_get_lhs (stmt))
846 : : {
847 : : /* Determine if LHS fully overwrites DECL.
848 : : We can't just check for equality; consider the case of
849 : : "s.field = EXPR;" where the stmt writes to the only field
850 : : of "s", and there's no padding. */
851 : 31488 : const region *lhs_reg = model.get_lvalue (lhs, NULL);
852 : 31488 : const region *decl_reg = model.get_lvalue (decl, NULL);
853 : 31488 : if (same_binding_p (lhs_reg, decl_reg,
854 : : model.get_manager ()->get_store_manager ()))
855 : : return true;
856 : : }
857 : : return false;
858 : : }
859 : :
860 : : /* Process POINT, popped from *WORKLIST.
861 : : Iterate over predecessors of POINT, adding to *WORKLIST and *SEEN,
862 : : until we get to a stmt that fully overwrites the decl. */
863 : :
864 : : void
865 : 68660 : state_purge_per_decl::
866 : : process_point_backwards (const function_point &point,
867 : : auto_vec<function_point> *worklist,
868 : : point_set_t *seen,
869 : : const state_purge_map &map,
870 : : const region_model &model)
871 : : {
872 : 68660 : logger *logger = map.get_logger ();
873 : 68660 : LOG_FUNC (logger);
874 : 68660 : if (logger)
875 : : {
876 : 0 : logger->start_log_line ();
877 : 0 : logger->log_partial ("considering point: '");
878 : 0 : point.print (logger->get_printer (), format (false));
879 : 0 : logger->log_partial ("' for %qE", m_decl);
880 : 0 : logger->end_log_line ();
881 : : }
882 : 68660 : const supernode *snode = point.get_supernode ();
883 : :
884 : 68660 : switch (point.get_kind ())
885 : : {
886 : 0 : default:
887 : 0 : gcc_unreachable ();
888 : :
889 : : case PK_ORIGIN:
890 : : break;
891 : :
892 : 13655 : case PK_BEFORE_SUPERNODE:
893 : 13655 : {
894 : : /* Add given pred to worklist. */
895 : 13655 : if (point.get_from_edge ())
896 : : {
897 : 12160 : gcc_assert (point.get_from_edge ()->m_src);
898 : 12160 : add_to_worklist
899 : 12160 : (function_point::after_supernode (point.get_from_edge ()->m_src),
900 : : worklist, seen, logger);
901 : : }
902 : : else
903 : : {
904 : : /* Add any intraprocedually edge for a call. */
905 : 1495 : if (snode->m_returning_call)
906 : : {
907 : 1078 : gcall *returning_call = snode->m_returning_call;
908 : 1078 : cgraph_edge *cedge
909 : 1078 : = supergraph_call_edge (snode->m_fun,
910 : : returning_call);
911 : 1078 : if(cedge)
912 : : {
913 : 747 : superedge *sedge
914 : 747 : = map.get_sg ().get_intraprocedural_edge_for_call (cedge);
915 : 747 : gcc_assert (sedge);
916 : 747 : add_to_worklist
917 : 747 : (function_point::after_supernode (sedge->m_src),
918 : : worklist, seen, logger);
919 : : }
920 : : else
921 : : {
922 : 331 : supernode *callernode
923 : 331 : = map.get_sg ().get_supernode_for_stmt (returning_call);
924 : :
925 : 331 : gcc_assert (callernode);
926 : 331 : add_to_worklist
927 : 331 : (function_point::after_supernode (callernode),
928 : : worklist, seen, logger);
929 : : }
930 : : }
931 : : }
932 : : }
933 : : break;
934 : :
935 : 41974 : case PK_BEFORE_STMT:
936 : 41974 : {
937 : : /* This is somewhat equivalent to how the SSA case handles
938 : : def-stmts. */
939 : 41974 : if (fully_overwrites_p (point.get_stmt (), m_decl, model)
940 : : /* ...but we mustn't be at a point that also consumes the
941 : : current value of the decl when it's generating the new
942 : : value, for cases such as
943 : : struct st s;
944 : : s = foo ();
945 : : s = bar (s);
946 : : where we want to make sure that we don't stop at the:
947 : : s = bar (s);
948 : : since otherwise we would erroneously purge the state of "s"
949 : : after:
950 : : s = foo ();
951 : : */
952 : 41974 : && !m_points_needing_decl.contains (point))
953 : : {
954 : 0 : if (logger)
955 : 0 : logger->log ("stmt fully overwrites %qE; terminating", m_decl);
956 : 0 : return;
957 : : }
958 : 41974 : if (point.get_stmt_idx () > 0)
959 : 57230 : add_to_worklist (function_point::before_stmt
960 : 28615 : (snode, point.get_stmt_idx () - 1),
961 : : worklist, seen, logger);
962 : : else
963 : : {
964 : : /* Add before_supernode to worklist. This captures the in-edge,
965 : : so we have to do it once per in-edge. */
966 : : unsigned i;
967 : : superedge *pred;
968 : 29753 : FOR_EACH_VEC_ELT (snode->m_preds, i, pred)
969 : 16394 : add_to_worklist (function_point::before_supernode (snode,
970 : : pred),
971 : : worklist, seen, logger);
972 : : }
973 : : }
974 : : break;
975 : :
976 : 13031 : case PK_AFTER_SUPERNODE:
977 : 13031 : {
978 : 22670 : if (snode->m_stmts.length ())
979 : 9639 : add_to_worklist
980 : 9639 : (function_point::before_stmt (snode,
981 : 9639 : snode->m_stmts.length () - 1),
982 : : worklist, seen, logger);
983 : : else
984 : : {
985 : : /* Add before_supernode to worklist. This captures the in-edge,
986 : : so we have to do it once per in-edge. */
987 : : unsigned i;
988 : : superedge *pred;
989 : 4647 : FOR_EACH_VEC_ELT (snode->m_preds, i, pred)
990 : 1255 : add_to_worklist (function_point::before_supernode (snode,
991 : : pred),
992 : : worklist, seen, logger);
993 : : }
994 : : }
995 : : break;
996 : : }
997 : :
998 : 68660 : }
999 : :
1000 : : /* Process POINT, popped from *WORKLIST.
1001 : : Iterate over successors of POINT, adding to *WORKLIST and *SEEN. */
1002 : :
1003 : : void
1004 : 64825 : state_purge_per_decl::
1005 : : process_point_forwards (const function_point &point,
1006 : : auto_vec<function_point> *worklist,
1007 : : point_set_t *seen,
1008 : : const state_purge_map &map)
1009 : : {
1010 : 64825 : logger *logger = map.get_logger ();
1011 : 64825 : LOG_FUNC (logger);
1012 : 64825 : if (logger)
1013 : : {
1014 : 0 : logger->start_log_line ();
1015 : 0 : logger->log_partial ("considering point: '");
1016 : 0 : point.print (logger->get_printer (), format (false));
1017 : 0 : logger->log_partial ("' for %qE", m_decl);
1018 : 0 : logger->end_log_line ();
1019 : : }
1020 : 64825 : const supernode *snode = point.get_supernode ();
1021 : :
1022 : 64825 : switch (point.get_kind ())
1023 : : {
1024 : 0 : default:
1025 : 0 : case PK_ORIGIN:
1026 : 0 : gcc_unreachable ();
1027 : :
1028 : 14726 : case PK_BEFORE_SUPERNODE:
1029 : 14726 : {
1030 : 14726 : function_point next = point.get_next ();
1031 : 14726 : add_to_worklist (next, worklist, seen, logger);
1032 : : }
1033 : 14726 : break;
1034 : :
1035 : 36568 : case PK_BEFORE_STMT:
1036 : 36568 : {
1037 : : /* Perhaps we should stop at a clobber of the decl,
1038 : : but that ought to purge state for us explicitly. */
1039 : 36568 : function_point next = point.get_next ();
1040 : 36568 : add_to_worklist (next, worklist, seen, logger);
1041 : : }
1042 : 36568 : break;
1043 : :
1044 : : case PK_AFTER_SUPERNODE:
1045 : : {
1046 : : /* Look at interprocedural out-edges. */
1047 : : unsigned i;
1048 : : superedge *succ;
1049 : 29321 : FOR_EACH_VEC_ELT (snode->m_succs, i, succ)
1050 : : {
1051 : 15790 : enum edge_kind kind = succ->get_kind ();
1052 : 15790 : if (kind == SUPEREDGE_CFG_EDGE
1053 : 15790 : || kind == SUPEREDGE_INTRAPROCEDURAL_CALL)
1054 : 14726 : add_to_worklist (function_point::before_supernode (succ->m_dest,
1055 : : succ),
1056 : : worklist, seen, logger);
1057 : : }
1058 : : }
1059 : : break;
1060 : : }
1061 : 64825 : }
1062 : :
1063 : : /* Return true if the decl is needed at POINT. */
1064 : :
1065 : : bool
1066 : 170110 : state_purge_per_decl::needed_at_point_p (const function_point &point) const
1067 : : {
1068 : 170110 : return const_cast <point_set_t &> (m_points_needing_decl).contains (point);
1069 : : }
1070 : :
1071 : : /* class state_purge_annotator : public dot_annotator. */
1072 : :
1073 : : /* Implementation of dot_annotator::add_node_annotations vfunc for
1074 : : state_purge_annotator.
1075 : :
1076 : : Add an additional record showing which names are purged on entry
1077 : : to the supernode N. */
1078 : :
1079 : : bool
1080 : 152 : state_purge_annotator::add_node_annotations (graphviz_out *gv,
1081 : : const supernode &n,
1082 : : bool within_table) const
1083 : : {
1084 : 152 : if (m_map == NULL)
1085 : : return false;
1086 : :
1087 : 152 : if (within_table)
1088 : : return false;
1089 : :
1090 : 76 : pretty_printer *pp = gv->get_pp ();
1091 : :
1092 : 76 : pp_printf (pp, "annotation_for_node_%i", n.m_index);
1093 : 76 : pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
1094 : : "lightblue");
1095 : 76 : pp_write_text_to_stream (pp);
1096 : :
1097 : : /* Different in-edges mean different names need purging.
1098 : : Determine which points to dump. */
1099 : 76 : auto_vec<function_point> points;
1100 : 76 : if (n.entry_p () || n.m_returning_call)
1101 : 16 : points.safe_push (function_point::before_supernode (&n, NULL));
1102 : : else
1103 : 252 : for (auto inedge : n.m_preds)
1104 : 72 : points.safe_push (function_point::before_supernode (&n, inedge));
1105 : 76 : points.safe_push (function_point::after_supernode (&n));
1106 : :
1107 : 392 : for (auto & point : points)
1108 : : {
1109 : 164 : point.print (pp, format (true));
1110 : 164 : pp_newline (pp);
1111 : 164 : print_needed (gv, point, false);
1112 : 164 : pp_newline (pp);
1113 : : }
1114 : :
1115 : 76 : pp_string (pp, "\"];\n\n");
1116 : 76 : pp_flush (pp);
1117 : 76 : return false;
1118 : 76 : }
1119 : :
1120 : : /* Print V to GV as a comma-separated list in braces, titling it with TITLE.
1121 : : If WITHIN_TABLE is true, print it within a <TR>
1122 : :
1123 : : Subroutine of state_purge_annotator::print_needed. */
1124 : :
1125 : : static void
1126 : 574 : print_vec_of_names (graphviz_out *gv, const char *title,
1127 : : const auto_vec<tree> &v, bool within_table)
1128 : : {
1129 : 574 : pretty_printer *pp = gv->get_pp ();
1130 : 574 : tree name;
1131 : 574 : unsigned i;
1132 : 574 : if (within_table)
1133 : 246 : gv->begin_trtd ();
1134 : 574 : pp_printf (pp, "%s: {", title);
1135 : 4375 : FOR_EACH_VEC_ELT (v, i, name)
1136 : : {
1137 : 3227 : if (i > 0)
1138 : 2703 : pp_string (pp, ", ");
1139 : 3227 : pp_printf (pp, "%qE", name);
1140 : : }
1141 : 574 : pp_printf (pp, "}");
1142 : 574 : if (within_table)
1143 : : {
1144 : 246 : pp_write_text_as_html_like_dot_to_stream (pp);
1145 : 246 : gv->end_tdtr ();
1146 : : }
1147 : 574 : pp_newline (pp);
1148 : 574 : }
1149 : :
1150 : : /* Implementation of dot_annotator::add_stmt_annotations for
1151 : : state_purge_annotator.
1152 : :
1153 : : Add text showing which names are purged at STMT. */
1154 : :
1155 : : void
1156 : 270 : state_purge_annotator::add_stmt_annotations (graphviz_out *gv,
1157 : : const gimple *stmt,
1158 : : bool within_row) const
1159 : : {
1160 : 270 : if (within_row)
1161 : 147 : return;
1162 : :
1163 : 135 : if (m_map == NULL)
1164 : : return;
1165 : :
1166 : 135 : if (stmt->code == GIMPLE_PHI)
1167 : : return;
1168 : :
1169 : 123 : pretty_printer *pp = gv->get_pp ();
1170 : :
1171 : 123 : pp_newline (pp);
1172 : :
1173 : 123 : const supernode *supernode = m_map->get_sg ().get_supernode_for_stmt (stmt);
1174 : 123 : unsigned int stmt_idx = supernode->get_stmt_index (stmt);
1175 : 123 : function_point before_stmt
1176 : 123 : (function_point::before_stmt (supernode, stmt_idx));
1177 : :
1178 : 123 : print_needed (gv, before_stmt, true);
1179 : : }
1180 : :
1181 : : /* Get the decls and SSA names needed and not-needed at POINT, and
1182 : : print them to GV.
1183 : : If WITHIN_TABLE is true, print them within <TR> elements. */
1184 : :
1185 : : void
1186 : 287 : state_purge_annotator::print_needed (graphviz_out *gv,
1187 : : const function_point &point,
1188 : : bool within_table) const
1189 : : {
1190 : 287 : auto_vec<tree> needed;
1191 : 287 : auto_vec<tree> not_needed;
1192 : 287 : for (state_purge_map::ssa_iterator iter = m_map->begin_ssas ();
1193 : 14208 : iter != m_map->end_ssas ();
1194 : 6817 : ++iter)
1195 : : {
1196 : 6817 : tree name = (*iter).first;
1197 : 6817 : state_purge_per_ssa_name *per_name_data = (*iter).second;
1198 : 6817 : if (&per_name_data->get_function () == point.get_function ())
1199 : : {
1200 : 3227 : if (per_name_data->needed_at_point_p (point))
1201 : 786 : needed.safe_push (name);
1202 : : else
1203 : 2441 : not_needed.safe_push (name);
1204 : : }
1205 : : }
1206 : 287 : for (state_purge_map::decl_iterator iter = m_map->begin_decls ();
1207 : 287 : iter != m_map->end_decls ();
1208 : 0 : ++iter)
1209 : : {
1210 : 0 : tree decl = (*iter).first;
1211 : 0 : state_purge_per_decl *per_decl_data = (*iter).second;
1212 : 0 : if (&per_decl_data->get_function () == point.get_function ())
1213 : : {
1214 : 0 : if (per_decl_data->needed_at_point_p (point))
1215 : 0 : needed.safe_push (decl);
1216 : : else
1217 : 0 : not_needed.safe_push (decl);
1218 : : }
1219 : : }
1220 : :
1221 : 287 : print_vec_of_names (gv, "needed here", needed, within_table);
1222 : 287 : print_vec_of_names (gv, "not needed here", not_needed, within_table);
1223 : 287 : }
1224 : :
1225 : : #endif /* #if ENABLE_ANALYZER */
|