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/>. */
23 #include "coretypes.h"
26 #include "tree-ssa-alias.h"
28 #include "basic-block.h"
30 #include "stringpool.h"
32 #include "gimple-ssa.h"
33 #include "tree-ssanames.h"
34 #include "tree-phinodes.h"
36 #include "ssa-iterators.h"
37 #include "diagnostic-core.h"
38 #include "gimple-pretty-print.h"
41 #include "analyzer/analyzer.h"
42 #include "analyzer/call-string.h"
44 #include "ordered-hash-map.h"
46 #include "gimple-iterator.h"
48 #include "analyzer/supergraph.h"
49 #include "analyzer/program-point.h"
50 #include "analyzer/analyzer-logging.h"
51 #include "analyzer/state-purge.h"
54 #include "analyzer/store.h"
55 #include "analyzer/region-model.h"
56 #include "gimple-walk.h"
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. */
64 get_candidate_for_purging (tree node
)
68 switch (TREE_CODE (iter
))
74 iter
= TREE_OPERAND (iter
, 0);
78 if (is_global_var (iter
))
89 /* Class-based handler for walk_stmt_load_store_addr_ops at a particular
90 function_point, for populating the worklists within a state_purge_map. */
92 class gimple_op_visitor
: public log_user
95 gimple_op_visitor (state_purge_map
*map
,
96 const function_point
&point
,
98 : log_user (map
->get_logger ()),
104 bool on_load (gimple
*stmt
, tree base
, tree op
)
106 LOG_FUNC (get_logger ());
110 pp_gimple_stmt_1 (&pp
, stmt
, 0, (dump_flags_t
)0);
111 log ("on_load: %s; base: %qE, op: %qE",
112 pp_formatted_text (&pp
), base
, op
);
114 if (tree node
= get_candidate_for_purging (base
))
119 bool on_store (gimple
*stmt
, tree base
, tree op
)
121 LOG_FUNC (get_logger ());
125 pp_gimple_stmt_1 (&pp
, stmt
, 0, (dump_flags_t
)0);
126 log ("on_store: %s; base: %qE, op: %qE",
127 pp_formatted_text (&pp
), base
, op
);
132 bool on_addr (gimple
*stmt
, tree base
, tree op
)
134 LOG_FUNC (get_logger ());
138 pp_gimple_stmt_1 (&pp
, stmt
, 0, (dump_flags_t
)0);
139 log ("on_addr: %s; base: %qE, op: %qE",
140 pp_formatted_text (&pp
), base
, op
);
142 if (TREE_CODE (op
) != ADDR_EXPR
)
144 if (tree node
= get_candidate_for_purging (base
))
147 add_pointed_to (node
);
153 void add_needed (tree decl
)
155 gcc_assert (get_candidate_for_purging (decl
) == decl
);
156 state_purge_per_decl
&data
157 = get_or_create_data_for_decl (decl
);
158 data
.add_needed_at (m_point
);
160 /* Handle calls: if we're seeing a use at a call, then add a use at the
161 "after-supernode" point (in case of interprocedural call superedges). */
162 if (m_point
.final_stmt_p ())
163 data
.add_needed_at (m_point
.get_next ());
166 void add_pointed_to (tree decl
)
168 gcc_assert (get_candidate_for_purging (decl
) == decl
);
169 get_or_create_data_for_decl (decl
).add_pointed_to_at (m_point
);
172 state_purge_per_decl
&
173 get_or_create_data_for_decl (tree decl
)
175 return m_map
->get_or_create_data_for_decl (m_fun
, decl
);
178 state_purge_map
*m_map
;
179 const function_point
&m_point
;
184 my_load_cb (gimple
*stmt
, tree base
, tree op
, void *user_data
)
186 gimple_op_visitor
*x
= (gimple_op_visitor
*)user_data
;
187 return x
->on_load (stmt
, base
, op
);
191 my_store_cb (gimple
*stmt
, tree base
, tree op
, void *user_data
)
193 gimple_op_visitor
*x
= (gimple_op_visitor
*)user_data
;
194 return x
->on_store (stmt
, base
, op
);
198 my_addr_cb (gimple
*stmt
, tree base
, tree op
, void *user_data
)
200 gimple_op_visitor
*x
= (gimple_op_visitor
*)user_data
;
201 return x
->on_addr (stmt
, base
, op
);
204 /* state_purge_map's ctor. Walk all SSA names in all functions, building
205 a state_purge_per_ssa_name instance for each.
206 Also, walk all loads and address-taken ops of local variables, building
207 a state_purge_per_decl as appropriate. */
209 state_purge_map::state_purge_map (const supergraph
&sg
,
210 region_model_manager
*mgr
,
212 : log_user (logger
), m_sg (sg
)
216 auto_timevar
tv (TV_ANALYZER_STATE_PURGE
);
219 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node
)
221 function
*fun
= node
->get_fun ();
223 log ("function: %s", function_name (fun
));
226 FOR_EACH_SSA_NAME (i
, name
, fun
)
228 /* For now, don't bother tracking the .MEM SSA names. */
229 if (tree var
= SSA_NAME_VAR (name
))
230 if (TREE_CODE (var
) == VAR_DECL
)
231 if (VAR_DECL_IS_VIRTUAL_OPERAND (var
))
233 m_ssa_map
.put (name
, new state_purge_per_ssa_name (*this, name
, fun
));
237 /* Find all uses of local vars.
238 We iterate through all function points, finding loads, stores, and
239 address-taken operations on locals, building a pair of worklists. */
240 for (auto snode
: sg
.m_nodes
)
243 log ("SN: %i", snode
->m_index
);
244 /* We ignore m_returning_call and phi nodes. */
247 FOR_EACH_VEC_ELT (snode
->m_stmts
, i
, stmt
)
249 function_point
point (function_point::before_stmt (snode
, i
));
250 gimple_op_visitor
v (this, point
, snode
->get_function ());
251 walk_stmt_load_store_addr_ops (stmt
, &v
,
252 my_load_cb
, my_store_cb
, my_addr_cb
);
256 /* Now iterate through the m_decl_map, processing each pair of worklists. */
257 for (state_purge_map::decl_iterator iter
= begin_decls ();
258 iter
!= end_decls ();
261 state_purge_per_decl
*per_decl_data
= (*iter
).second
;
262 per_decl_data
->process_worklists (*this, mgr
);
266 /* state_purge_map's dtor. */
268 state_purge_map::~state_purge_map ()
270 for (auto iter
: m_ssa_map
)
272 for (auto iter
: m_decl_map
)
276 /* Get the state_purge_per_decl for local DECL within FUN, creating it
279 state_purge_per_decl
&
280 state_purge_map::get_or_create_data_for_decl (function
*fun
, tree decl
)
282 if (state_purge_per_decl
**slot
283 = const_cast <decl_map_t
&> (m_decl_map
).get (decl
))
285 state_purge_per_decl
*result
= new state_purge_per_decl (*this, decl
, fun
);
286 m_decl_map
.put (decl
, result
);
290 /* class state_purge_per_ssa_name : public state_purge_per_tree. */
292 /* state_purge_per_ssa_name's ctor.
294 Locate all uses of VAR within FUN.
295 Walk backwards from each use, marking program points, until
296 we reach the def stmt, populating m_points_needing_var.
298 We have to track program points rather than
299 just stmts since there could be empty basic blocks on the way. */
301 state_purge_per_ssa_name::state_purge_per_ssa_name (const state_purge_map
&map
,
304 : state_purge_per_tree (fun
), m_points_needing_name (), m_name (name
)
306 LOG_FUNC (map
.get_logger ());
308 if (map
.get_logger ())
310 map
.log ("SSA name: %qE within %qD", name
, fun
->decl
);
313 const gimple
*def_stmt
= SSA_NAME_DEF_STMT (name
);
315 pp_gimple_stmt_1 (&pp
, def_stmt
, 0, (dump_flags_t
)0);
316 map
.log ("def stmt: %s", pp_formatted_text (&pp
));
319 auto_vec
<function_point
> worklist
;
321 /* Add all immediate uses of name to the worklist.
322 Compare with debug_immediate_uses. */
323 imm_use_iterator iter
;
325 FOR_EACH_IMM_USE_FAST (use_p
, iter
, name
)
327 if (USE_STMT (use_p
))
329 const gimple
*use_stmt
= USE_STMT (use_p
);
330 if (map
.get_logger ())
333 pp_gimple_stmt_1 (&pp
, use_stmt
, 0, (dump_flags_t
)0);
334 map
.log ("used by stmt: %s", pp_formatted_text (&pp
));
337 const supernode
*snode
338 = map
.get_sg ().get_supernode_for_stmt (use_stmt
);
340 /* If it's a use within a phi node, then we care about
341 which in-edge we came from. */
342 if (use_stmt
->code
== GIMPLE_PHI
)
344 for (gphi_iterator gpi
345 = const_cast<supernode
*> (snode
)->start_phis ();
346 !gsi_end_p (gpi
); gsi_next (&gpi
))
348 gphi
*phi
= gpi
.phi ();
351 /* Find arguments (and thus in-edges) which use NAME. */
352 for (unsigned arg_idx
= 0;
353 arg_idx
< gimple_phi_num_args (phi
);
356 if (name
== gimple_phi_arg (phi
, arg_idx
)->def
)
358 edge in_edge
= gimple_phi_arg_edge (phi
, arg_idx
);
359 const superedge
*in_sedge
360 = map
.get_sg ().get_edge_for_cfg_edge (in_edge
);
362 = function_point::before_supernode
364 add_to_worklist (point
, &worklist
,
366 m_points_needing_name
.add (point
);
374 function_point point
= before_use_stmt (map
, use_stmt
);
375 add_to_worklist (point
, &worklist
, map
.get_logger ());
376 m_points_needing_name
.add (point
);
378 /* We also need to add uses for conditionals and switches,
379 where the stmt "happens" at the after_supernode, for filtering
381 if (use_stmt
== snode
->get_last_stmt ())
383 if (map
.get_logger ())
384 map
.log ("last stmt in BB");
386 = function_point::after_supernode (snode
);
387 add_to_worklist (point
, &worklist
, map
.get_logger ());
388 m_points_needing_name
.add (point
);
391 if (map
.get_logger ())
392 map
.log ("not last stmt in BB");
397 /* Process worklist by walking backwards until we reach the def stmt. */
399 log_scope
s (map
.get_logger (), "processing worklist");
400 while (worklist
.length () > 0)
402 function_point point
= worklist
.pop ();
403 process_point (point
, &worklist
, map
);
407 if (map
.get_logger ())
409 map
.log ("%qE in %qD is needed to process:", name
, fun
->decl
);
410 /* Log m_points_needing_name, sorting it to avoid churn when comparing
412 auto_vec
<function_point
> points
;
413 for (point_set_t::iterator iter
= m_points_needing_name
.begin ();
414 iter
!= m_points_needing_name
.end ();
416 points
.safe_push (*iter
);
417 points
.qsort (function_point::cmp_ptr
);
419 function_point
*point
;
420 FOR_EACH_VEC_ELT (points
, i
, point
)
422 map
.start_log_line ();
423 map
.get_logger ()->log_partial (" point: ");
424 point
->print (map
.get_logger ()->get_printer (), format (false));
430 /* Return true if the SSA name is needed at POINT. */
433 state_purge_per_ssa_name::needed_at_point_p (const function_point
&point
) const
435 return const_cast <point_set_t
&> (m_points_needing_name
).contains (point
);
438 /* Get the function_point representing immediately before USE_STMT.
439 Subroutine of ctor. */
442 state_purge_per_ssa_name::before_use_stmt (const state_purge_map
&map
,
443 const gimple
*use_stmt
)
445 gcc_assert (use_stmt
->code
!= GIMPLE_PHI
);
447 const supernode
*supernode
448 = map
.get_sg ().get_supernode_for_stmt (use_stmt
);
449 unsigned int stmt_idx
= supernode
->get_stmt_index (use_stmt
);
450 return function_point::before_stmt (supernode
, stmt_idx
);
453 /* Add POINT to *WORKLIST if the point has not already been seen.
454 Subroutine of ctor. */
457 state_purge_per_ssa_name::add_to_worklist (const function_point
&point
,
458 auto_vec
<function_point
> *worklist
,
464 logger
->start_log_line ();
465 logger
->log_partial ("point: '");
466 point
.print (logger
->get_printer (), format (false));
467 logger
->log_partial ("' for worklist for %qE", m_name
);
468 logger
->end_log_line ();
471 gcc_assert (point
.get_function () == get_function ());
472 if (point
.get_from_edge ())
473 gcc_assert (point
.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE
);
475 if (m_points_needing_name
.contains (point
))
478 logger
->log ("already seen for %qE", m_name
);
483 logger
->log ("not seen; adding to worklist for %qE", m_name
);
484 m_points_needing_name
.add (point
);
485 worklist
->safe_push (point
);
489 /* Return true iff NAME is used by any of the phi nodes in SNODE
490 when processing the in-edge with PHI_ARG_IDX. */
493 name_used_by_phis_p (tree name
, const supernode
*snode
,
496 gcc_assert (TREE_CODE (name
) == SSA_NAME
);
498 for (gphi_iterator gpi
499 = const_cast<supernode
*> (snode
)->start_phis ();
500 !gsi_end_p (gpi
); gsi_next (&gpi
))
502 gphi
*phi
= gpi
.phi ();
503 if (gimple_phi_arg_def (phi
, phi_arg_idx
) == name
)
509 /* Process POINT, popped from WORKLIST.
510 Iterate over predecessors of POINT, adding to WORKLIST. */
513 state_purge_per_ssa_name::process_point (const function_point
&point
,
514 auto_vec
<function_point
> *worklist
,
515 const state_purge_map
&map
)
517 logger
*logger
= map
.get_logger ();
521 logger
->start_log_line ();
522 logger
->log_partial ("considering point: '");
523 point
.print (logger
->get_printer (), format (false));
524 logger
->log_partial ("' for %qE", m_name
);
525 logger
->end_log_line ();
528 gimple
*def_stmt
= SSA_NAME_DEF_STMT (m_name
);
530 const supernode
*snode
= point
.get_supernode ();
532 switch (point
.get_kind ())
540 case PK_BEFORE_SUPERNODE
:
542 for (gphi_iterator gpi
543 = const_cast<supernode
*> (snode
)->start_phis ();
544 !gsi_end_p (gpi
); gsi_next (&gpi
))
546 gcc_assert (point
.get_from_edge ());
547 const cfg_superedge
*cfg_sedge
548 = point
.get_from_edge ()->dyn_cast_cfg_superedge ();
549 gcc_assert (cfg_sedge
);
551 gphi
*phi
= gpi
.phi ();
552 /* Are we at the def-stmt for m_name? */
555 if (name_used_by_phis_p (m_name
, snode
,
556 cfg_sedge
->get_phi_arg_idx ()))
559 logger
->log ("name in def stmt used within phis;"
565 logger
->log ("name in def stmt not used within phis;"
572 /* Add given pred to worklist. */
573 if (point
.get_from_edge ())
575 gcc_assert (point
.get_from_edge ()->m_src
);
577 (function_point::after_supernode (point
.get_from_edge ()->m_src
),
582 /* Add any intraprocedually edge for a call. */
583 if (snode
->m_returning_call
)
585 gcall
*returning_call
= snode
->m_returning_call
;
587 = supergraph_call_edge (snode
->m_fun
,
592 = map
.get_sg ().get_intraprocedural_edge_for_call (cedge
);
595 (function_point::after_supernode (sedge
->m_src
),
600 supernode
*callernode
601 = map
.get_sg ().get_supernode_for_stmt (returning_call
);
603 gcc_assert (callernode
);
605 (function_point::after_supernode (callernode
),
615 if (def_stmt
== point
.get_stmt ())
618 logger
->log ("def stmt; terminating");
621 if (point
.get_stmt_idx () > 0)
622 add_to_worklist (function_point::before_stmt
623 (snode
, point
.get_stmt_idx () - 1),
627 /* Add before_supernode to worklist. This captures the in-edge,
628 so we have to do it once per in-edge. */
631 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
632 add_to_worklist (function_point::before_supernode (snode
,
639 case PK_AFTER_SUPERNODE
:
641 if (snode
->m_stmts
.length ())
643 (function_point::before_stmt (snode
,
644 snode
->m_stmts
.length () - 1),
648 /* Add before_supernode to worklist. This captures the in-edge,
649 so we have to do it once per in-edge. */
652 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
653 add_to_worklist (function_point::before_supernode (snode
,
656 /* If it's the initial BB, add it, to ensure that we
657 have "before supernode" for the initial ENTRY block, and don't
658 erroneously purge SSA names for initial values of parameters. */
659 if (snode
->entry_p ())
662 (function_point::before_supernode (snode
, NULL
),
671 /* class state_purge_per_decl : public state_purge_per_tree. */
673 /* state_purge_per_decl's ctor. */
675 state_purge_per_decl::state_purge_per_decl (const state_purge_map
&map
,
678 : state_purge_per_tree (fun
),
681 /* The RESULT_DECL is always needed at the end of its function. */
682 if (TREE_CODE (decl
) == RESULT_DECL
)
684 supernode
*exit_snode
= map
.get_sg ().get_node_for_function_exit (fun
);
685 add_needed_at (function_point::after_supernode (exit_snode
));
689 /* Mark the value of the decl (or a subvalue within it) as being needed
693 state_purge_per_decl::add_needed_at (const function_point
&point
)
695 m_points_needing_decl
.add (point
);
698 /* Mark that a pointer to the decl (or a region within it) is taken
702 state_purge_per_decl::add_pointed_to_at (const function_point
&point
)
704 m_points_taking_address
.add (point
);
707 /* Process the worklists for this decl:
708 (a) walk backwards from points where we know the value of the decl
709 is needed, marking points until we get to a stmt that fully overwrites
711 (b) walk forwards from points where the address of the decl is taken,
712 marking points as potentially needing the value of the decl. */
715 state_purge_per_decl::process_worklists (const state_purge_map
&map
,
716 region_model_manager
*mgr
)
718 logger
*logger
= map
.get_logger ();
721 logger
->log ("decl: %qE within %qD", m_decl
, get_fndecl ());
723 /* Worklist for walking backwards from uses. */
725 auto_vec
<function_point
> worklist
;
728 /* Add all uses of the decl to the worklist. */
729 for (auto iter
: m_points_needing_decl
)
730 worklist
.safe_push (iter
);
732 region_model
model (mgr
);
733 model
.push_frame (get_function (), NULL
, NULL
);
735 /* Process worklist by walking backwards until we reach a stmt
736 that fully overwrites the decl. */
738 log_scope
s (logger
, "processing worklist");
739 while (worklist
.length () > 0)
741 function_point point
= worklist
.pop ();
742 process_point_backwards (point
, &worklist
, &seen
, map
, model
);
747 /* Worklist for walking forwards from address-taken points. */
749 auto_vec
<function_point
> worklist
;
752 /* Add all uses of the decl to the worklist. */
753 for (auto iter
: m_points_taking_address
)
755 worklist
.safe_push (iter
);
757 /* Add to m_points_needing_decl (now that we traversed
758 it above for the backward worklist). */
759 m_points_needing_decl
.add (iter
);
762 /* Process worklist by walking forwards. */
764 log_scope
s (logger
, "processing worklist");
765 while (worklist
.length () > 0)
767 function_point point
= worklist
.pop ();
768 process_point_forwards (point
, &worklist
, &seen
, map
);
774 /* Add POINT to *WORKLIST if the point is not already in *seen.
775 Add newly seen points to *SEEN and to m_points_needing_name. */
778 state_purge_per_decl::add_to_worklist (const function_point
&point
,
779 auto_vec
<function_point
> *worklist
,
786 logger
->start_log_line ();
787 logger
->log_partial ("point: '");
788 point
.print (logger
->get_printer (), format (false));
789 logger
->log_partial ("' for worklist for %qE", m_decl
);
790 logger
->end_log_line ();
793 gcc_assert (point
.get_function () == get_function ());
794 if (point
.get_from_edge ())
795 gcc_assert (point
.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE
);
797 if (seen
->contains (point
))
800 logger
->log ("already seen for %qE", m_decl
);
805 logger
->log ("not seen; adding to worklist for %qE", m_decl
);
806 m_points_needing_decl
.add (point
);
808 worklist
->safe_push (point
);
812 /* Determine if REG_A and REG_B express writing to exactly the same
814 For example, when "s.field" is the only field of "s", and there's no
815 padding, this should return true. */
818 same_binding_p (const region
*reg_a
, const region
*reg_b
,
819 store_manager
*store_mgr
)
821 if (reg_a
->get_base_region () != reg_b
->get_base_region ())
823 const binding_key
*bind_key_a
= binding_key::make (store_mgr
, reg_a
);
824 const binding_key
*bind_key_b
= binding_key::make (store_mgr
, reg_b
);
825 return bind_key_a
== bind_key_b
;
828 /* Return true if STMT fully overwrites DECL. */
831 fully_overwrites_p (const gimple
*stmt
, tree decl
,
832 const region_model
&model
)
834 if (tree lhs
= gimple_get_lhs (stmt
))
836 /* Determine if LHS fully overwrites DECL.
837 We can't just check for equality; consider the case of
838 "s.field = EXPR;" where the stmt writes to the only field
839 of "s", and there's no padding. */
840 const region
*lhs_reg
= model
.get_lvalue (lhs
, NULL
);
841 const region
*decl_reg
= model
.get_lvalue (decl
, NULL
);
842 if (same_binding_p (lhs_reg
, decl_reg
,
843 model
.get_manager ()->get_store_manager ()))
849 /* Process POINT, popped from *WORKLIST.
850 Iterate over predecessors of POINT, adding to *WORKLIST and *SEEN,
851 until we get to a stmt that fully overwrites the decl. */
854 state_purge_per_decl::
855 process_point_backwards (const function_point
&point
,
856 auto_vec
<function_point
> *worklist
,
858 const state_purge_map
&map
,
859 const region_model
&model
)
861 logger
*logger
= map
.get_logger ();
865 logger
->start_log_line ();
866 logger
->log_partial ("considering point: '");
867 point
.print (logger
->get_printer (), format (false));
868 logger
->log_partial ("' for %qE", m_decl
);
869 logger
->end_log_line ();
871 const supernode
*snode
= point
.get_supernode ();
873 switch (point
.get_kind ())
881 case PK_BEFORE_SUPERNODE
:
883 /* Add given pred to worklist. */
884 if (point
.get_from_edge ())
886 gcc_assert (point
.get_from_edge ()->m_src
);
888 (function_point::after_supernode (point
.get_from_edge ()->m_src
),
889 worklist
, seen
, logger
);
893 /* Add any intraprocedually edge for a call. */
894 if (snode
->m_returning_call
)
896 gcall
*returning_call
= snode
->m_returning_call
;
898 = supergraph_call_edge (snode
->m_fun
,
903 = map
.get_sg ().get_intraprocedural_edge_for_call (cedge
);
906 (function_point::after_supernode (sedge
->m_src
),
907 worklist
, seen
, logger
);
911 supernode
*callernode
912 = map
.get_sg ().get_supernode_for_stmt (returning_call
);
914 gcc_assert (callernode
);
916 (function_point::after_supernode (callernode
),
917 worklist
, seen
, logger
);
926 /* This is somewhat equivalent to how the SSA case handles
928 if (fully_overwrites_p (point
.get_stmt (), m_decl
, model
))
931 logger
->log ("stmt fully overwrites %qE; terminating", m_decl
);
934 if (point
.get_stmt_idx () > 0)
935 add_to_worklist (function_point::before_stmt
936 (snode
, point
.get_stmt_idx () - 1),
937 worklist
, seen
, logger
);
940 /* Add before_supernode to worklist. This captures the in-edge,
941 so we have to do it once per in-edge. */
944 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
945 add_to_worklist (function_point::before_supernode (snode
,
947 worklist
, seen
, logger
);
952 case PK_AFTER_SUPERNODE
:
954 if (snode
->m_stmts
.length ())
956 (function_point::before_stmt (snode
,
957 snode
->m_stmts
.length () - 1),
958 worklist
, seen
, logger
);
961 /* Add before_supernode to worklist. This captures the in-edge,
962 so we have to do it once per in-edge. */
965 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
966 add_to_worklist (function_point::before_supernode (snode
,
968 worklist
, seen
, logger
);
976 /* Process POINT, popped from *WORKLIST.
977 Iterate over successors of POINT, adding to *WORKLIST and *SEEN. */
980 state_purge_per_decl::
981 process_point_forwards (const function_point
&point
,
982 auto_vec
<function_point
> *worklist
,
984 const state_purge_map
&map
)
986 logger
*logger
= map
.get_logger ();
990 logger
->start_log_line ();
991 logger
->log_partial ("considering point: '");
992 point
.print (logger
->get_printer (), format (false));
993 logger
->log_partial ("' for %qE", m_decl
);
994 logger
->end_log_line ();
996 const supernode
*snode
= point
.get_supernode ();
998 switch (point
.get_kind ())
1004 case PK_BEFORE_SUPERNODE
:
1006 function_point next
= point
.get_next ();
1007 add_to_worklist (next
, worklist
, seen
, logger
);
1011 case PK_BEFORE_STMT
:
1013 /* Perhaps we should stop at a clobber of the decl,
1014 but that ought to purge state for us explicitly. */
1015 function_point next
= point
.get_next ();
1016 add_to_worklist (next
, worklist
, seen
, logger
);
1020 case PK_AFTER_SUPERNODE
:
1022 /* Look at interprocedural out-edges. */
1025 FOR_EACH_VEC_ELT (snode
->m_succs
, i
, succ
)
1027 enum edge_kind kind
= succ
->get_kind ();
1028 if (kind
== SUPEREDGE_CFG_EDGE
1029 || kind
== SUPEREDGE_INTRAPROCEDURAL_CALL
)
1030 add_to_worklist (function_point::before_supernode (succ
->m_dest
,
1032 worklist
, seen
, logger
);
1039 /* Return true if the decl is needed at POINT. */
1042 state_purge_per_decl::needed_at_point_p (const function_point
&point
) const
1044 return const_cast <point_set_t
&> (m_points_needing_decl
).contains (point
);
1047 /* class state_purge_annotator : public dot_annotator. */
1049 /* Implementation of dot_annotator::add_node_annotations vfunc for
1050 state_purge_annotator.
1052 Add an additional record showing which names are purged on entry
1053 to the supernode N. */
1056 state_purge_annotator::add_node_annotations (graphviz_out
*gv
,
1058 bool within_table
) const
1066 pretty_printer
*pp
= gv
->get_pp ();
1068 pp_printf (pp
, "annotation_for_node_%i", n
.m_index
);
1069 pp_printf (pp
, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
1071 pp_write_text_to_stream (pp
);
1073 /* Different in-edges mean different names need purging.
1074 Determine which points to dump. */
1075 auto_vec
<function_point
> points
;
1076 if (n
.entry_p () || n
.m_returning_call
)
1077 points
.safe_push (function_point::before_supernode (&n
, NULL
));
1079 for (auto inedge
: n
.m_preds
)
1080 points
.safe_push (function_point::before_supernode (&n
, inedge
));
1081 points
.safe_push (function_point::after_supernode (&n
));
1083 for (auto & point
: points
)
1085 point
.print (pp
, format (true));
1087 print_needed (gv
, point
, false);
1091 pp_string (pp
, "\"];\n\n");
1096 /* Print V to GV as a comma-separated list in braces, titling it with TITLE.
1097 If WITHIN_TABLE is true, print it within a <TR>
1099 Subroutine of state_purge_annotator::print_needed. */
1102 print_vec_of_names (graphviz_out
*gv
, const char *title
,
1103 const auto_vec
<tree
> &v
, bool within_table
)
1105 pretty_printer
*pp
= gv
->get_pp ();
1110 pp_printf (pp
, "%s: {", title
);
1111 FOR_EACH_VEC_ELT (v
, i
, name
)
1114 pp_string (pp
, ", ");
1115 pp_printf (pp
, "%qE", name
);
1117 pp_printf (pp
, "}");
1120 pp_write_text_as_html_like_dot_to_stream (pp
);
1126 /* Implementation of dot_annotator::add_stmt_annotations for
1127 state_purge_annotator.
1129 Add text showing which names are purged at STMT. */
1132 state_purge_annotator::add_stmt_annotations (graphviz_out
*gv
,
1134 bool within_row
) const
1142 if (stmt
->code
== GIMPLE_PHI
)
1145 pretty_printer
*pp
= gv
->get_pp ();
1149 const supernode
*supernode
= m_map
->get_sg ().get_supernode_for_stmt (stmt
);
1150 unsigned int stmt_idx
= supernode
->get_stmt_index (stmt
);
1151 function_point before_stmt
1152 (function_point::before_stmt (supernode
, stmt_idx
));
1154 print_needed (gv
, before_stmt
, true);
1157 /* Get the decls and SSA names needed and not-needed at POINT, and
1159 If WITHIN_TABLE is true, print them within <TR> elements. */
1162 state_purge_annotator::print_needed (graphviz_out
*gv
,
1163 const function_point
&point
,
1164 bool within_table
) const
1166 auto_vec
<tree
> needed
;
1167 auto_vec
<tree
> not_needed
;
1168 for (state_purge_map::ssa_iterator iter
= m_map
->begin_ssas ();
1169 iter
!= m_map
->end_ssas ();
1172 tree name
= (*iter
).first
;
1173 state_purge_per_ssa_name
*per_name_data
= (*iter
).second
;
1174 if (per_name_data
->get_function () == point
.get_function ())
1176 if (per_name_data
->needed_at_point_p (point
))
1177 needed
.safe_push (name
);
1179 not_needed
.safe_push (name
);
1182 for (state_purge_map::decl_iterator iter
= m_map
->begin_decls ();
1183 iter
!= m_map
->end_decls ();
1186 tree decl
= (*iter
).first
;
1187 state_purge_per_decl
*per_decl_data
= (*iter
).second
;
1188 if (per_decl_data
->get_function () == point
.get_function ())
1190 if (per_decl_data
->needed_at_point_p (point
))
1191 needed
.safe_push (decl
);
1193 not_needed
.safe_push (decl
);
1197 print_vec_of_names (gv
, "needed here", needed
, within_table
);
1198 print_vec_of_names (gv
, "not needed here", not_needed
, within_table
);
1201 #endif /* #if ENABLE_ANALYZER */