1 /* Classes for purging state at function_points.
2 Copyright (C) 2019-2024 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
;
175 const function
&m_fun
;
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 ();
219 log ("function: %s", function_name (fun
));
222 FOR_EACH_SSA_NAME (i
, name
, fun
)
224 /* For now, don't bother tracking the .MEM SSA names. */
225 if (tree var
= SSA_NAME_VAR (name
))
226 if (TREE_CODE (var
) == VAR_DECL
)
227 if (VAR_DECL_IS_VIRTUAL_OPERAND (var
))
229 m_ssa_map
.put (name
, new state_purge_per_ssa_name (*this, name
, *fun
));
233 /* Find all uses of local vars.
234 We iterate through all function points, finding loads, stores, and
235 address-taken operations on locals, building a pair of worklists. */
236 for (auto snode
: sg
.m_nodes
)
239 log ("SN: %i", snode
->m_index
);
240 /* We ignore m_returning_call and phi nodes. */
243 FOR_EACH_VEC_ELT (snode
->m_stmts
, i
, stmt
)
245 function
*fun
= snode
->get_function ();
247 function_point
point (function_point::before_stmt (snode
, i
));
248 gimple_op_visitor
v (this, point
, *fun
);
249 walk_stmt_load_store_addr_ops (stmt
, &v
,
250 my_load_cb
, my_store_cb
, my_addr_cb
);
254 /* Now iterate through the m_decl_map, processing each pair of worklists. */
255 for (state_purge_map::decl_iterator iter
= begin_decls ();
256 iter
!= end_decls ();
259 state_purge_per_decl
*per_decl_data
= (*iter
).second
;
260 per_decl_data
->process_worklists (*this, mgr
);
264 /* state_purge_map's dtor. */
266 state_purge_map::~state_purge_map ()
268 for (auto iter
: m_ssa_map
)
270 for (auto iter
: m_decl_map
)
274 /* Get the state_purge_per_decl for local DECL within FUN, creating it
277 state_purge_per_decl
&
278 state_purge_map::get_or_create_data_for_decl (const function
&fun
, tree decl
)
280 if (state_purge_per_decl
**slot
281 = const_cast <decl_map_t
&> (m_decl_map
).get (decl
))
283 state_purge_per_decl
*result
= new state_purge_per_decl (*this, decl
, fun
);
284 m_decl_map
.put (decl
, result
);
288 /* class state_purge_per_ssa_name : public state_purge_per_tree. */
290 /* state_purge_per_ssa_name's ctor.
292 Locate all uses of VAR within FUN.
293 Walk backwards from each use, marking program points, until
294 we reach the def stmt, populating m_points_needing_var.
296 We have to track program points rather than
297 just stmts since there could be empty basic blocks on the way. */
299 state_purge_per_ssa_name::state_purge_per_ssa_name (const state_purge_map
&map
,
302 : state_purge_per_tree (fun
), m_points_needing_name (), m_name (name
)
304 LOG_FUNC (map
.get_logger ());
306 if (map
.get_logger ())
308 map
.log ("SSA name: %qE within %qD", name
, fun
.decl
);
311 const gimple
*def_stmt
= SSA_NAME_DEF_STMT (name
);
313 pp_gimple_stmt_1 (&pp
, def_stmt
, 0, (dump_flags_t
)0);
314 map
.log ("def stmt: %s", pp_formatted_text (&pp
));
317 auto_vec
<function_point
> worklist
;
319 /* Add all immediate uses of name to the worklist.
320 Compare with debug_immediate_uses. */
321 imm_use_iterator iter
;
323 FOR_EACH_IMM_USE_FAST (use_p
, iter
, name
)
325 if (USE_STMT (use_p
))
327 const gimple
*use_stmt
= USE_STMT (use_p
);
328 if (map
.get_logger ())
331 pp_gimple_stmt_1 (&pp
, use_stmt
, 0, (dump_flags_t
)0);
332 map
.log ("used by stmt: %s", pp_formatted_text (&pp
));
335 if (is_gimple_debug (use_stmt
))
337 /* We skipped debug stmts when building the supergraph,
338 so ignore them now. */
339 if (map
.get_logger ())
340 map
.log ("skipping debug stmt");
344 const supernode
*snode
345 = map
.get_sg ().get_supernode_for_stmt (use_stmt
);
347 /* If it's a use within a phi node, then we care about
348 which in-edge we came from. */
349 if (use_stmt
->code
== GIMPLE_PHI
)
351 for (gphi_iterator gpi
352 = const_cast<supernode
*> (snode
)->start_phis ();
353 !gsi_end_p (gpi
); gsi_next (&gpi
))
355 gphi
*phi
= gpi
.phi ();
358 /* Find arguments (and thus in-edges) which use NAME. */
359 for (unsigned arg_idx
= 0;
360 arg_idx
< gimple_phi_num_args (phi
);
363 if (name
== gimple_phi_arg (phi
, arg_idx
)->def
)
365 edge in_edge
= gimple_phi_arg_edge (phi
, arg_idx
);
366 const superedge
*in_sedge
367 = map
.get_sg ().get_edge_for_cfg_edge (in_edge
);
369 = function_point::before_supernode
371 add_to_worklist (point
, &worklist
,
373 m_points_needing_name
.add (point
);
381 function_point point
= before_use_stmt (map
, use_stmt
);
382 add_to_worklist (point
, &worklist
, map
.get_logger ());
383 m_points_needing_name
.add (point
);
385 /* We also need to add uses for conditionals and switches,
386 where the stmt "happens" at the after_supernode, for filtering
388 if (use_stmt
== snode
->get_last_stmt ())
390 if (map
.get_logger ())
391 map
.log ("last stmt in BB");
393 = function_point::after_supernode (snode
);
394 add_to_worklist (point
, &worklist
, map
.get_logger ());
395 m_points_needing_name
.add (point
);
398 if (map
.get_logger ())
399 map
.log ("not last stmt in BB");
404 /* Process worklist by walking backwards until we reach the def stmt. */
406 log_scope
s (map
.get_logger (), "processing worklist");
407 while (worklist
.length () > 0)
409 function_point point
= worklist
.pop ();
410 process_point (point
, &worklist
, map
);
414 if (map
.get_logger ())
416 map
.log ("%qE in %qD is needed to process:", name
, fun
.decl
);
417 /* Log m_points_needing_name, sorting it to avoid churn when comparing
419 auto_vec
<function_point
> points
;
420 for (point_set_t::iterator iter
= m_points_needing_name
.begin ();
421 iter
!= m_points_needing_name
.end ();
423 points
.safe_push (*iter
);
424 points
.qsort (function_point::cmp_ptr
);
426 function_point
*point
;
427 FOR_EACH_VEC_ELT (points
, i
, point
)
429 map
.start_log_line ();
430 map
.get_logger ()->log_partial (" point: ");
431 point
->print (map
.get_logger ()->get_printer (), format (false));
437 /* Return true if the SSA name is needed at POINT. */
440 state_purge_per_ssa_name::needed_at_point_p (const function_point
&point
) const
442 return const_cast <point_set_t
&> (m_points_needing_name
).contains (point
);
445 /* Get the function_point representing immediately before USE_STMT.
446 Subroutine of ctor. */
449 state_purge_per_ssa_name::before_use_stmt (const state_purge_map
&map
,
450 const gimple
*use_stmt
)
452 gcc_assert (use_stmt
->code
!= GIMPLE_PHI
);
454 const supernode
*supernode
455 = map
.get_sg ().get_supernode_for_stmt (use_stmt
);
456 unsigned int stmt_idx
= supernode
->get_stmt_index (use_stmt
);
457 return function_point::before_stmt (supernode
, stmt_idx
);
460 /* Add POINT to *WORKLIST if the point has not already been seen.
461 Subroutine of ctor. */
464 state_purge_per_ssa_name::add_to_worklist (const function_point
&point
,
465 auto_vec
<function_point
> *worklist
,
471 logger
->start_log_line ();
472 logger
->log_partial ("point: '");
473 point
.print (logger
->get_printer (), format (false));
474 logger
->log_partial ("' for worklist for %qE", m_name
);
475 logger
->end_log_line ();
478 gcc_assert (point
.get_function () == &get_function ());
479 if (point
.get_from_edge ())
480 gcc_assert (point
.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE
);
482 if (m_points_needing_name
.contains (point
))
485 logger
->log ("already seen for %qE", m_name
);
490 logger
->log ("not seen; adding to worklist for %qE", m_name
);
491 m_points_needing_name
.add (point
);
492 worklist
->safe_push (point
);
496 /* Return true iff NAME is used by any of the phi nodes in SNODE
497 when processing the in-edge with PHI_ARG_IDX. */
500 name_used_by_phis_p (tree name
, const supernode
*snode
,
503 gcc_assert (TREE_CODE (name
) == SSA_NAME
);
505 for (gphi_iterator gpi
506 = const_cast<supernode
*> (snode
)->start_phis ();
507 !gsi_end_p (gpi
); gsi_next (&gpi
))
509 gphi
*phi
= gpi
.phi ();
510 if (gimple_phi_arg_def (phi
, phi_arg_idx
) == name
)
516 /* Process POINT, popped from WORKLIST.
517 Iterate over predecessors of POINT, adding to WORKLIST. */
520 state_purge_per_ssa_name::process_point (const function_point
&point
,
521 auto_vec
<function_point
> *worklist
,
522 const state_purge_map
&map
)
524 logger
*logger
= map
.get_logger ();
528 logger
->start_log_line ();
529 logger
->log_partial ("considering point: '");
530 point
.print (logger
->get_printer (), format (false));
531 logger
->log_partial ("' for %qE", m_name
);
532 logger
->end_log_line ();
535 gimple
*def_stmt
= SSA_NAME_DEF_STMT (m_name
);
537 const supernode
*snode
= point
.get_supernode ();
539 switch (point
.get_kind ())
547 case PK_BEFORE_SUPERNODE
:
549 for (gphi_iterator gpi
550 = const_cast<supernode
*> (snode
)->start_phis ();
551 !gsi_end_p (gpi
); gsi_next (&gpi
))
553 gcc_assert (point
.get_from_edge ());
554 const cfg_superedge
*cfg_sedge
555 = point
.get_from_edge ()->dyn_cast_cfg_superedge ();
556 gcc_assert (cfg_sedge
);
558 gphi
*phi
= gpi
.phi ();
559 /* Are we at the def-stmt for m_name? */
562 if (name_used_by_phis_p (m_name
, snode
,
563 cfg_sedge
->get_phi_arg_idx ()))
566 logger
->log ("name in def stmt used within phis;"
572 logger
->log ("name in def stmt not used within phis;"
579 /* Add given pred to worklist. */
580 if (point
.get_from_edge ())
582 gcc_assert (point
.get_from_edge ()->m_src
);
584 (function_point::after_supernode (point
.get_from_edge ()->m_src
),
589 /* Add any intraprocedually edge for a call. */
590 if (snode
->m_returning_call
)
592 gcall
*returning_call
= snode
->m_returning_call
;
594 = supergraph_call_edge (snode
->m_fun
,
599 = map
.get_sg ().get_intraprocedural_edge_for_call (cedge
);
602 (function_point::after_supernode (sedge
->m_src
),
607 supernode
*callernode
608 = map
.get_sg ().get_supernode_for_stmt (returning_call
);
610 gcc_assert (callernode
);
612 (function_point::after_supernode (callernode
),
622 if (def_stmt
== point
.get_stmt ())
625 logger
->log ("def stmt; terminating");
628 if (point
.get_stmt_idx () > 0)
629 add_to_worklist (function_point::before_stmt
630 (snode
, point
.get_stmt_idx () - 1),
634 /* Add before_supernode to worklist. This captures the in-edge,
635 so we have to do it once per in-edge. */
638 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
639 add_to_worklist (function_point::before_supernode (snode
,
646 case PK_AFTER_SUPERNODE
:
648 if (snode
->m_stmts
.length ())
650 (function_point::before_stmt (snode
,
651 snode
->m_stmts
.length () - 1),
655 /* Add before_supernode to worklist. This captures the in-edge,
656 so we have to do it once per in-edge. */
659 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
660 add_to_worklist (function_point::before_supernode (snode
,
663 /* If it's the initial BB, add it, to ensure that we
664 have "before supernode" for the initial ENTRY block, and don't
665 erroneously purge SSA names for initial values of parameters. */
666 if (snode
->entry_p ())
669 (function_point::before_supernode (snode
, NULL
),
678 /* class state_purge_per_decl : public state_purge_per_tree. */
680 /* state_purge_per_decl's ctor. */
682 state_purge_per_decl::state_purge_per_decl (const state_purge_map
&map
,
685 : state_purge_per_tree (fun
),
688 /* The RESULT_DECL is always needed at the end of its function. */
689 if (TREE_CODE (decl
) == RESULT_DECL
)
691 supernode
*exit_snode
= map
.get_sg ().get_node_for_function_exit (fun
);
692 add_needed_at (function_point::after_supernode (exit_snode
));
696 /* Mark the value of the decl (or a subvalue within it) as being needed
700 state_purge_per_decl::add_needed_at (const function_point
&point
)
702 m_points_needing_decl
.add (point
);
705 /* Mark that a pointer to the decl (or a region within it) is taken
709 state_purge_per_decl::add_pointed_to_at (const function_point
&point
)
711 m_points_taking_address
.add (point
);
714 /* Process the worklists for this decl:
715 (a) walk backwards from points where we know the value of the decl
716 is needed, marking points until we get to a stmt that fully overwrites
718 (b) walk forwards from points where the address of the decl is taken,
719 marking points as potentially needing the value of the decl. */
722 state_purge_per_decl::process_worklists (const state_purge_map
&map
,
723 region_model_manager
*mgr
)
725 logger
*logger
= map
.get_logger ();
728 logger
->log ("decl: %qE within %qD", m_decl
, get_fndecl ());
730 /* Worklist for walking backwards from uses. */
732 auto_vec
<function_point
> worklist
;
735 /* Add all uses of the decl to the worklist. */
736 for (auto iter
: m_points_needing_decl
)
737 worklist
.safe_push (iter
);
739 region_model
model (mgr
);
740 model
.push_frame (get_function (), NULL
, NULL
);
742 /* Process worklist by walking backwards until we reach a stmt
743 that fully overwrites the decl. */
745 log_scope
s (logger
, "processing worklist");
746 while (worklist
.length () > 0)
748 function_point point
= worklist
.pop ();
749 process_point_backwards (point
, &worklist
, &seen
, map
, model
);
754 /* Worklist for walking forwards from address-taken points. */
756 auto_vec
<function_point
> worklist
;
759 /* Add all uses of the decl to the worklist. */
760 for (auto iter
: m_points_taking_address
)
762 worklist
.safe_push (iter
);
764 /* Add to m_points_needing_decl (now that we traversed
765 it above for the backward worklist). */
766 m_points_needing_decl
.add (iter
);
769 /* Process worklist by walking forwards. */
771 log_scope
s (logger
, "processing worklist");
772 while (worklist
.length () > 0)
774 function_point point
= worklist
.pop ();
775 process_point_forwards (point
, &worklist
, &seen
, map
);
781 /* Add POINT to *WORKLIST if the point is not already in *seen.
782 Add newly seen points to *SEEN and to m_points_needing_name. */
785 state_purge_per_decl::add_to_worklist (const function_point
&point
,
786 auto_vec
<function_point
> *worklist
,
793 logger
->start_log_line ();
794 logger
->log_partial ("point: '");
795 point
.print (logger
->get_printer (), format (false));
796 logger
->log_partial ("' for worklist for %qE", m_decl
);
797 logger
->end_log_line ();
800 gcc_assert (point
.get_function () == &get_function ());
801 if (point
.get_from_edge ())
802 gcc_assert (point
.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE
);
804 if (seen
->contains (point
))
807 logger
->log ("already seen for %qE", m_decl
);
812 logger
->log ("not seen; adding to worklist for %qE", m_decl
);
813 m_points_needing_decl
.add (point
);
815 worklist
->safe_push (point
);
819 /* Determine if REG_A and REG_B express writing to exactly the same
821 For example, when "s.field" is the only field of "s", and there's no
822 padding, this should return true. */
825 same_binding_p (const region
*reg_a
, const region
*reg_b
,
826 store_manager
*store_mgr
)
828 if (reg_a
->get_base_region () != reg_b
->get_base_region ())
830 if (reg_a
->empty_p ())
832 const binding_key
*bind_key_a
= binding_key::make (store_mgr
, reg_a
);
833 if (reg_b
->empty_p ())
835 const binding_key
*bind_key_b
= binding_key::make (store_mgr
, reg_b
);
836 return bind_key_a
== bind_key_b
;
839 /* Return true if STMT fully overwrites DECL. */
842 fully_overwrites_p (const gimple
*stmt
, tree decl
,
843 const region_model
&model
)
845 if (tree lhs
= gimple_get_lhs (stmt
))
847 /* Determine if LHS fully overwrites DECL.
848 We can't just check for equality; consider the case of
849 "s.field = EXPR;" where the stmt writes to the only field
850 of "s", and there's no padding. */
851 const region
*lhs_reg
= model
.get_lvalue (lhs
, NULL
);
852 const region
*decl_reg
= model
.get_lvalue (decl
, NULL
);
853 if (same_binding_p (lhs_reg
, decl_reg
,
854 model
.get_manager ()->get_store_manager ()))
860 /* Process POINT, popped from *WORKLIST.
861 Iterate over predecessors of POINT, adding to *WORKLIST and *SEEN,
862 until we get to a stmt that fully overwrites the decl. */
865 state_purge_per_decl::
866 process_point_backwards (const function_point
&point
,
867 auto_vec
<function_point
> *worklist
,
869 const state_purge_map
&map
,
870 const region_model
&model
)
872 logger
*logger
= map
.get_logger ();
876 logger
->start_log_line ();
877 logger
->log_partial ("considering point: '");
878 point
.print (logger
->get_printer (), format (false));
879 logger
->log_partial ("' for %qE", m_decl
);
880 logger
->end_log_line ();
882 const supernode
*snode
= point
.get_supernode ();
884 switch (point
.get_kind ())
892 case PK_BEFORE_SUPERNODE
:
894 /* Add given pred to worklist. */
895 if (point
.get_from_edge ())
897 gcc_assert (point
.get_from_edge ()->m_src
);
899 (function_point::after_supernode (point
.get_from_edge ()->m_src
),
900 worklist
, seen
, logger
);
904 /* Add any intraprocedually edge for a call. */
905 if (snode
->m_returning_call
)
907 gcall
*returning_call
= snode
->m_returning_call
;
909 = supergraph_call_edge (snode
->m_fun
,
914 = map
.get_sg ().get_intraprocedural_edge_for_call (cedge
);
917 (function_point::after_supernode (sedge
->m_src
),
918 worklist
, seen
, logger
);
922 supernode
*callernode
923 = map
.get_sg ().get_supernode_for_stmt (returning_call
);
925 gcc_assert (callernode
);
927 (function_point::after_supernode (callernode
),
928 worklist
, seen
, logger
);
937 /* This is somewhat equivalent to how the SSA case handles
939 if (fully_overwrites_p (point
.get_stmt (), m_decl
, model
)
940 /* ...but we mustn't be at a point that also consumes the
941 current value of the decl when it's generating the new
942 value, for cases such as
946 where we want to make sure that we don't stop at the:
948 since otherwise we would erroneously purge the state of "s"
952 && !m_points_needing_decl
.contains (point
))
955 logger
->log ("stmt fully overwrites %qE; terminating", m_decl
);
958 if (point
.get_stmt_idx () > 0)
959 add_to_worklist (function_point::before_stmt
960 (snode
, point
.get_stmt_idx () - 1),
961 worklist
, seen
, logger
);
964 /* Add before_supernode to worklist. This captures the in-edge,
965 so we have to do it once per in-edge. */
968 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
969 add_to_worklist (function_point::before_supernode (snode
,
971 worklist
, seen
, logger
);
976 case PK_AFTER_SUPERNODE
:
978 if (snode
->m_stmts
.length ())
980 (function_point::before_stmt (snode
,
981 snode
->m_stmts
.length () - 1),
982 worklist
, seen
, logger
);
985 /* Add before_supernode to worklist. This captures the in-edge,
986 so we have to do it once per in-edge. */
989 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
990 add_to_worklist (function_point::before_supernode (snode
,
992 worklist
, seen
, logger
);
1000 /* Process POINT, popped from *WORKLIST.
1001 Iterate over successors of POINT, adding to *WORKLIST and *SEEN. */
1004 state_purge_per_decl::
1005 process_point_forwards (const function_point
&point
,
1006 auto_vec
<function_point
> *worklist
,
1008 const state_purge_map
&map
)
1010 logger
*logger
= map
.get_logger ();
1014 logger
->start_log_line ();
1015 logger
->log_partial ("considering point: '");
1016 point
.print (logger
->get_printer (), format (false));
1017 logger
->log_partial ("' for %qE", m_decl
);
1018 logger
->end_log_line ();
1020 const supernode
*snode
= point
.get_supernode ();
1022 switch (point
.get_kind ())
1028 case PK_BEFORE_SUPERNODE
:
1030 function_point next
= point
.get_next ();
1031 add_to_worklist (next
, worklist
, seen
, logger
);
1035 case PK_BEFORE_STMT
:
1037 /* Perhaps we should stop at a clobber of the decl,
1038 but that ought to purge state for us explicitly. */
1039 function_point next
= point
.get_next ();
1040 add_to_worklist (next
, worklist
, seen
, logger
);
1044 case PK_AFTER_SUPERNODE
:
1046 /* Look at interprocedural out-edges. */
1049 FOR_EACH_VEC_ELT (snode
->m_succs
, i
, succ
)
1051 enum edge_kind kind
= succ
->get_kind ();
1052 if (kind
== SUPEREDGE_CFG_EDGE
1053 || kind
== SUPEREDGE_INTRAPROCEDURAL_CALL
)
1054 add_to_worklist (function_point::before_supernode (succ
->m_dest
,
1056 worklist
, seen
, logger
);
1063 /* Return true if the decl is needed at POINT. */
1066 state_purge_per_decl::needed_at_point_p (const function_point
&point
) const
1068 return const_cast <point_set_t
&> (m_points_needing_decl
).contains (point
);
1071 /* class state_purge_annotator : public dot_annotator. */
1073 /* Implementation of dot_annotator::add_node_annotations vfunc for
1074 state_purge_annotator.
1076 Add an additional record showing which names are purged on entry
1077 to the supernode N. */
1080 state_purge_annotator::add_node_annotations (graphviz_out
*gv
,
1082 bool within_table
) const
1090 pretty_printer
*pp
= gv
->get_pp ();
1092 pp_printf (pp
, "annotation_for_node_%i", n
.m_index
);
1093 pp_printf (pp
, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
1095 pp_write_text_to_stream (pp
);
1097 /* Different in-edges mean different names need purging.
1098 Determine which points to dump. */
1099 auto_vec
<function_point
> points
;
1100 if (n
.entry_p () || n
.m_returning_call
)
1101 points
.safe_push (function_point::before_supernode (&n
, NULL
));
1103 for (auto inedge
: n
.m_preds
)
1104 points
.safe_push (function_point::before_supernode (&n
, inedge
));
1105 points
.safe_push (function_point::after_supernode (&n
));
1107 for (auto & point
: points
)
1109 point
.print (pp
, format (true));
1111 print_needed (gv
, point
, false);
1115 pp_string (pp
, "\"];\n\n");
1120 /* Print V to GV as a comma-separated list in braces, titling it with TITLE.
1121 If WITHIN_TABLE is true, print it within a <TR>
1123 Subroutine of state_purge_annotator::print_needed. */
1126 print_vec_of_names (graphviz_out
*gv
, const char *title
,
1127 const auto_vec
<tree
> &v
, bool within_table
)
1129 pretty_printer
*pp
= gv
->get_pp ();
1134 pp_printf (pp
, "%s: {", title
);
1135 FOR_EACH_VEC_ELT (v
, i
, name
)
1138 pp_string (pp
, ", ");
1139 pp_printf (pp
, "%qE", name
);
1141 pp_printf (pp
, "}");
1144 pp_write_text_as_html_like_dot_to_stream (pp
);
1150 /* Implementation of dot_annotator::add_stmt_annotations for
1151 state_purge_annotator.
1153 Add text showing which names are purged at STMT. */
1156 state_purge_annotator::add_stmt_annotations (graphviz_out
*gv
,
1158 bool within_row
) const
1166 if (stmt
->code
== GIMPLE_PHI
)
1169 pretty_printer
*pp
= gv
->get_pp ();
1173 const supernode
*supernode
= m_map
->get_sg ().get_supernode_for_stmt (stmt
);
1174 unsigned int stmt_idx
= supernode
->get_stmt_index (stmt
);
1175 function_point before_stmt
1176 (function_point::before_stmt (supernode
, stmt_idx
));
1178 print_needed (gv
, before_stmt
, true);
1181 /* Get the decls and SSA names needed and not-needed at POINT, and
1183 If WITHIN_TABLE is true, print them within <TR> elements. */
1186 state_purge_annotator::print_needed (graphviz_out
*gv
,
1187 const function_point
&point
,
1188 bool within_table
) const
1190 auto_vec
<tree
> needed
;
1191 auto_vec
<tree
> not_needed
;
1192 for (state_purge_map::ssa_iterator iter
= m_map
->begin_ssas ();
1193 iter
!= m_map
->end_ssas ();
1196 tree name
= (*iter
).first
;
1197 state_purge_per_ssa_name
*per_name_data
= (*iter
).second
;
1198 if (&per_name_data
->get_function () == point
.get_function ())
1200 if (per_name_data
->needed_at_point_p (point
))
1201 needed
.safe_push (name
);
1203 not_needed
.safe_push (name
);
1206 for (state_purge_map::decl_iterator iter
= m_map
->begin_decls ();
1207 iter
!= m_map
->end_decls ();
1210 tree decl
= (*iter
).first
;
1211 state_purge_per_decl
*per_decl_data
= (*iter
).second
;
1212 if (&per_decl_data
->get_function () == point
.get_function ())
1214 if (per_decl_data
->needed_at_point_p (point
))
1215 needed
.safe_push (decl
);
1217 not_needed
.safe_push (decl
);
1221 print_vec_of_names (gv
, "needed here", needed
, within_table
);
1222 print_vec_of_names (gv
, "not needed here", not_needed
, within_table
);
1225 #endif /* #if ENABLE_ANALYZER */