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