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