d: Merge upstream dmd 56589f0f4, druntime 651389b5, phobos 1516ecad9.
[official-gcc.git] / gcc / analyzer / state-purge.cc
blob7a061a194808d4b3d18ef64e48b15f632c3c355a
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>.
5 This file is part of GCC.
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.
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.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "tree.h"
25 #include "timevar.h"
26 #include "tree-ssa-alias.h"
27 #include "function.h"
28 #include "basic-block.h"
29 #include "gimple.h"
30 #include "stringpool.h"
31 #include "tree-vrp.h"
32 #include "gimple-ssa.h"
33 #include "tree-ssanames.h"
34 #include "tree-phinodes.h"
35 #include "options.h"
36 #include "ssa-iterators.h"
37 #include "diagnostic-core.h"
38 #include "gimple-pretty-print.h"
39 #include "function.h"
40 #include "json.h"
41 #include "analyzer/analyzer.h"
42 #include "analyzer/call-string.h"
43 #include "digraph.h"
44 #include "ordered-hash-map.h"
45 #include "cfg.h"
46 #include "gimple-iterator.h"
47 #include "cgraph.h"
48 #include "analyzer/supergraph.h"
49 #include "analyzer/program-point.h"
50 #include "analyzer/analyzer-logging.h"
51 #include "analyzer/state-purge.h"
52 #include "tristate.h"
53 #include "selftest.h"
54 #include "analyzer/store.h"
55 #include "analyzer/region-model.h"
56 #include "gimple-walk.h"
58 #if ENABLE_ANALYZER
60 /* Given NODE at an access, determine if this access is within
61 a decl that could be consider for purging, and if so, return the decl. */
63 static tree
64 get_candidate_for_purging (tree node)
66 tree iter = node;
67 while (1)
68 switch (TREE_CODE (iter))
70 default:
71 return NULL_TREE;
73 case COMPONENT_REF:
74 iter = TREE_OPERAND (iter, 0);
75 continue;
77 case VAR_DECL:
78 if (is_global_var (iter))
79 return NULL_TREE;
80 else
81 return iter;
83 case PARM_DECL:
84 case RESULT_DECL:
85 return iter;
89 /* Class-based handler for walk_stmt_load_store_addr_ops at a particular
90 function_point, for populating the worklists within a state_purge_map. */
92 class gimple_op_visitor : public log_user
94 public:
95 gimple_op_visitor (state_purge_map *map,
96 const function_point &point,
97 function *fun)
98 : log_user (map->get_logger ()),
99 m_map (map),
100 m_point (point),
101 m_fun (fun)
104 bool on_load (gimple *stmt, tree base, tree op)
106 LOG_FUNC (get_logger ());
107 if (get_logger ())
109 pretty_printer pp;
110 pp_gimple_stmt_1 (&pp, stmt, 0, (dump_flags_t)0);
111 log ("on_load: %s; base: %qE, op: %qE",
112 pp_formatted_text (&pp), base, op);
114 if (tree node = get_candidate_for_purging (base))
115 add_needed (node);
116 return true;
119 bool on_store (gimple *stmt, tree base, tree op)
121 LOG_FUNC (get_logger ());
122 if (get_logger ())
124 pretty_printer pp;
125 pp_gimple_stmt_1 (&pp, stmt, 0, (dump_flags_t)0);
126 log ("on_store: %s; base: %qE, op: %qE",
127 pp_formatted_text (&pp), base, op);
129 return true;
132 bool on_addr (gimple *stmt, tree base, tree op)
134 LOG_FUNC (get_logger ());
135 if (get_logger ())
137 pretty_printer pp;
138 pp_gimple_stmt_1 (&pp, stmt, 0, (dump_flags_t)0);
139 log ("on_addr: %s; base: %qE, op: %qE",
140 pp_formatted_text (&pp), base, op);
142 if (TREE_CODE (op) != ADDR_EXPR)
143 return true;
144 if (tree node = get_candidate_for_purging (base))
146 add_needed (node);
147 add_pointed_to (node);
149 return true;
152 private:
153 void add_needed (tree decl)
155 gcc_assert (get_candidate_for_purging (decl) == decl);
156 state_purge_per_decl &data
157 = get_or_create_data_for_decl (decl);
158 data.add_needed_at (m_point);
160 /* Handle calls: if we're seeing a use at a call, then add a use at the
161 "after-supernode" point (in case of interprocedural call superedges). */
162 if (m_point.final_stmt_p ())
163 data.add_needed_at (m_point.get_next ());
166 void add_pointed_to (tree decl)
168 gcc_assert (get_candidate_for_purging (decl) == decl);
169 get_or_create_data_for_decl (decl).add_pointed_to_at (m_point);
172 state_purge_per_decl &
173 get_or_create_data_for_decl (tree decl)
175 return m_map->get_or_create_data_for_decl (m_fun, decl);
178 state_purge_map *m_map;
179 const function_point &m_point;
180 function *m_fun;
183 static bool
184 my_load_cb (gimple *stmt, tree base, tree op, void *user_data)
186 gimple_op_visitor *x = (gimple_op_visitor *)user_data;
187 return x->on_load (stmt, base, op);
190 static bool
191 my_store_cb (gimple *stmt, tree base, tree op, void *user_data)
193 gimple_op_visitor *x = (gimple_op_visitor *)user_data;
194 return x->on_store (stmt, base, op);
197 static bool
198 my_addr_cb (gimple *stmt, tree base, tree op, void *user_data)
200 gimple_op_visitor *x = (gimple_op_visitor *)user_data;
201 return x->on_addr (stmt, base, op);
204 /* state_purge_map's ctor. Walk all SSA names in all functions, building
205 a state_purge_per_ssa_name instance for each.
206 Also, walk all loads and address-taken ops of local variables, building
207 a state_purge_per_decl as appropriate. */
209 state_purge_map::state_purge_map (const supergraph &sg,
210 region_model_manager *mgr,
211 logger *logger)
212 : log_user (logger), m_sg (sg)
214 LOG_FUNC (logger);
216 auto_timevar tv (TV_ANALYZER_STATE_PURGE);
218 cgraph_node *node;
219 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
221 function *fun = node->get_fun ();
222 if (logger)
223 log ("function: %s", function_name (fun));
224 tree name;
225 unsigned int i;;
226 FOR_EACH_SSA_NAME (i, name, fun)
228 /* For now, don't bother tracking the .MEM SSA names. */
229 if (tree var = SSA_NAME_VAR (name))
230 if (TREE_CODE (var) == VAR_DECL)
231 if (VAR_DECL_IS_VIRTUAL_OPERAND (var))
232 continue;
233 m_ssa_map.put (name, new state_purge_per_ssa_name (*this, name, fun));
237 /* Find all uses of local vars.
238 We iterate through all function points, finding loads, stores, and
239 address-taken operations on locals, building a pair of worklists. */
240 for (auto snode : sg.m_nodes)
242 if (logger)
243 log ("SN: %i", snode->m_index);
244 /* We ignore m_returning_call and phi nodes. */
245 gimple *stmt;
246 unsigned i;
247 FOR_EACH_VEC_ELT (snode->m_stmts, i, stmt)
249 function_point point (function_point::before_stmt (snode, i));
250 gimple_op_visitor v (this, point, snode->get_function ());
251 walk_stmt_load_store_addr_ops (stmt, &v,
252 my_load_cb, my_store_cb, my_addr_cb);
256 /* Now iterate through the m_decl_map, processing each pair of worklists. */
257 for (state_purge_map::decl_iterator iter = begin_decls ();
258 iter != end_decls ();
259 ++iter)
261 state_purge_per_decl *per_decl_data = (*iter).second;
262 per_decl_data->process_worklists (*this, mgr);
266 /* state_purge_map's dtor. */
268 state_purge_map::~state_purge_map ()
270 for (auto iter : m_ssa_map)
271 delete iter.second;
272 for (auto iter : m_decl_map)
273 delete iter.second;
276 /* Get the state_purge_per_decl for local DECL within FUN, creating it
277 if necessary. */
279 state_purge_per_decl &
280 state_purge_map::get_or_create_data_for_decl (function *fun, tree decl)
282 if (state_purge_per_decl **slot
283 = const_cast <decl_map_t&> (m_decl_map).get (decl))
284 return **slot;
285 state_purge_per_decl *result = new state_purge_per_decl (*this, decl, fun);
286 m_decl_map.put (decl, result);
287 return *result;
290 /* class state_purge_per_ssa_name : public state_purge_per_tree. */
292 /* state_purge_per_ssa_name's ctor.
294 Locate all uses of VAR within FUN.
295 Walk backwards from each use, marking program points, until
296 we reach the def stmt, populating m_points_needing_var.
298 We have to track program points rather than
299 just stmts since there could be empty basic blocks on the way. */
301 state_purge_per_ssa_name::state_purge_per_ssa_name (const state_purge_map &map,
302 tree name,
303 function *fun)
304 : state_purge_per_tree (fun), m_points_needing_name (), m_name (name)
306 LOG_FUNC (map.get_logger ());
308 if (map.get_logger ())
310 map.log ("SSA name: %qE within %qD", name, fun->decl);
312 /* Show def stmt. */
313 const gimple *def_stmt = SSA_NAME_DEF_STMT (name);
314 pretty_printer pp;
315 pp_gimple_stmt_1 (&pp, def_stmt, 0, (dump_flags_t)0);
316 map.log ("def stmt: %s", pp_formatted_text (&pp));
319 auto_vec<function_point> worklist;
321 /* Add all immediate uses of name to the worklist.
322 Compare with debug_immediate_uses. */
323 imm_use_iterator iter;
324 use_operand_p use_p;
325 FOR_EACH_IMM_USE_FAST (use_p, iter, name)
327 if (USE_STMT (use_p))
329 const gimple *use_stmt = USE_STMT (use_p);
330 if (map.get_logger ())
332 pretty_printer pp;
333 pp_gimple_stmt_1 (&pp, use_stmt, 0, (dump_flags_t)0);
334 map.log ("used by stmt: %s", pp_formatted_text (&pp));
337 const supernode *snode
338 = map.get_sg ().get_supernode_for_stmt (use_stmt);
340 /* If it's a use within a phi node, then we care about
341 which in-edge we came from. */
342 if (use_stmt->code == GIMPLE_PHI)
344 for (gphi_iterator gpi
345 = const_cast<supernode *> (snode)->start_phis ();
346 !gsi_end_p (gpi); gsi_next (&gpi))
348 gphi *phi = gpi.phi ();
349 if (phi == use_stmt)
351 /* Find arguments (and thus in-edges) which use NAME. */
352 for (unsigned arg_idx = 0;
353 arg_idx < gimple_phi_num_args (phi);
354 ++arg_idx)
356 if (name == gimple_phi_arg (phi, arg_idx)->def)
358 edge in_edge = gimple_phi_arg_edge (phi, arg_idx);
359 const superedge *in_sedge
360 = map.get_sg ().get_edge_for_cfg_edge (in_edge);
361 function_point point
362 = function_point::before_supernode
363 (snode, in_sedge);
364 add_to_worklist (point, &worklist,
365 map.get_logger ());
366 m_points_needing_name.add (point);
372 else
374 function_point point = before_use_stmt (map, use_stmt);
375 add_to_worklist (point, &worklist, map.get_logger ());
376 m_points_needing_name.add (point);
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 if (use_stmt == snode->get_last_stmt ())
383 if (map.get_logger ())
384 map.log ("last stmt in BB");
385 function_point point
386 = function_point::after_supernode (snode);
387 add_to_worklist (point, &worklist, map.get_logger ());
388 m_points_needing_name.add (point);
390 else
391 if (map.get_logger ())
392 map.log ("not last stmt in BB");
397 /* Process worklist by walking backwards until we reach the def stmt. */
399 log_scope s (map.get_logger (), "processing worklist");
400 while (worklist.length () > 0)
402 function_point point = worklist.pop ();
403 process_point (point, &worklist, map);
407 if (map.get_logger ())
409 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 auto_vec<function_point> points;
413 for (point_set_t::iterator iter = m_points_needing_name.begin ();
414 iter != m_points_needing_name.end ();
415 ++iter)
416 points.safe_push (*iter);
417 points.qsort (function_point::cmp_ptr);
418 unsigned i;
419 function_point *point;
420 FOR_EACH_VEC_ELT (points, i, point)
422 map.start_log_line ();
423 map.get_logger ()->log_partial (" point: ");
424 point->print (map.get_logger ()->get_printer (), format (false));
425 map.end_log_line ();
430 /* Return true if the SSA name is needed at POINT. */
432 bool
433 state_purge_per_ssa_name::needed_at_point_p (const function_point &point) const
435 return const_cast <point_set_t &> (m_points_needing_name).contains (point);
438 /* Get the function_point representing immediately before USE_STMT.
439 Subroutine of ctor. */
441 function_point
442 state_purge_per_ssa_name::before_use_stmt (const state_purge_map &map,
443 const gimple *use_stmt)
445 gcc_assert (use_stmt->code != GIMPLE_PHI);
447 const supernode *supernode
448 = map.get_sg ().get_supernode_for_stmt (use_stmt);
449 unsigned int stmt_idx = supernode->get_stmt_index (use_stmt);
450 return function_point::before_stmt (supernode, stmt_idx);
453 /* Add POINT to *WORKLIST if the point has not already been seen.
454 Subroutine of ctor. */
456 void
457 state_purge_per_ssa_name::add_to_worklist (const function_point &point,
458 auto_vec<function_point> *worklist,
459 logger *logger)
461 LOG_FUNC (logger);
462 if (logger)
464 logger->start_log_line ();
465 logger->log_partial ("point: '");
466 point.print (logger->get_printer (), format (false));
467 logger->log_partial ("' for worklist for %qE", m_name);
468 logger->end_log_line ();
471 gcc_assert (point.get_function () == get_function ());
472 if (point.get_from_edge ())
473 gcc_assert (point.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE);
475 if (m_points_needing_name.contains (point))
477 if (logger)
478 logger->log ("already seen for %qE", m_name);
480 else
482 if (logger)
483 logger->log ("not seen; adding to worklist for %qE", m_name);
484 m_points_needing_name.add (point);
485 worklist->safe_push (point);
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. */
492 static bool
493 name_used_by_phis_p (tree name, const supernode *snode,
494 size_t phi_arg_idx)
496 gcc_assert (TREE_CODE (name) == SSA_NAME);
498 for (gphi_iterator gpi
499 = const_cast<supernode *> (snode)->start_phis ();
500 !gsi_end_p (gpi); gsi_next (&gpi))
502 gphi *phi = gpi.phi ();
503 if (gimple_phi_arg_def (phi, phi_arg_idx) == name)
504 return true;
506 return false;
509 /* Process POINT, popped from WORKLIST.
510 Iterate over predecessors of POINT, adding to WORKLIST. */
512 void
513 state_purge_per_ssa_name::process_point (const function_point &point,
514 auto_vec<function_point> *worklist,
515 const state_purge_map &map)
517 logger *logger = map.get_logger ();
518 LOG_FUNC (logger);
519 if (logger)
521 logger->start_log_line ();
522 logger->log_partial ("considering point: '");
523 point.print (logger->get_printer (), format (false));
524 logger->log_partial ("' for %qE", m_name);
525 logger->end_log_line ();
528 gimple *def_stmt = SSA_NAME_DEF_STMT (m_name);
530 const supernode *snode = point.get_supernode ();
532 switch (point.get_kind ())
534 default:
535 gcc_unreachable ();
537 case PK_ORIGIN:
538 break;
540 case PK_BEFORE_SUPERNODE:
542 for (gphi_iterator gpi
543 = const_cast<supernode *> (snode)->start_phis ();
544 !gsi_end_p (gpi); gsi_next (&gpi))
546 gcc_assert (point.get_from_edge ());
547 const cfg_superedge *cfg_sedge
548 = point.get_from_edge ()->dyn_cast_cfg_superedge ();
549 gcc_assert (cfg_sedge);
551 gphi *phi = gpi.phi ();
552 /* Are we at the def-stmt for m_name? */
553 if (phi == def_stmt)
555 if (name_used_by_phis_p (m_name, snode,
556 cfg_sedge->get_phi_arg_idx ()))
558 if (logger)
559 logger->log ("name in def stmt used within phis;"
560 " continuing");
562 else
564 if (logger)
565 logger->log ("name in def stmt not used within phis;"
566 " terminating");
567 return;
572 /* Add given pred to worklist. */
573 if (point.get_from_edge ())
575 gcc_assert (point.get_from_edge ()->m_src);
576 add_to_worklist
577 (function_point::after_supernode (point.get_from_edge ()->m_src),
578 worklist, logger);
580 else
582 /* Add any intraprocedually edge for a call. */
583 if (snode->m_returning_call)
585 gcall *returning_call = snode->m_returning_call;
586 cgraph_edge *cedge
587 = supergraph_call_edge (snode->m_fun,
588 returning_call);
589 if(cedge)
591 superedge *sedge
592 = map.get_sg ().get_intraprocedural_edge_for_call (cedge);
593 gcc_assert (sedge);
594 add_to_worklist
595 (function_point::after_supernode (sedge->m_src),
596 worklist, logger);
598 else
600 supernode *callernode
601 = map.get_sg ().get_supernode_for_stmt (returning_call);
603 gcc_assert (callernode);
604 add_to_worklist
605 (function_point::after_supernode (callernode),
606 worklist, logger);
611 break;
613 case PK_BEFORE_STMT:
615 if (def_stmt == point.get_stmt ())
617 if (logger)
618 logger->log ("def stmt; terminating");
619 return;
621 if (point.get_stmt_idx () > 0)
622 add_to_worklist (function_point::before_stmt
623 (snode, point.get_stmt_idx () - 1),
624 worklist, logger);
625 else
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 FOR_EACH_VEC_ELT (snode->m_preds, i, pred)
632 add_to_worklist (function_point::before_supernode (snode,
633 pred),
634 worklist, logger);
637 break;
639 case PK_AFTER_SUPERNODE:
641 if (snode->m_stmts.length ())
642 add_to_worklist
643 (function_point::before_stmt (snode,
644 snode->m_stmts.length () - 1),
645 worklist, logger);
646 else
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 FOR_EACH_VEC_ELT (snode->m_preds, i, pred)
653 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 if (snode->entry_p ())
661 add_to_worklist
662 (function_point::before_supernode (snode, NULL),
663 worklist, logger);
667 break;
671 /* class state_purge_per_decl : public state_purge_per_tree. */
673 /* state_purge_per_decl's ctor. */
675 state_purge_per_decl::state_purge_per_decl (const state_purge_map &map,
676 tree decl,
677 function *fun)
678 : state_purge_per_tree (fun),
679 m_decl (decl)
681 /* The RESULT_DECL is always needed at the end of its function. */
682 if (TREE_CODE (decl) == RESULT_DECL)
684 supernode *exit_snode = map.get_sg ().get_node_for_function_exit (fun);
685 add_needed_at (function_point::after_supernode (exit_snode));
689 /* Mark the value of the decl (or a subvalue within it) as being needed
690 at POINT. */
692 void
693 state_purge_per_decl::add_needed_at (const function_point &point)
695 m_points_needing_decl.add (point);
698 /* Mark that a pointer to the decl (or a region within it) is taken
699 at POINT. */
701 void
702 state_purge_per_decl::add_pointed_to_at (const function_point &point)
704 m_points_taking_address.add (point);
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. */
714 void
715 state_purge_per_decl::process_worklists (const state_purge_map &map,
716 region_model_manager *mgr)
718 logger *logger = map.get_logger ();
719 LOG_SCOPE (logger);
720 if (logger)
721 logger->log ("decl: %qE within %qD", m_decl, get_fndecl ());
723 /* Worklist for walking backwards from uses. */
725 auto_vec<function_point> worklist;
726 point_set_t seen;
728 /* Add all uses of the decl to the worklist. */
729 for (auto iter : m_points_needing_decl)
730 worklist.safe_push (iter);
732 region_model model (mgr);
733 model.push_frame (get_function (), NULL, NULL);
735 /* Process worklist by walking backwards until we reach a stmt
736 that fully overwrites the decl. */
738 log_scope s (logger, "processing worklist");
739 while (worklist.length () > 0)
741 function_point point = worklist.pop ();
742 process_point_backwards (point, &worklist, &seen, map, model);
747 /* Worklist for walking forwards from address-taken points. */
749 auto_vec<function_point> worklist;
750 point_set_t seen;
752 /* Add all uses of the decl to the worklist. */
753 for (auto iter : m_points_taking_address)
755 worklist.safe_push (iter);
757 /* Add to m_points_needing_decl (now that we traversed
758 it above for the backward worklist). */
759 m_points_needing_decl.add (iter);
762 /* Process worklist by walking forwards. */
764 log_scope s (logger, "processing worklist");
765 while (worklist.length () > 0)
767 function_point point = worklist.pop ();
768 process_point_forwards (point, &worklist, &seen, map);
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. */
777 void
778 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)
783 LOG_FUNC (logger);
784 if (logger)
786 logger->start_log_line ();
787 logger->log_partial ("point: '");
788 point.print (logger->get_printer (), format (false));
789 logger->log_partial ("' for worklist for %qE", m_decl);
790 logger->end_log_line ();
793 gcc_assert (point.get_function () == get_function ());
794 if (point.get_from_edge ())
795 gcc_assert (point.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE);
797 if (seen->contains (point))
799 if (logger)
800 logger->log ("already seen for %qE", m_decl);
802 else
804 if (logger)
805 logger->log ("not seen; adding to worklist for %qE", m_decl);
806 m_points_needing_decl.add (point);
807 seen->add (point);
808 worklist->safe_push (point);
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. */
817 static bool
818 same_binding_p (const region *reg_a, const region *reg_b,
819 store_manager *store_mgr)
821 if (reg_a->get_base_region () != reg_b->get_base_region ())
822 return false;
823 const binding_key *bind_key_a = binding_key::make (store_mgr, reg_a);
824 const binding_key *bind_key_b = binding_key::make (store_mgr, reg_b);
825 return bind_key_a == bind_key_b;
828 /* Return true if STMT fully overwrites DECL. */
830 static bool
831 fully_overwrites_p (const gimple *stmt, tree decl,
832 const region_model &model)
834 if (tree lhs = gimple_get_lhs (stmt))
836 /* Determine if LHS fully overwrites DECL.
837 We can't just check for equality; consider the case of
838 "s.field = EXPR;" where the stmt writes to the only field
839 of "s", and there's no padding. */
840 const region *lhs_reg = model.get_lvalue (lhs, NULL);
841 const region *decl_reg = model.get_lvalue (decl, NULL);
842 if (same_binding_p (lhs_reg, decl_reg,
843 model.get_manager ()->get_store_manager ()))
844 return true;
846 return false;
849 /* Process POINT, popped from *WORKLIST.
850 Iterate over predecessors of POINT, adding to *WORKLIST and *SEEN,
851 until we get to a stmt that fully overwrites the decl. */
853 void
854 state_purge_per_decl::
855 process_point_backwards (const function_point &point,
856 auto_vec<function_point> *worklist,
857 point_set_t *seen,
858 const state_purge_map &map,
859 const region_model &model)
861 logger *logger = map.get_logger ();
862 LOG_FUNC (logger);
863 if (logger)
865 logger->start_log_line ();
866 logger->log_partial ("considering point: '");
867 point.print (logger->get_printer (), format (false));
868 logger->log_partial ("' for %qE", m_decl);
869 logger->end_log_line ();
871 const supernode *snode = point.get_supernode ();
873 switch (point.get_kind ())
875 default:
876 gcc_unreachable ();
878 case PK_ORIGIN:
879 break;
881 case PK_BEFORE_SUPERNODE:
883 /* Add given pred to worklist. */
884 if (point.get_from_edge ())
886 gcc_assert (point.get_from_edge ()->m_src);
887 add_to_worklist
888 (function_point::after_supernode (point.get_from_edge ()->m_src),
889 worklist, seen, logger);
891 else
893 /* Add any intraprocedually edge for a call. */
894 if (snode->m_returning_call)
896 gcall *returning_call = snode->m_returning_call;
897 cgraph_edge *cedge
898 = supergraph_call_edge (snode->m_fun,
899 returning_call);
900 if(cedge)
902 superedge *sedge
903 = map.get_sg ().get_intraprocedural_edge_for_call (cedge);
904 gcc_assert (sedge);
905 add_to_worklist
906 (function_point::after_supernode (sedge->m_src),
907 worklist, seen, logger);
909 else
911 supernode *callernode
912 = map.get_sg ().get_supernode_for_stmt (returning_call);
914 gcc_assert (callernode);
915 add_to_worklist
916 (function_point::after_supernode (callernode),
917 worklist, seen, logger);
922 break;
924 case PK_BEFORE_STMT:
926 /* This is somewhat equivalent to how the SSA case handles
927 def-stmts. */
928 if (fully_overwrites_p (point.get_stmt (), m_decl, model))
930 if (logger)
931 logger->log ("stmt fully overwrites %qE; terminating", m_decl);
932 return;
934 if (point.get_stmt_idx () > 0)
935 add_to_worklist (function_point::before_stmt
936 (snode, point.get_stmt_idx () - 1),
937 worklist, seen, logger);
938 else
940 /* Add before_supernode to worklist. This captures the in-edge,
941 so we have to do it once per in-edge. */
942 unsigned i;
943 superedge *pred;
944 FOR_EACH_VEC_ELT (snode->m_preds, i, pred)
945 add_to_worklist (function_point::before_supernode (snode,
946 pred),
947 worklist, seen, logger);
950 break;
952 case PK_AFTER_SUPERNODE:
954 if (snode->m_stmts.length ())
955 add_to_worklist
956 (function_point::before_stmt (snode,
957 snode->m_stmts.length () - 1),
958 worklist, seen, logger);
959 else
961 /* Add before_supernode to worklist. This captures the in-edge,
962 so we have to do it once per in-edge. */
963 unsigned i;
964 superedge *pred;
965 FOR_EACH_VEC_ELT (snode->m_preds, i, pred)
966 add_to_worklist (function_point::before_supernode (snode,
967 pred),
968 worklist, seen, logger);
971 break;
976 /* Process POINT, popped from *WORKLIST.
977 Iterate over successors of POINT, adding to *WORKLIST and *SEEN. */
979 void
980 state_purge_per_decl::
981 process_point_forwards (const function_point &point,
982 auto_vec<function_point> *worklist,
983 point_set_t *seen,
984 const state_purge_map &map)
986 logger *logger = map.get_logger ();
987 LOG_FUNC (logger);
988 if (logger)
990 logger->start_log_line ();
991 logger->log_partial ("considering point: '");
992 point.print (logger->get_printer (), format (false));
993 logger->log_partial ("' for %qE", m_decl);
994 logger->end_log_line ();
996 const supernode *snode = point.get_supernode ();
998 switch (point.get_kind ())
1000 default:
1001 case PK_ORIGIN:
1002 gcc_unreachable ();
1004 case PK_BEFORE_SUPERNODE:
1006 function_point next = point.get_next ();
1007 add_to_worklist (next, worklist, seen, logger);
1009 break;
1011 case PK_BEFORE_STMT:
1013 /* Perhaps we should stop at a clobber of the decl,
1014 but that ought to purge state for us explicitly. */
1015 function_point next = point.get_next ();
1016 add_to_worklist (next, worklist, seen, logger);
1018 break;
1020 case PK_AFTER_SUPERNODE:
1022 /* Look at interprocedural out-edges. */
1023 unsigned i;
1024 superedge *succ;
1025 FOR_EACH_VEC_ELT (snode->m_succs, i, succ)
1027 enum edge_kind kind = succ->get_kind ();
1028 if (kind == SUPEREDGE_CFG_EDGE
1029 || kind == SUPEREDGE_INTRAPROCEDURAL_CALL)
1030 add_to_worklist (function_point::before_supernode (succ->m_dest,
1031 succ),
1032 worklist, seen, logger);
1035 break;
1039 /* Return true if the decl is needed at POINT. */
1041 bool
1042 state_purge_per_decl::needed_at_point_p (const function_point &point) const
1044 return const_cast <point_set_t &> (m_points_needing_decl).contains (point);
1047 /* class state_purge_annotator : public dot_annotator. */
1049 /* Implementation of dot_annotator::add_node_annotations vfunc for
1050 state_purge_annotator.
1052 Add an additional record showing which names are purged on entry
1053 to the supernode N. */
1055 bool
1056 state_purge_annotator::add_node_annotations (graphviz_out *gv,
1057 const supernode &n,
1058 bool within_table) const
1060 if (m_map == NULL)
1061 return false;
1063 if (within_table)
1064 return false;
1066 pretty_printer *pp = gv->get_pp ();
1068 pp_printf (pp, "annotation_for_node_%i", n.m_index);
1069 pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
1070 "lightblue");
1071 pp_write_text_to_stream (pp);
1073 /* Different in-edges mean different names need purging.
1074 Determine which points to dump. */
1075 auto_vec<function_point> points;
1076 if (n.entry_p () || n.m_returning_call)
1077 points.safe_push (function_point::before_supernode (&n, NULL));
1078 else
1079 for (auto inedge : n.m_preds)
1080 points.safe_push (function_point::before_supernode (&n, inedge));
1081 points.safe_push (function_point::after_supernode (&n));
1083 for (auto & point : points)
1085 point.print (pp, format (true));
1086 pp_newline (pp);
1087 print_needed (gv, point, false);
1088 pp_newline (pp);
1091 pp_string (pp, "\"];\n\n");
1092 pp_flush (pp);
1093 return false;
1096 /* Print V to GV as a comma-separated list in braces, titling it with TITLE.
1097 If WITHIN_TABLE is true, print it within a <TR>
1099 Subroutine of state_purge_annotator::print_needed. */
1101 static void
1102 print_vec_of_names (graphviz_out *gv, const char *title,
1103 const auto_vec<tree> &v, bool within_table)
1105 pretty_printer *pp = gv->get_pp ();
1106 tree name;
1107 unsigned i;
1108 if (within_table)
1109 gv->begin_trtd ();
1110 pp_printf (pp, "%s: {", title);
1111 FOR_EACH_VEC_ELT (v, i, name)
1113 if (i > 0)
1114 pp_string (pp, ", ");
1115 pp_printf (pp, "%qE", name);
1117 pp_printf (pp, "}");
1118 if (within_table)
1120 pp_write_text_as_html_like_dot_to_stream (pp);
1121 gv->end_tdtr ();
1123 pp_newline (pp);
1126 /* Implementation of dot_annotator::add_stmt_annotations for
1127 state_purge_annotator.
1129 Add text showing which names are purged at STMT. */
1131 void
1132 state_purge_annotator::add_stmt_annotations (graphviz_out *gv,
1133 const gimple *stmt,
1134 bool within_row) const
1136 if (within_row)
1137 return;
1139 if (m_map == NULL)
1140 return;
1142 if (stmt->code == GIMPLE_PHI)
1143 return;
1145 pretty_printer *pp = gv->get_pp ();
1147 pp_newline (pp);
1149 const supernode *supernode = m_map->get_sg ().get_supernode_for_stmt (stmt);
1150 unsigned int stmt_idx = supernode->get_stmt_index (stmt);
1151 function_point before_stmt
1152 (function_point::before_stmt (supernode, stmt_idx));
1154 print_needed (gv, before_stmt, true);
1157 /* Get the decls and SSA names needed and not-needed at POINT, and
1158 print them to GV.
1159 If WITHIN_TABLE is true, print them within <TR> elements. */
1161 void
1162 state_purge_annotator::print_needed (graphviz_out *gv,
1163 const function_point &point,
1164 bool within_table) const
1166 auto_vec<tree> needed;
1167 auto_vec<tree> not_needed;
1168 for (state_purge_map::ssa_iterator iter = m_map->begin_ssas ();
1169 iter != m_map->end_ssas ();
1170 ++iter)
1172 tree name = (*iter).first;
1173 state_purge_per_ssa_name *per_name_data = (*iter).second;
1174 if (per_name_data->get_function () == point.get_function ())
1176 if (per_name_data->needed_at_point_p (point))
1177 needed.safe_push (name);
1178 else
1179 not_needed.safe_push (name);
1182 for (state_purge_map::decl_iterator iter = m_map->begin_decls ();
1183 iter != m_map->end_decls ();
1184 ++iter)
1186 tree decl = (*iter).first;
1187 state_purge_per_decl *per_decl_data = (*iter).second;
1188 if (per_decl_data->get_function () == point.get_function ())
1190 if (per_decl_data->needed_at_point_p (point))
1191 needed.safe_push (decl);
1192 else
1193 not_needed.safe_push (decl);
1197 print_vec_of_names (gv, "needed here", needed, within_table);
1198 print_vec_of_names (gv, "not needed here", not_needed, within_table);
1201 #endif /* #if ENABLE_ANALYZER */