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