1 /* Classes for purging state at function_points.
2 Copyright (C) 2019-2023 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)
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/>. */
22 #define INCLUDE_MEMORY
24 #include "coretypes.h"
27 #include "tree-ssa-alias.h"
29 #include "basic-block.h"
31 #include "stringpool.h"
33 #include "gimple-ssa.h"
34 #include "tree-ssanames.h"
35 #include "tree-phinodes.h"
37 #include "ssa-iterators.h"
38 #include "diagnostic-core.h"
39 #include "gimple-pretty-print.h"
40 #include "analyzer/analyzer.h"
41 #include "analyzer/call-string.h"
42 #include "analyzer/supergraph.h"
43 #include "analyzer/program-point.h"
44 #include "analyzer/analyzer-logging.h"
45 #include "analyzer/state-purge.h"
46 #include "analyzer/store.h"
47 #include "analyzer/region-model.h"
48 #include "gimple-walk.h"
53 /* Given NODE at an access, determine if this access is within
54 a decl that could be consider for purging, and if so, return the decl. */
57 get_candidate_for_purging (tree node
)
61 switch (TREE_CODE (iter
))
69 iter
= TREE_OPERAND (iter
, 0);
73 if (is_global_var (iter
))
84 /* Class-based handler for walk_stmt_load_store_addr_ops at a particular
85 function_point, for populating the worklists within a state_purge_map. */
87 class gimple_op_visitor
: public log_user
90 gimple_op_visitor (state_purge_map
*map
,
91 const function_point
&point
,
93 : log_user (map
->get_logger ()),
99 bool on_load (gimple
*stmt
, tree base
, tree op
)
101 LOG_FUNC (get_logger ());
105 pp_gimple_stmt_1 (&pp
, stmt
, 0, (dump_flags_t
)0);
106 log ("on_load: %s; base: %qE, op: %qE",
107 pp_formatted_text (&pp
), base
, op
);
109 if (tree node
= get_candidate_for_purging (base
))
114 bool on_store (gimple
*stmt
, tree base
, tree op
)
116 LOG_FUNC (get_logger ());
120 pp_gimple_stmt_1 (&pp
, stmt
, 0, (dump_flags_t
)0);
121 log ("on_store: %s; base: %qE, op: %qE",
122 pp_formatted_text (&pp
), base
, op
);
127 bool on_addr (gimple
*stmt
, tree base
, tree op
)
129 LOG_FUNC (get_logger ());
133 pp_gimple_stmt_1 (&pp
, stmt
, 0, (dump_flags_t
)0);
134 log ("on_addr: %s; base: %qE, op: %qE",
135 pp_formatted_text (&pp
), base
, op
);
137 if (TREE_CODE (op
) != ADDR_EXPR
)
139 if (tree node
= get_candidate_for_purging (base
))
142 add_pointed_to (node
);
148 void add_needed (tree decl
)
150 gcc_assert (get_candidate_for_purging (decl
) == decl
);
151 state_purge_per_decl
&data
152 = get_or_create_data_for_decl (decl
);
153 data
.add_needed_at (m_point
);
155 /* Handle calls: if we're seeing a use at a call, then add a use at the
156 "after-supernode" point (in case of interprocedural call superedges). */
157 if (m_point
.final_stmt_p ())
158 data
.add_needed_at (m_point
.get_next ());
161 void add_pointed_to (tree decl
)
163 gcc_assert (get_candidate_for_purging (decl
) == decl
);
164 get_or_create_data_for_decl (decl
).add_pointed_to_at (m_point
);
167 state_purge_per_decl
&
168 get_or_create_data_for_decl (tree decl
)
170 return m_map
->get_or_create_data_for_decl (m_fun
, decl
);
173 state_purge_map
*m_map
;
174 const function_point
&m_point
;
179 my_load_cb (gimple
*stmt
, tree base
, tree op
, void *user_data
)
181 gimple_op_visitor
*x
= (gimple_op_visitor
*)user_data
;
182 return x
->on_load (stmt
, base
, op
);
186 my_store_cb (gimple
*stmt
, tree base
, tree op
, void *user_data
)
188 gimple_op_visitor
*x
= (gimple_op_visitor
*)user_data
;
189 return x
->on_store (stmt
, base
, op
);
193 my_addr_cb (gimple
*stmt
, tree base
, tree op
, void *user_data
)
195 gimple_op_visitor
*x
= (gimple_op_visitor
*)user_data
;
196 return x
->on_addr (stmt
, base
, op
);
199 /* state_purge_map's ctor. Walk all SSA names in all functions, building
200 a state_purge_per_ssa_name instance for each.
201 Also, walk all loads and address-taken ops of local variables, building
202 a state_purge_per_decl as appropriate. */
204 state_purge_map::state_purge_map (const supergraph
&sg
,
205 region_model_manager
*mgr
,
207 : log_user (logger
), m_sg (sg
)
211 auto_timevar
tv (TV_ANALYZER_STATE_PURGE
);
214 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node
)
216 function
*fun
= node
->get_fun ();
218 log ("function: %s", function_name (fun
));
221 FOR_EACH_SSA_NAME (i
, name
, fun
)
223 /* For now, don't bother tracking the .MEM SSA names. */
224 if (tree var
= SSA_NAME_VAR (name
))
225 if (TREE_CODE (var
) == VAR_DECL
)
226 if (VAR_DECL_IS_VIRTUAL_OPERAND (var
))
228 m_ssa_map
.put (name
, new state_purge_per_ssa_name (*this, name
, fun
));
232 /* Find all uses of local vars.
233 We iterate through all function points, finding loads, stores, and
234 address-taken operations on locals, building a pair of worklists. */
235 for (auto snode
: sg
.m_nodes
)
238 log ("SN: %i", snode
->m_index
);
239 /* We ignore m_returning_call and phi nodes. */
242 FOR_EACH_VEC_ELT (snode
->m_stmts
, i
, stmt
)
244 function_point
point (function_point::before_stmt (snode
, i
));
245 gimple_op_visitor
v (this, point
, snode
->get_function ());
246 walk_stmt_load_store_addr_ops (stmt
, &v
,
247 my_load_cb
, my_store_cb
, my_addr_cb
);
251 /* Now iterate through the m_decl_map, processing each pair of worklists. */
252 for (state_purge_map::decl_iterator iter
= begin_decls ();
253 iter
!= end_decls ();
256 state_purge_per_decl
*per_decl_data
= (*iter
).second
;
257 per_decl_data
->process_worklists (*this, mgr
);
261 /* state_purge_map's dtor. */
263 state_purge_map::~state_purge_map ()
265 for (auto iter
: m_ssa_map
)
267 for (auto iter
: m_decl_map
)
271 /* Get the state_purge_per_decl for local DECL within FUN, creating it
274 state_purge_per_decl
&
275 state_purge_map::get_or_create_data_for_decl (function
*fun
, tree decl
)
277 if (state_purge_per_decl
**slot
278 = const_cast <decl_map_t
&> (m_decl_map
).get (decl
))
280 state_purge_per_decl
*result
= new state_purge_per_decl (*this, decl
, fun
);
281 m_decl_map
.put (decl
, result
);
285 /* class state_purge_per_ssa_name : public state_purge_per_tree. */
287 /* state_purge_per_ssa_name's ctor.
289 Locate all uses of VAR within FUN.
290 Walk backwards from each use, marking program points, until
291 we reach the def stmt, populating m_points_needing_var.
293 We have to track program points rather than
294 just stmts since there could be empty basic blocks on the way. */
296 state_purge_per_ssa_name::state_purge_per_ssa_name (const state_purge_map
&map
,
299 : state_purge_per_tree (fun
), m_points_needing_name (), m_name (name
)
301 LOG_FUNC (map
.get_logger ());
303 if (map
.get_logger ())
305 map
.log ("SSA name: %qE within %qD", name
, fun
->decl
);
308 const gimple
*def_stmt
= SSA_NAME_DEF_STMT (name
);
310 pp_gimple_stmt_1 (&pp
, def_stmt
, 0, (dump_flags_t
)0);
311 map
.log ("def stmt: %s", pp_formatted_text (&pp
));
314 auto_vec
<function_point
> worklist
;
316 /* Add all immediate uses of name to the worklist.
317 Compare with debug_immediate_uses. */
318 imm_use_iterator iter
;
320 FOR_EACH_IMM_USE_FAST (use_p
, iter
, name
)
322 if (USE_STMT (use_p
))
324 const gimple
*use_stmt
= USE_STMT (use_p
);
325 if (map
.get_logger ())
328 pp_gimple_stmt_1 (&pp
, use_stmt
, 0, (dump_flags_t
)0);
329 map
.log ("used by stmt: %s", pp_formatted_text (&pp
));
332 const supernode
*snode
333 = map
.get_sg ().get_supernode_for_stmt (use_stmt
);
335 /* If it's a use within a phi node, then we care about
336 which in-edge we came from. */
337 if (use_stmt
->code
== GIMPLE_PHI
)
339 for (gphi_iterator gpi
340 = const_cast<supernode
*> (snode
)->start_phis ();
341 !gsi_end_p (gpi
); gsi_next (&gpi
))
343 gphi
*phi
= gpi
.phi ();
346 /* Find arguments (and thus in-edges) which use NAME. */
347 for (unsigned arg_idx
= 0;
348 arg_idx
< gimple_phi_num_args (phi
);
351 if (name
== gimple_phi_arg (phi
, arg_idx
)->def
)
353 edge in_edge
= gimple_phi_arg_edge (phi
, arg_idx
);
354 const superedge
*in_sedge
355 = map
.get_sg ().get_edge_for_cfg_edge (in_edge
);
357 = function_point::before_supernode
359 add_to_worklist (point
, &worklist
,
361 m_points_needing_name
.add (point
);
369 function_point point
= before_use_stmt (map
, use_stmt
);
370 add_to_worklist (point
, &worklist
, map
.get_logger ());
371 m_points_needing_name
.add (point
);
373 /* We also need to add uses for conditionals and switches,
374 where the stmt "happens" at the after_supernode, for filtering
376 if (use_stmt
== snode
->get_last_stmt ())
378 if (map
.get_logger ())
379 map
.log ("last stmt in BB");
381 = function_point::after_supernode (snode
);
382 add_to_worklist (point
, &worklist
, map
.get_logger ());
383 m_points_needing_name
.add (point
);
386 if (map
.get_logger ())
387 map
.log ("not last stmt in BB");
392 /* Process worklist by walking backwards until we reach the def stmt. */
394 log_scope
s (map
.get_logger (), "processing worklist");
395 while (worklist
.length () > 0)
397 function_point point
= worklist
.pop ();
398 process_point (point
, &worklist
, map
);
402 if (map
.get_logger ())
404 map
.log ("%qE in %qD is needed to process:", name
, fun
->decl
);
405 /* Log m_points_needing_name, sorting it to avoid churn when comparing
407 auto_vec
<function_point
> points
;
408 for (point_set_t::iterator iter
= m_points_needing_name
.begin ();
409 iter
!= m_points_needing_name
.end ();
411 points
.safe_push (*iter
);
412 points
.qsort (function_point::cmp_ptr
);
414 function_point
*point
;
415 FOR_EACH_VEC_ELT (points
, i
, point
)
417 map
.start_log_line ();
418 map
.get_logger ()->log_partial (" point: ");
419 point
->print (map
.get_logger ()->get_printer (), format (false));
425 /* Return true if the SSA name is needed at POINT. */
428 state_purge_per_ssa_name::needed_at_point_p (const function_point
&point
) const
430 return const_cast <point_set_t
&> (m_points_needing_name
).contains (point
);
433 /* Get the function_point representing immediately before USE_STMT.
434 Subroutine of ctor. */
437 state_purge_per_ssa_name::before_use_stmt (const state_purge_map
&map
,
438 const gimple
*use_stmt
)
440 gcc_assert (use_stmt
->code
!= GIMPLE_PHI
);
442 const supernode
*supernode
443 = map
.get_sg ().get_supernode_for_stmt (use_stmt
);
444 unsigned int stmt_idx
= supernode
->get_stmt_index (use_stmt
);
445 return function_point::before_stmt (supernode
, stmt_idx
);
448 /* Add POINT to *WORKLIST if the point has not already been seen.
449 Subroutine of ctor. */
452 state_purge_per_ssa_name::add_to_worklist (const function_point
&point
,
453 auto_vec
<function_point
> *worklist
,
459 logger
->start_log_line ();
460 logger
->log_partial ("point: '");
461 point
.print (logger
->get_printer (), format (false));
462 logger
->log_partial ("' for worklist for %qE", m_name
);
463 logger
->end_log_line ();
466 gcc_assert (point
.get_function () == get_function ());
467 if (point
.get_from_edge ())
468 gcc_assert (point
.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE
);
470 if (m_points_needing_name
.contains (point
))
473 logger
->log ("already seen for %qE", m_name
);
478 logger
->log ("not seen; adding to worklist for %qE", m_name
);
479 m_points_needing_name
.add (point
);
480 worklist
->safe_push (point
);
484 /* Return true iff NAME is used by any of the phi nodes in SNODE
485 when processing the in-edge with PHI_ARG_IDX. */
488 name_used_by_phis_p (tree name
, const supernode
*snode
,
491 gcc_assert (TREE_CODE (name
) == SSA_NAME
);
493 for (gphi_iterator gpi
494 = const_cast<supernode
*> (snode
)->start_phis ();
495 !gsi_end_p (gpi
); gsi_next (&gpi
))
497 gphi
*phi
= gpi
.phi ();
498 if (gimple_phi_arg_def (phi
, phi_arg_idx
) == name
)
504 /* Process POINT, popped from WORKLIST.
505 Iterate over predecessors of POINT, adding to WORKLIST. */
508 state_purge_per_ssa_name::process_point (const function_point
&point
,
509 auto_vec
<function_point
> *worklist
,
510 const state_purge_map
&map
)
512 logger
*logger
= map
.get_logger ();
516 logger
->start_log_line ();
517 logger
->log_partial ("considering point: '");
518 point
.print (logger
->get_printer (), format (false));
519 logger
->log_partial ("' for %qE", m_name
);
520 logger
->end_log_line ();
523 gimple
*def_stmt
= SSA_NAME_DEF_STMT (m_name
);
525 const supernode
*snode
= point
.get_supernode ();
527 switch (point
.get_kind ())
535 case PK_BEFORE_SUPERNODE
:
537 for (gphi_iterator gpi
538 = const_cast<supernode
*> (snode
)->start_phis ();
539 !gsi_end_p (gpi
); gsi_next (&gpi
))
541 gcc_assert (point
.get_from_edge ());
542 const cfg_superedge
*cfg_sedge
543 = point
.get_from_edge ()->dyn_cast_cfg_superedge ();
544 gcc_assert (cfg_sedge
);
546 gphi
*phi
= gpi
.phi ();
547 /* Are we at the def-stmt for m_name? */
550 if (name_used_by_phis_p (m_name
, snode
,
551 cfg_sedge
->get_phi_arg_idx ()))
554 logger
->log ("name in def stmt used within phis;"
560 logger
->log ("name in def stmt not used within phis;"
567 /* Add given pred to worklist. */
568 if (point
.get_from_edge ())
570 gcc_assert (point
.get_from_edge ()->m_src
);
572 (function_point::after_supernode (point
.get_from_edge ()->m_src
),
577 /* Add any intraprocedually edge for a call. */
578 if (snode
->m_returning_call
)
580 gcall
*returning_call
= snode
->m_returning_call
;
582 = supergraph_call_edge (snode
->m_fun
,
587 = map
.get_sg ().get_intraprocedural_edge_for_call (cedge
);
590 (function_point::after_supernode (sedge
->m_src
),
595 supernode
*callernode
596 = map
.get_sg ().get_supernode_for_stmt (returning_call
);
598 gcc_assert (callernode
);
600 (function_point::after_supernode (callernode
),
610 if (def_stmt
== point
.get_stmt ())
613 logger
->log ("def stmt; terminating");
616 if (point
.get_stmt_idx () > 0)
617 add_to_worklist (function_point::before_stmt
618 (snode
, point
.get_stmt_idx () - 1),
622 /* Add before_supernode to worklist. This captures the in-edge,
623 so we have to do it once per in-edge. */
626 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
627 add_to_worklist (function_point::before_supernode (snode
,
634 case PK_AFTER_SUPERNODE
:
636 if (snode
->m_stmts
.length ())
638 (function_point::before_stmt (snode
,
639 snode
->m_stmts
.length () - 1),
643 /* Add before_supernode to worklist. This captures the in-edge,
644 so we have to do it once per in-edge. */
647 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
648 add_to_worklist (function_point::before_supernode (snode
,
651 /* If it's the initial BB, add it, to ensure that we
652 have "before supernode" for the initial ENTRY block, and don't
653 erroneously purge SSA names for initial values of parameters. */
654 if (snode
->entry_p ())
657 (function_point::before_supernode (snode
, NULL
),
666 /* class state_purge_per_decl : public state_purge_per_tree. */
668 /* state_purge_per_decl's ctor. */
670 state_purge_per_decl::state_purge_per_decl (const state_purge_map
&map
,
673 : state_purge_per_tree (fun
),
676 /* The RESULT_DECL is always needed at the end of its function. */
677 if (TREE_CODE (decl
) == RESULT_DECL
)
679 supernode
*exit_snode
= map
.get_sg ().get_node_for_function_exit (fun
);
680 add_needed_at (function_point::after_supernode (exit_snode
));
684 /* Mark the value of the decl (or a subvalue within it) as being needed
688 state_purge_per_decl::add_needed_at (const function_point
&point
)
690 m_points_needing_decl
.add (point
);
693 /* Mark that a pointer to the decl (or a region within it) is taken
697 state_purge_per_decl::add_pointed_to_at (const function_point
&point
)
699 m_points_taking_address
.add (point
);
702 /* Process the worklists for this decl:
703 (a) walk backwards from points where we know the value of the decl
704 is needed, marking points until we get to a stmt that fully overwrites
706 (b) walk forwards from points where the address of the decl is taken,
707 marking points as potentially needing the value of the decl. */
710 state_purge_per_decl::process_worklists (const state_purge_map
&map
,
711 region_model_manager
*mgr
)
713 logger
*logger
= map
.get_logger ();
716 logger
->log ("decl: %qE within %qD", m_decl
, get_fndecl ());
718 /* Worklist for walking backwards from uses. */
720 auto_vec
<function_point
> worklist
;
723 /* Add all uses of the decl to the worklist. */
724 for (auto iter
: m_points_needing_decl
)
725 worklist
.safe_push (iter
);
727 region_model
model (mgr
);
728 model
.push_frame (get_function (), NULL
, NULL
);
730 /* Process worklist by walking backwards until we reach a stmt
731 that fully overwrites the decl. */
733 log_scope
s (logger
, "processing worklist");
734 while (worklist
.length () > 0)
736 function_point point
= worklist
.pop ();
737 process_point_backwards (point
, &worklist
, &seen
, map
, model
);
742 /* Worklist for walking forwards from address-taken points. */
744 auto_vec
<function_point
> worklist
;
747 /* Add all uses of the decl to the worklist. */
748 for (auto iter
: m_points_taking_address
)
750 worklist
.safe_push (iter
);
752 /* Add to m_points_needing_decl (now that we traversed
753 it above for the backward worklist). */
754 m_points_needing_decl
.add (iter
);
757 /* Process worklist by walking forwards. */
759 log_scope
s (logger
, "processing worklist");
760 while (worklist
.length () > 0)
762 function_point point
= worklist
.pop ();
763 process_point_forwards (point
, &worklist
, &seen
, map
);
769 /* Add POINT to *WORKLIST if the point is not already in *seen.
770 Add newly seen points to *SEEN and to m_points_needing_name. */
773 state_purge_per_decl::add_to_worklist (const function_point
&point
,
774 auto_vec
<function_point
> *worklist
,
781 logger
->start_log_line ();
782 logger
->log_partial ("point: '");
783 point
.print (logger
->get_printer (), format (false));
784 logger
->log_partial ("' for worklist for %qE", m_decl
);
785 logger
->end_log_line ();
788 gcc_assert (point
.get_function () == get_function ());
789 if (point
.get_from_edge ())
790 gcc_assert (point
.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE
);
792 if (seen
->contains (point
))
795 logger
->log ("already seen for %qE", m_decl
);
800 logger
->log ("not seen; adding to worklist for %qE", m_decl
);
801 m_points_needing_decl
.add (point
);
803 worklist
->safe_push (point
);
807 /* Determine if REG_A and REG_B express writing to exactly the same
809 For example, when "s.field" is the only field of "s", and there's no
810 padding, this should return true. */
813 same_binding_p (const region
*reg_a
, const region
*reg_b
,
814 store_manager
*store_mgr
)
816 if (reg_a
->get_base_region () != reg_b
->get_base_region ())
818 if (reg_a
->empty_p ())
820 const binding_key
*bind_key_a
= binding_key::make (store_mgr
, reg_a
);
821 if (reg_b
->empty_p ())
823 const binding_key
*bind_key_b
= binding_key::make (store_mgr
, reg_b
);
824 return bind_key_a
== bind_key_b
;
827 /* Return true if STMT fully overwrites DECL. */
830 fully_overwrites_p (const gimple
*stmt
, tree decl
,
831 const region_model
&model
)
833 if (tree lhs
= gimple_get_lhs (stmt
))
835 /* Determine if LHS fully overwrites DECL.
836 We can't just check for equality; consider the case of
837 "s.field = EXPR;" where the stmt writes to the only field
838 of "s", and there's no padding. */
839 const region
*lhs_reg
= model
.get_lvalue (lhs
, NULL
);
840 const region
*decl_reg
= model
.get_lvalue (decl
, NULL
);
841 if (same_binding_p (lhs_reg
, decl_reg
,
842 model
.get_manager ()->get_store_manager ()))
848 /* Process POINT, popped from *WORKLIST.
849 Iterate over predecessors of POINT, adding to *WORKLIST and *SEEN,
850 until we get to a stmt that fully overwrites the decl. */
853 state_purge_per_decl::
854 process_point_backwards (const function_point
&point
,
855 auto_vec
<function_point
> *worklist
,
857 const state_purge_map
&map
,
858 const region_model
&model
)
860 logger
*logger
= map
.get_logger ();
864 logger
->start_log_line ();
865 logger
->log_partial ("considering point: '");
866 point
.print (logger
->get_printer (), format (false));
867 logger
->log_partial ("' for %qE", m_decl
);
868 logger
->end_log_line ();
870 const supernode
*snode
= point
.get_supernode ();
872 switch (point
.get_kind ())
880 case PK_BEFORE_SUPERNODE
:
882 /* Add given pred to worklist. */
883 if (point
.get_from_edge ())
885 gcc_assert (point
.get_from_edge ()->m_src
);
887 (function_point::after_supernode (point
.get_from_edge ()->m_src
),
888 worklist
, seen
, logger
);
892 /* Add any intraprocedually edge for a call. */
893 if (snode
->m_returning_call
)
895 gcall
*returning_call
= snode
->m_returning_call
;
897 = supergraph_call_edge (snode
->m_fun
,
902 = map
.get_sg ().get_intraprocedural_edge_for_call (cedge
);
905 (function_point::after_supernode (sedge
->m_src
),
906 worklist
, seen
, logger
);
910 supernode
*callernode
911 = map
.get_sg ().get_supernode_for_stmt (returning_call
);
913 gcc_assert (callernode
);
915 (function_point::after_supernode (callernode
),
916 worklist
, seen
, logger
);
925 /* This is somewhat equivalent to how the SSA case handles
927 if (fully_overwrites_p (point
.get_stmt (), m_decl
, model
)
928 /* ...but we mustn't be at a point that also consumes the
929 current value of the decl when it's generating the new
930 value, for cases such as
934 where we want to make sure that we don't stop at the:
936 since otherwise we would erroneously purge the state of "s"
940 && !m_points_needing_decl
.contains (point
))
943 logger
->log ("stmt fully overwrites %qE; terminating", m_decl
);
946 if (point
.get_stmt_idx () > 0)
947 add_to_worklist (function_point::before_stmt
948 (snode
, point
.get_stmt_idx () - 1),
949 worklist
, seen
, logger
);
952 /* Add before_supernode to worklist. This captures the in-edge,
953 so we have to do it once per in-edge. */
956 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
957 add_to_worklist (function_point::before_supernode (snode
,
959 worklist
, seen
, logger
);
964 case PK_AFTER_SUPERNODE
:
966 if (snode
->m_stmts
.length ())
968 (function_point::before_stmt (snode
,
969 snode
->m_stmts
.length () - 1),
970 worklist
, seen
, logger
);
973 /* Add before_supernode to worklist. This captures the in-edge,
974 so we have to do it once per in-edge. */
977 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
978 add_to_worklist (function_point::before_supernode (snode
,
980 worklist
, seen
, logger
);
988 /* Process POINT, popped from *WORKLIST.
989 Iterate over successors of POINT, adding to *WORKLIST and *SEEN. */
992 state_purge_per_decl::
993 process_point_forwards (const function_point
&point
,
994 auto_vec
<function_point
> *worklist
,
996 const state_purge_map
&map
)
998 logger
*logger
= map
.get_logger ();
1002 logger
->start_log_line ();
1003 logger
->log_partial ("considering point: '");
1004 point
.print (logger
->get_printer (), format (false));
1005 logger
->log_partial ("' for %qE", m_decl
);
1006 logger
->end_log_line ();
1008 const supernode
*snode
= point
.get_supernode ();
1010 switch (point
.get_kind ())
1016 case PK_BEFORE_SUPERNODE
:
1018 function_point next
= point
.get_next ();
1019 add_to_worklist (next
, worklist
, seen
, logger
);
1023 case PK_BEFORE_STMT
:
1025 /* Perhaps we should stop at a clobber of the decl,
1026 but that ought to purge state for us explicitly. */
1027 function_point next
= point
.get_next ();
1028 add_to_worklist (next
, worklist
, seen
, logger
);
1032 case PK_AFTER_SUPERNODE
:
1034 /* Look at interprocedural out-edges. */
1037 FOR_EACH_VEC_ELT (snode
->m_succs
, i
, succ
)
1039 enum edge_kind kind
= succ
->get_kind ();
1040 if (kind
== SUPEREDGE_CFG_EDGE
1041 || kind
== SUPEREDGE_INTRAPROCEDURAL_CALL
)
1042 add_to_worklist (function_point::before_supernode (succ
->m_dest
,
1044 worklist
, seen
, logger
);
1051 /* Return true if the decl is needed at POINT. */
1054 state_purge_per_decl::needed_at_point_p (const function_point
&point
) const
1056 return const_cast <point_set_t
&> (m_points_needing_decl
).contains (point
);
1059 /* class state_purge_annotator : public dot_annotator. */
1061 /* Implementation of dot_annotator::add_node_annotations vfunc for
1062 state_purge_annotator.
1064 Add an additional record showing which names are purged on entry
1065 to the supernode N. */
1068 state_purge_annotator::add_node_annotations (graphviz_out
*gv
,
1070 bool within_table
) const
1078 pretty_printer
*pp
= gv
->get_pp ();
1080 pp_printf (pp
, "annotation_for_node_%i", n
.m_index
);
1081 pp_printf (pp
, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
1083 pp_write_text_to_stream (pp
);
1085 /* Different in-edges mean different names need purging.
1086 Determine which points to dump. */
1087 auto_vec
<function_point
> points
;
1088 if (n
.entry_p () || n
.m_returning_call
)
1089 points
.safe_push (function_point::before_supernode (&n
, NULL
));
1091 for (auto inedge
: n
.m_preds
)
1092 points
.safe_push (function_point::before_supernode (&n
, inedge
));
1093 points
.safe_push (function_point::after_supernode (&n
));
1095 for (auto & point
: points
)
1097 point
.print (pp
, format (true));
1099 print_needed (gv
, point
, false);
1103 pp_string (pp
, "\"];\n\n");
1108 /* Print V to GV as a comma-separated list in braces, titling it with TITLE.
1109 If WITHIN_TABLE is true, print it within a <TR>
1111 Subroutine of state_purge_annotator::print_needed. */
1114 print_vec_of_names (graphviz_out
*gv
, const char *title
,
1115 const auto_vec
<tree
> &v
, bool within_table
)
1117 pretty_printer
*pp
= gv
->get_pp ();
1122 pp_printf (pp
, "%s: {", title
);
1123 FOR_EACH_VEC_ELT (v
, i
, name
)
1126 pp_string (pp
, ", ");
1127 pp_printf (pp
, "%qE", name
);
1129 pp_printf (pp
, "}");
1132 pp_write_text_as_html_like_dot_to_stream (pp
);
1138 /* Implementation of dot_annotator::add_stmt_annotations for
1139 state_purge_annotator.
1141 Add text showing which names are purged at STMT. */
1144 state_purge_annotator::add_stmt_annotations (graphviz_out
*gv
,
1146 bool within_row
) const
1154 if (stmt
->code
== GIMPLE_PHI
)
1157 pretty_printer
*pp
= gv
->get_pp ();
1161 const supernode
*supernode
= m_map
->get_sg ().get_supernode_for_stmt (stmt
);
1162 unsigned int stmt_idx
= supernode
->get_stmt_index (stmt
);
1163 function_point before_stmt
1164 (function_point::before_stmt (supernode
, stmt_idx
));
1166 print_needed (gv
, before_stmt
, true);
1169 /* Get the decls and SSA names needed and not-needed at POINT, and
1171 If WITHIN_TABLE is true, print them within <TR> elements. */
1174 state_purge_annotator::print_needed (graphviz_out
*gv
,
1175 const function_point
&point
,
1176 bool within_table
) const
1178 auto_vec
<tree
> needed
;
1179 auto_vec
<tree
> not_needed
;
1180 for (state_purge_map::ssa_iterator iter
= m_map
->begin_ssas ();
1181 iter
!= m_map
->end_ssas ();
1184 tree name
= (*iter
).first
;
1185 state_purge_per_ssa_name
*per_name_data
= (*iter
).second
;
1186 if (per_name_data
->get_function () == point
.get_function ())
1188 if (per_name_data
->needed_at_point_p (point
))
1189 needed
.safe_push (name
);
1191 not_needed
.safe_push (name
);
1194 for (state_purge_map::decl_iterator iter
= m_map
->begin_decls ();
1195 iter
!= m_map
->end_decls ();
1198 tree decl
= (*iter
).first
;
1199 state_purge_per_decl
*per_decl_data
= (*iter
).second
;
1200 if (per_decl_data
->get_function () == point
.get_function ())
1202 if (per_decl_data
->needed_at_point_p (point
))
1203 needed
.safe_push (decl
);
1205 not_needed
.safe_push (decl
);
1209 print_vec_of_names (gv
, "needed here", needed
, within_table
);
1210 print_vec_of_names (gv
, "not needed here", not_needed
, within_table
);
1213 #endif /* #if ENABLE_ANALYZER */