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