1 /* Classes for purging state at function_points.
2 Copyright (C) 2019-2022 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
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
))
67 iter
= TREE_OPERAND (iter
, 0);
71 if (is_global_var (iter
))
82 /* Class-based handler for walk_stmt_load_store_addr_ops at a particular
83 function_point, for populating the worklists within a state_purge_map. */
85 class gimple_op_visitor
: public log_user
88 gimple_op_visitor (state_purge_map
*map
,
89 const function_point
&point
,
91 : log_user (map
->get_logger ()),
97 bool on_load (gimple
*stmt
, tree base
, tree op
)
99 LOG_FUNC (get_logger ());
103 pp_gimple_stmt_1 (&pp
, stmt
, 0, (dump_flags_t
)0);
104 log ("on_load: %s; base: %qE, op: %qE",
105 pp_formatted_text (&pp
), base
, op
);
107 if (tree node
= get_candidate_for_purging (base
))
112 bool on_store (gimple
*stmt
, tree base
, tree op
)
114 LOG_FUNC (get_logger ());
118 pp_gimple_stmt_1 (&pp
, stmt
, 0, (dump_flags_t
)0);
119 log ("on_store: %s; base: %qE, op: %qE",
120 pp_formatted_text (&pp
), base
, op
);
125 bool on_addr (gimple
*stmt
, tree base
, tree op
)
127 LOG_FUNC (get_logger ());
131 pp_gimple_stmt_1 (&pp
, stmt
, 0, (dump_flags_t
)0);
132 log ("on_addr: %s; base: %qE, op: %qE",
133 pp_formatted_text (&pp
), base
, op
);
135 if (TREE_CODE (op
) != ADDR_EXPR
)
137 if (tree node
= get_candidate_for_purging (base
))
140 add_pointed_to (node
);
146 void add_needed (tree decl
)
148 gcc_assert (get_candidate_for_purging (decl
) == decl
);
149 state_purge_per_decl
&data
150 = get_or_create_data_for_decl (decl
);
151 data
.add_needed_at (m_point
);
153 /* Handle calls: if we're seeing a use at a call, then add a use at the
154 "after-supernode" point (in case of interprocedural call superedges). */
155 if (m_point
.final_stmt_p ())
156 data
.add_needed_at (m_point
.get_next ());
159 void add_pointed_to (tree decl
)
161 gcc_assert (get_candidate_for_purging (decl
) == decl
);
162 get_or_create_data_for_decl (decl
).add_pointed_to_at (m_point
);
165 state_purge_per_decl
&
166 get_or_create_data_for_decl (tree decl
)
168 return m_map
->get_or_create_data_for_decl (m_fun
, decl
);
171 state_purge_map
*m_map
;
172 const function_point
&m_point
;
177 my_load_cb (gimple
*stmt
, tree base
, tree op
, void *user_data
)
179 gimple_op_visitor
*x
= (gimple_op_visitor
*)user_data
;
180 return x
->on_load (stmt
, base
, op
);
184 my_store_cb (gimple
*stmt
, tree base
, tree op
, void *user_data
)
186 gimple_op_visitor
*x
= (gimple_op_visitor
*)user_data
;
187 return x
->on_store (stmt
, base
, op
);
191 my_addr_cb (gimple
*stmt
, tree base
, tree op
, void *user_data
)
193 gimple_op_visitor
*x
= (gimple_op_visitor
*)user_data
;
194 return x
->on_addr (stmt
, base
, op
);
197 /* state_purge_map's ctor. Walk all SSA names in all functions, building
198 a state_purge_per_ssa_name instance for each.
199 Also, walk all loads and address-taken ops of local variables, building
200 a state_purge_per_decl as appropriate. */
202 state_purge_map::state_purge_map (const supergraph
&sg
,
203 region_model_manager
*mgr
,
205 : log_user (logger
), m_sg (sg
)
209 auto_timevar
tv (TV_ANALYZER_STATE_PURGE
);
212 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node
)
214 function
*fun
= node
->get_fun ();
216 log ("function: %s", function_name (fun
));
219 FOR_EACH_SSA_NAME (i
, name
, fun
)
221 /* For now, don't bother tracking the .MEM SSA names. */
222 if (tree var
= SSA_NAME_VAR (name
))
223 if (TREE_CODE (var
) == VAR_DECL
)
224 if (VAR_DECL_IS_VIRTUAL_OPERAND (var
))
226 m_ssa_map
.put (name
, new state_purge_per_ssa_name (*this, name
, fun
));
230 /* Find all uses of local vars.
231 We iterate through all function points, finding loads, stores, and
232 address-taken operations on locals, building a pair of worklists. */
233 for (auto snode
: sg
.m_nodes
)
236 log ("SN: %i", snode
->m_index
);
237 /* We ignore m_returning_call and phi nodes. */
240 FOR_EACH_VEC_ELT (snode
->m_stmts
, i
, stmt
)
242 function_point
point (function_point::before_stmt (snode
, i
));
243 gimple_op_visitor
v (this, point
, snode
->get_function ());
244 walk_stmt_load_store_addr_ops (stmt
, &v
,
245 my_load_cb
, my_store_cb
, my_addr_cb
);
249 /* Now iterate through the m_decl_map, processing each pair of worklists. */
250 for (state_purge_map::decl_iterator iter
= begin_decls ();
251 iter
!= end_decls ();
254 state_purge_per_decl
*per_decl_data
= (*iter
).second
;
255 per_decl_data
->process_worklists (*this, mgr
);
259 /* state_purge_map's dtor. */
261 state_purge_map::~state_purge_map ()
263 for (auto iter
: m_ssa_map
)
265 for (auto iter
: m_decl_map
)
269 /* Get the state_purge_per_decl for local DECL within FUN, creating it
272 state_purge_per_decl
&
273 state_purge_map::get_or_create_data_for_decl (function
*fun
, tree decl
)
275 if (state_purge_per_decl
**slot
276 = const_cast <decl_map_t
&> (m_decl_map
).get (decl
))
278 state_purge_per_decl
*result
= new state_purge_per_decl (*this, decl
, fun
);
279 m_decl_map
.put (decl
, result
);
283 /* class state_purge_per_ssa_name : public state_purge_per_tree. */
285 /* state_purge_per_ssa_name's ctor.
287 Locate all uses of VAR within FUN.
288 Walk backwards from each use, marking program points, until
289 we reach the def stmt, populating m_points_needing_var.
291 We have to track program points rather than
292 just stmts since there could be empty basic blocks on the way. */
294 state_purge_per_ssa_name::state_purge_per_ssa_name (const state_purge_map
&map
,
297 : state_purge_per_tree (fun
), m_points_needing_name (), m_name (name
)
299 LOG_FUNC (map
.get_logger ());
301 if (map
.get_logger ())
303 map
.log ("SSA name: %qE within %qD", name
, fun
->decl
);
306 const gimple
*def_stmt
= SSA_NAME_DEF_STMT (name
);
308 pp_gimple_stmt_1 (&pp
, def_stmt
, 0, (dump_flags_t
)0);
309 map
.log ("def stmt: %s", pp_formatted_text (&pp
));
312 auto_vec
<function_point
> worklist
;
314 /* Add all immediate uses of name to the worklist.
315 Compare with debug_immediate_uses. */
316 imm_use_iterator iter
;
318 FOR_EACH_IMM_USE_FAST (use_p
, iter
, name
)
320 if (USE_STMT (use_p
))
322 const gimple
*use_stmt
= USE_STMT (use_p
);
323 if (map
.get_logger ())
326 pp_gimple_stmt_1 (&pp
, use_stmt
, 0, (dump_flags_t
)0);
327 map
.log ("used by stmt: %s", pp_formatted_text (&pp
));
330 const supernode
*snode
331 = map
.get_sg ().get_supernode_for_stmt (use_stmt
);
333 /* If it's a use within a phi node, then we care about
334 which in-edge we came from. */
335 if (use_stmt
->code
== GIMPLE_PHI
)
337 for (gphi_iterator gpi
338 = const_cast<supernode
*> (snode
)->start_phis ();
339 !gsi_end_p (gpi
); gsi_next (&gpi
))
341 gphi
*phi
= gpi
.phi ();
344 /* Find arguments (and thus in-edges) which use NAME. */
345 for (unsigned arg_idx
= 0;
346 arg_idx
< gimple_phi_num_args (phi
);
349 if (name
== gimple_phi_arg (phi
, arg_idx
)->def
)
351 edge in_edge
= gimple_phi_arg_edge (phi
, arg_idx
);
352 const superedge
*in_sedge
353 = map
.get_sg ().get_edge_for_cfg_edge (in_edge
);
355 = function_point::before_supernode
357 add_to_worklist (point
, &worklist
,
359 m_points_needing_name
.add (point
);
367 function_point point
= before_use_stmt (map
, use_stmt
);
368 add_to_worklist (point
, &worklist
, map
.get_logger ());
369 m_points_needing_name
.add (point
);
371 /* We also need to add uses for conditionals and switches,
372 where the stmt "happens" at the after_supernode, for filtering
374 if (use_stmt
== snode
->get_last_stmt ())
376 if (map
.get_logger ())
377 map
.log ("last stmt in BB");
379 = function_point::after_supernode (snode
);
380 add_to_worklist (point
, &worklist
, map
.get_logger ());
381 m_points_needing_name
.add (point
);
384 if (map
.get_logger ())
385 map
.log ("not last stmt in BB");
390 /* Process worklist by walking backwards until we reach the def stmt. */
392 log_scope
s (map
.get_logger (), "processing worklist");
393 while (worklist
.length () > 0)
395 function_point point
= worklist
.pop ();
396 process_point (point
, &worklist
, map
);
400 if (map
.get_logger ())
402 map
.log ("%qE in %qD is needed to process:", name
, fun
->decl
);
403 /* Log m_points_needing_name, sorting it to avoid churn when comparing
405 auto_vec
<function_point
> points
;
406 for (point_set_t::iterator iter
= m_points_needing_name
.begin ();
407 iter
!= m_points_needing_name
.end ();
409 points
.safe_push (*iter
);
410 points
.qsort (function_point::cmp_ptr
);
412 function_point
*point
;
413 FOR_EACH_VEC_ELT (points
, i
, point
)
415 map
.start_log_line ();
416 map
.get_logger ()->log_partial (" point: ");
417 point
->print (map
.get_logger ()->get_printer (), format (false));
423 /* Return true if the SSA name is needed at POINT. */
426 state_purge_per_ssa_name::needed_at_point_p (const function_point
&point
) const
428 return const_cast <point_set_t
&> (m_points_needing_name
).contains (point
);
431 /* Get the function_point representing immediately before USE_STMT.
432 Subroutine of ctor. */
435 state_purge_per_ssa_name::before_use_stmt (const state_purge_map
&map
,
436 const gimple
*use_stmt
)
438 gcc_assert (use_stmt
->code
!= GIMPLE_PHI
);
440 const supernode
*supernode
441 = map
.get_sg ().get_supernode_for_stmt (use_stmt
);
442 unsigned int stmt_idx
= supernode
->get_stmt_index (use_stmt
);
443 return function_point::before_stmt (supernode
, stmt_idx
);
446 /* Add POINT to *WORKLIST if the point has not already been seen.
447 Subroutine of ctor. */
450 state_purge_per_ssa_name::add_to_worklist (const function_point
&point
,
451 auto_vec
<function_point
> *worklist
,
457 logger
->start_log_line ();
458 logger
->log_partial ("point: '");
459 point
.print (logger
->get_printer (), format (false));
460 logger
->log_partial ("' for worklist for %qE", m_name
);
461 logger
->end_log_line ();
464 gcc_assert (point
.get_function () == get_function ());
465 if (point
.get_from_edge ())
466 gcc_assert (point
.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE
);
468 if (m_points_needing_name
.contains (point
))
471 logger
->log ("already seen for %qE", m_name
);
476 logger
->log ("not seen; adding to worklist for %qE", m_name
);
477 m_points_needing_name
.add (point
);
478 worklist
->safe_push (point
);
482 /* Return true iff NAME is used by any of the phi nodes in SNODE
483 when processing the in-edge with PHI_ARG_IDX. */
486 name_used_by_phis_p (tree name
, const supernode
*snode
,
489 gcc_assert (TREE_CODE (name
) == SSA_NAME
);
491 for (gphi_iterator gpi
492 = const_cast<supernode
*> (snode
)->start_phis ();
493 !gsi_end_p (gpi
); gsi_next (&gpi
))
495 gphi
*phi
= gpi
.phi ();
496 if (gimple_phi_arg_def (phi
, phi_arg_idx
) == name
)
502 /* Process POINT, popped from WORKLIST.
503 Iterate over predecessors of POINT, adding to WORKLIST. */
506 state_purge_per_ssa_name::process_point (const function_point
&point
,
507 auto_vec
<function_point
> *worklist
,
508 const state_purge_map
&map
)
510 logger
*logger
= map
.get_logger ();
514 logger
->start_log_line ();
515 logger
->log_partial ("considering point: '");
516 point
.print (logger
->get_printer (), format (false));
517 logger
->log_partial ("' for %qE", m_name
);
518 logger
->end_log_line ();
521 gimple
*def_stmt
= SSA_NAME_DEF_STMT (m_name
);
523 const supernode
*snode
= point
.get_supernode ();
525 switch (point
.get_kind ())
533 case PK_BEFORE_SUPERNODE
:
535 for (gphi_iterator gpi
536 = const_cast<supernode
*> (snode
)->start_phis ();
537 !gsi_end_p (gpi
); gsi_next (&gpi
))
539 gcc_assert (point
.get_from_edge ());
540 const cfg_superedge
*cfg_sedge
541 = point
.get_from_edge ()->dyn_cast_cfg_superedge ();
542 gcc_assert (cfg_sedge
);
544 gphi
*phi
= gpi
.phi ();
545 /* Are we at the def-stmt for m_name? */
548 if (name_used_by_phis_p (m_name
, snode
,
549 cfg_sedge
->get_phi_arg_idx ()))
552 logger
->log ("name in def stmt used within phis;"
558 logger
->log ("name in def stmt not used within phis;"
565 /* Add given pred to worklist. */
566 if (point
.get_from_edge ())
568 gcc_assert (point
.get_from_edge ()->m_src
);
570 (function_point::after_supernode (point
.get_from_edge ()->m_src
),
575 /* Add any intraprocedually edge for a call. */
576 if (snode
->m_returning_call
)
578 gcall
*returning_call
= snode
->m_returning_call
;
580 = supergraph_call_edge (snode
->m_fun
,
585 = map
.get_sg ().get_intraprocedural_edge_for_call (cedge
);
588 (function_point::after_supernode (sedge
->m_src
),
593 supernode
*callernode
594 = map
.get_sg ().get_supernode_for_stmt (returning_call
);
596 gcc_assert (callernode
);
598 (function_point::after_supernode (callernode
),
608 if (def_stmt
== point
.get_stmt ())
611 logger
->log ("def stmt; terminating");
614 if (point
.get_stmt_idx () > 0)
615 add_to_worklist (function_point::before_stmt
616 (snode
, point
.get_stmt_idx () - 1),
620 /* Add before_supernode to worklist. This captures the in-edge,
621 so we have to do it once per in-edge. */
624 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
625 add_to_worklist (function_point::before_supernode (snode
,
632 case PK_AFTER_SUPERNODE
:
634 if (snode
->m_stmts
.length ())
636 (function_point::before_stmt (snode
,
637 snode
->m_stmts
.length () - 1),
641 /* Add before_supernode to worklist. This captures the in-edge,
642 so we have to do it once per in-edge. */
645 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
646 add_to_worklist (function_point::before_supernode (snode
,
649 /* If it's the initial BB, add it, to ensure that we
650 have "before supernode" for the initial ENTRY block, and don't
651 erroneously purge SSA names for initial values of parameters. */
652 if (snode
->entry_p ())
655 (function_point::before_supernode (snode
, NULL
),
664 /* class state_purge_per_decl : public state_purge_per_tree. */
666 /* state_purge_per_decl's ctor. */
668 state_purge_per_decl::state_purge_per_decl (const state_purge_map
&map
,
671 : state_purge_per_tree (fun
),
674 /* The RESULT_DECL is always needed at the end of its function. */
675 if (TREE_CODE (decl
) == RESULT_DECL
)
677 supernode
*exit_snode
= map
.get_sg ().get_node_for_function_exit (fun
);
678 add_needed_at (function_point::after_supernode (exit_snode
));
682 /* Mark the value of the decl (or a subvalue within it) as being needed
686 state_purge_per_decl::add_needed_at (const function_point
&point
)
688 m_points_needing_decl
.add (point
);
691 /* Mark that a pointer to the decl (or a region within it) is taken
695 state_purge_per_decl::add_pointed_to_at (const function_point
&point
)
697 m_points_taking_address
.add (point
);
700 /* Process the worklists for this decl:
701 (a) walk backwards from points where we know the value of the decl
702 is needed, marking points until we get to a stmt that fully overwrites
704 (b) walk forwards from points where the address of the decl is taken,
705 marking points as potentially needing the value of the decl. */
708 state_purge_per_decl::process_worklists (const state_purge_map
&map
,
709 region_model_manager
*mgr
)
711 logger
*logger
= map
.get_logger ();
714 logger
->log ("decl: %qE within %qD", m_decl
, get_fndecl ());
716 /* Worklist for walking backwards from uses. */
718 auto_vec
<function_point
> worklist
;
721 /* Add all uses of the decl to the worklist. */
722 for (auto iter
: m_points_needing_decl
)
723 worklist
.safe_push (iter
);
725 region_model
model (mgr
);
726 model
.push_frame (get_function (), NULL
, NULL
);
728 /* Process worklist by walking backwards until we reach a stmt
729 that fully overwrites the decl. */
731 log_scope
s (logger
, "processing worklist");
732 while (worklist
.length () > 0)
734 function_point point
= worklist
.pop ();
735 process_point_backwards (point
, &worklist
, &seen
, map
, model
);
740 /* Worklist for walking forwards from address-taken points. */
742 auto_vec
<function_point
> worklist
;
745 /* Add all uses of the decl to the worklist. */
746 for (auto iter
: m_points_taking_address
)
748 worklist
.safe_push (iter
);
750 /* Add to m_points_needing_decl (now that we traversed
751 it above for the backward worklist). */
752 m_points_needing_decl
.add (iter
);
755 /* Process worklist by walking forwards. */
757 log_scope
s (logger
, "processing worklist");
758 while (worklist
.length () > 0)
760 function_point point
= worklist
.pop ();
761 process_point_forwards (point
, &worklist
, &seen
, map
);
767 /* Add POINT to *WORKLIST if the point is not already in *seen.
768 Add newly seen points to *SEEN and to m_points_needing_name. */
771 state_purge_per_decl::add_to_worklist (const function_point
&point
,
772 auto_vec
<function_point
> *worklist
,
779 logger
->start_log_line ();
780 logger
->log_partial ("point: '");
781 point
.print (logger
->get_printer (), format (false));
782 logger
->log_partial ("' for worklist for %qE", m_decl
);
783 logger
->end_log_line ();
786 gcc_assert (point
.get_function () == get_function ());
787 if (point
.get_from_edge ())
788 gcc_assert (point
.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE
);
790 if (seen
->contains (point
))
793 logger
->log ("already seen for %qE", m_decl
);
798 logger
->log ("not seen; adding to worklist for %qE", m_decl
);
799 m_points_needing_decl
.add (point
);
801 worklist
->safe_push (point
);
805 /* Determine if REG_A and REG_B express writing to exactly the same
807 For example, when "s.field" is the only field of "s", and there's no
808 padding, this should return true. */
811 same_binding_p (const region
*reg_a
, const region
*reg_b
,
812 store_manager
*store_mgr
)
814 if (reg_a
->get_base_region () != reg_b
->get_base_region ())
816 const binding_key
*bind_key_a
= binding_key::make (store_mgr
, reg_a
);
817 const binding_key
*bind_key_b
= binding_key::make (store_mgr
, reg_b
);
818 return bind_key_a
== bind_key_b
;
821 /* Return true if STMT fully overwrites DECL. */
824 fully_overwrites_p (const gimple
*stmt
, tree decl
,
825 const region_model
&model
)
827 if (tree lhs
= gimple_get_lhs (stmt
))
829 /* Determine if LHS fully overwrites DECL.
830 We can't just check for equality; consider the case of
831 "s.field = EXPR;" where the stmt writes to the only field
832 of "s", and there's no padding. */
833 const region
*lhs_reg
= model
.get_lvalue (lhs
, NULL
);
834 const region
*decl_reg
= model
.get_lvalue (decl
, NULL
);
835 if (same_binding_p (lhs_reg
, decl_reg
,
836 model
.get_manager ()->get_store_manager ()))
842 /* Process POINT, popped from *WORKLIST.
843 Iterate over predecessors of POINT, adding to *WORKLIST and *SEEN,
844 until we get to a stmt that fully overwrites the decl. */
847 state_purge_per_decl::
848 process_point_backwards (const function_point
&point
,
849 auto_vec
<function_point
> *worklist
,
851 const state_purge_map
&map
,
852 const region_model
&model
)
854 logger
*logger
= map
.get_logger ();
858 logger
->start_log_line ();
859 logger
->log_partial ("considering point: '");
860 point
.print (logger
->get_printer (), format (false));
861 logger
->log_partial ("' for %qE", m_decl
);
862 logger
->end_log_line ();
864 const supernode
*snode
= point
.get_supernode ();
866 switch (point
.get_kind ())
874 case PK_BEFORE_SUPERNODE
:
876 /* Add given pred to worklist. */
877 if (point
.get_from_edge ())
879 gcc_assert (point
.get_from_edge ()->m_src
);
881 (function_point::after_supernode (point
.get_from_edge ()->m_src
),
882 worklist
, seen
, logger
);
886 /* Add any intraprocedually edge for a call. */
887 if (snode
->m_returning_call
)
889 gcall
*returning_call
= snode
->m_returning_call
;
891 = supergraph_call_edge (snode
->m_fun
,
896 = map
.get_sg ().get_intraprocedural_edge_for_call (cedge
);
899 (function_point::after_supernode (sedge
->m_src
),
900 worklist
, seen
, logger
);
904 supernode
*callernode
905 = map
.get_sg ().get_supernode_for_stmt (returning_call
);
907 gcc_assert (callernode
);
909 (function_point::after_supernode (callernode
),
910 worklist
, seen
, logger
);
919 /* This is somewhat equivalent to how the SSA case handles
921 if (fully_overwrites_p (point
.get_stmt (), m_decl
, model
))
924 logger
->log ("stmt fully overwrites %qE; terminating", m_decl
);
927 if (point
.get_stmt_idx () > 0)
928 add_to_worklist (function_point::before_stmt
929 (snode
, point
.get_stmt_idx () - 1),
930 worklist
, seen
, logger
);
933 /* Add before_supernode to worklist. This captures the in-edge,
934 so we have to do it once per in-edge. */
937 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
938 add_to_worklist (function_point::before_supernode (snode
,
940 worklist
, seen
, logger
);
945 case PK_AFTER_SUPERNODE
:
947 if (snode
->m_stmts
.length ())
949 (function_point::before_stmt (snode
,
950 snode
->m_stmts
.length () - 1),
951 worklist
, seen
, logger
);
954 /* Add before_supernode to worklist. This captures the in-edge,
955 so we have to do it once per in-edge. */
958 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
959 add_to_worklist (function_point::before_supernode (snode
,
961 worklist
, seen
, logger
);
969 /* Process POINT, popped from *WORKLIST.
970 Iterate over successors of POINT, adding to *WORKLIST and *SEEN. */
973 state_purge_per_decl::
974 process_point_forwards (const function_point
&point
,
975 auto_vec
<function_point
> *worklist
,
977 const state_purge_map
&map
)
979 logger
*logger
= map
.get_logger ();
983 logger
->start_log_line ();
984 logger
->log_partial ("considering point: '");
985 point
.print (logger
->get_printer (), format (false));
986 logger
->log_partial ("' for %qE", m_decl
);
987 logger
->end_log_line ();
989 const supernode
*snode
= point
.get_supernode ();
991 switch (point
.get_kind ())
997 case PK_BEFORE_SUPERNODE
:
999 function_point next
= point
.get_next ();
1000 add_to_worklist (next
, worklist
, seen
, logger
);
1004 case PK_BEFORE_STMT
:
1006 /* Perhaps we should stop at a clobber of the decl,
1007 but that ought to purge state for us explicitly. */
1008 function_point next
= point
.get_next ();
1009 add_to_worklist (next
, worklist
, seen
, logger
);
1013 case PK_AFTER_SUPERNODE
:
1015 /* Look at interprocedural out-edges. */
1018 FOR_EACH_VEC_ELT (snode
->m_succs
, i
, succ
)
1020 enum edge_kind kind
= succ
->get_kind ();
1021 if (kind
== SUPEREDGE_CFG_EDGE
1022 || kind
== SUPEREDGE_INTRAPROCEDURAL_CALL
)
1023 add_to_worklist (function_point::before_supernode (succ
->m_dest
,
1025 worklist
, seen
, logger
);
1032 /* Return true if the decl is needed at POINT. */
1035 state_purge_per_decl::needed_at_point_p (const function_point
&point
) const
1037 return const_cast <point_set_t
&> (m_points_needing_decl
).contains (point
);
1040 /* class state_purge_annotator : public dot_annotator. */
1042 /* Implementation of dot_annotator::add_node_annotations vfunc for
1043 state_purge_annotator.
1045 Add an additional record showing which names are purged on entry
1046 to the supernode N. */
1049 state_purge_annotator::add_node_annotations (graphviz_out
*gv
,
1051 bool within_table
) const
1059 pretty_printer
*pp
= gv
->get_pp ();
1061 pp_printf (pp
, "annotation_for_node_%i", n
.m_index
);
1062 pp_printf (pp
, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
1064 pp_write_text_to_stream (pp
);
1066 /* Different in-edges mean different names need purging.
1067 Determine which points to dump. */
1068 auto_vec
<function_point
> points
;
1069 if (n
.entry_p () || n
.m_returning_call
)
1070 points
.safe_push (function_point::before_supernode (&n
, NULL
));
1072 for (auto inedge
: n
.m_preds
)
1073 points
.safe_push (function_point::before_supernode (&n
, inedge
));
1074 points
.safe_push (function_point::after_supernode (&n
));
1076 for (auto & point
: points
)
1078 point
.print (pp
, format (true));
1080 print_needed (gv
, point
, false);
1084 pp_string (pp
, "\"];\n\n");
1089 /* Print V to GV as a comma-separated list in braces, titling it with TITLE.
1090 If WITHIN_TABLE is true, print it within a <TR>
1092 Subroutine of state_purge_annotator::print_needed. */
1095 print_vec_of_names (graphviz_out
*gv
, const char *title
,
1096 const auto_vec
<tree
> &v
, bool within_table
)
1098 pretty_printer
*pp
= gv
->get_pp ();
1103 pp_printf (pp
, "%s: {", title
);
1104 FOR_EACH_VEC_ELT (v
, i
, name
)
1107 pp_string (pp
, ", ");
1108 pp_printf (pp
, "%qE", name
);
1110 pp_printf (pp
, "}");
1113 pp_write_text_as_html_like_dot_to_stream (pp
);
1119 /* Implementation of dot_annotator::add_stmt_annotations for
1120 state_purge_annotator.
1122 Add text showing which names are purged at STMT. */
1125 state_purge_annotator::add_stmt_annotations (graphviz_out
*gv
,
1127 bool within_row
) const
1135 if (stmt
->code
== GIMPLE_PHI
)
1138 pretty_printer
*pp
= gv
->get_pp ();
1142 const supernode
*supernode
= m_map
->get_sg ().get_supernode_for_stmt (stmt
);
1143 unsigned int stmt_idx
= supernode
->get_stmt_index (stmt
);
1144 function_point before_stmt
1145 (function_point::before_stmt (supernode
, stmt_idx
));
1147 print_needed (gv
, before_stmt
, true);
1150 /* Get the decls and SSA names needed and not-needed at POINT, and
1152 If WITHIN_TABLE is true, print them within <TR> elements. */
1155 state_purge_annotator::print_needed (graphviz_out
*gv
,
1156 const function_point
&point
,
1157 bool within_table
) const
1159 auto_vec
<tree
> needed
;
1160 auto_vec
<tree
> not_needed
;
1161 for (state_purge_map::ssa_iterator iter
= m_map
->begin_ssas ();
1162 iter
!= m_map
->end_ssas ();
1165 tree name
= (*iter
).first
;
1166 state_purge_per_ssa_name
*per_name_data
= (*iter
).second
;
1167 if (per_name_data
->get_function () == point
.get_function ())
1169 if (per_name_data
->needed_at_point_p (point
))
1170 needed
.safe_push (name
);
1172 not_needed
.safe_push (name
);
1175 for (state_purge_map::decl_iterator iter
= m_map
->begin_decls ();
1176 iter
!= m_map
->end_decls ();
1179 tree decl
= (*iter
).first
;
1180 state_purge_per_decl
*per_decl_data
= (*iter
).second
;
1181 if (per_decl_data
->get_function () == point
.get_function ())
1183 if (per_decl_data
->needed_at_point_p (point
))
1184 needed
.safe_push (decl
);
1186 not_needed
.safe_push (decl
);
1190 print_vec_of_names (gv
, "needed here", needed
, within_table
);
1191 print_vec_of_names (gv
, "not needed here", not_needed
, within_table
);
1194 #endif /* #if ENABLE_ANALYZER */