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
23 #define INCLUDE_VECTOR
25 #include "coretypes.h"
28 #include "tree-ssa-alias.h"
30 #include "basic-block.h"
32 #include "stringpool.h"
34 #include "gimple-ssa.h"
35 #include "tree-ssanames.h"
36 #include "tree-phinodes.h"
38 #include "ssa-iterators.h"
39 #include "diagnostic-core.h"
40 #include "gimple-pretty-print.h"
41 #include "analyzer/analyzer.h"
42 #include "analyzer/call-string.h"
43 #include "analyzer/supergraph.h"
44 #include "analyzer/program-point.h"
45 #include "analyzer/analyzer-logging.h"
46 #include "analyzer/state-purge.h"
47 #include "analyzer/store.h"
48 #include "analyzer/region-model.h"
49 #include "gimple-walk.h"
54 /* Given NODE at an access, determine if this access is within
55 a decl that could be consider for purging, and if so, return the decl. */
58 get_candidate_for_purging (tree node
)
62 switch (TREE_CODE (iter
))
70 iter
= TREE_OPERAND (iter
, 0);
74 if (is_global_var (iter
))
85 /* Class-based handler for walk_stmt_load_store_addr_ops at a particular
86 function_point, for populating the worklists within a state_purge_map. */
88 class gimple_op_visitor
: public log_user
91 gimple_op_visitor (state_purge_map
*map
,
92 const function_point
&point
,
94 : log_user (map
->get_logger ()),
100 bool on_load (gimple
*stmt
, tree base
, tree op
)
102 LOG_FUNC (get_logger ());
106 pp_gimple_stmt_1 (&pp
, stmt
, 0, (dump_flags_t
)0);
107 log ("on_load: %s; base: %qE, op: %qE",
108 pp_formatted_text (&pp
), base
, op
);
110 if (tree node
= get_candidate_for_purging (base
))
115 bool on_store (gimple
*stmt
, tree base
, tree op
)
117 LOG_FUNC (get_logger ());
121 pp_gimple_stmt_1 (&pp
, stmt
, 0, (dump_flags_t
)0);
122 log ("on_store: %s; base: %qE, op: %qE",
123 pp_formatted_text (&pp
), base
, op
);
128 bool on_addr (gimple
*stmt
, tree base
, tree op
)
130 LOG_FUNC (get_logger ());
134 pp_gimple_stmt_1 (&pp
, stmt
, 0, (dump_flags_t
)0);
135 log ("on_addr: %s; base: %qE, op: %qE",
136 pp_formatted_text (&pp
), base
, op
);
138 if (TREE_CODE (op
) != ADDR_EXPR
)
140 if (tree node
= get_candidate_for_purging (base
))
143 add_pointed_to (node
);
149 void add_needed (tree decl
)
151 gcc_assert (get_candidate_for_purging (decl
) == decl
);
152 state_purge_per_decl
&data
153 = get_or_create_data_for_decl (decl
);
154 data
.add_needed_at (m_point
);
156 /* Handle calls: if we're seeing a use at a call, then add a use at the
157 "after-supernode" point (in case of interprocedural call superedges). */
158 if (m_point
.final_stmt_p ())
159 data
.add_needed_at (m_point
.get_next ());
162 void add_pointed_to (tree decl
)
164 gcc_assert (get_candidate_for_purging (decl
) == decl
);
165 get_or_create_data_for_decl (decl
).add_pointed_to_at (m_point
);
168 state_purge_per_decl
&
169 get_or_create_data_for_decl (tree decl
)
171 return m_map
->get_or_create_data_for_decl (m_fun
, decl
);
174 state_purge_map
*m_map
;
175 const function_point
&m_point
;
176 const function
&m_fun
;
180 my_load_cb (gimple
*stmt
, tree base
, tree op
, void *user_data
)
182 gimple_op_visitor
*x
= (gimple_op_visitor
*)user_data
;
183 return x
->on_load (stmt
, base
, op
);
187 my_store_cb (gimple
*stmt
, tree base
, tree op
, void *user_data
)
189 gimple_op_visitor
*x
= (gimple_op_visitor
*)user_data
;
190 return x
->on_store (stmt
, base
, op
);
194 my_addr_cb (gimple
*stmt
, tree base
, tree op
, void *user_data
)
196 gimple_op_visitor
*x
= (gimple_op_visitor
*)user_data
;
197 return x
->on_addr (stmt
, base
, op
);
200 /* state_purge_map's ctor. Walk all SSA names in all functions, building
201 a state_purge_per_ssa_name instance for each.
202 Also, walk all loads and address-taken ops of local variables, building
203 a state_purge_per_decl as appropriate. */
205 state_purge_map::state_purge_map (const supergraph
&sg
,
206 region_model_manager
*mgr
,
208 : log_user (logger
), m_sg (sg
)
212 auto_timevar
tv (TV_ANALYZER_STATE_PURGE
);
215 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node
)
217 function
*fun
= node
->get_fun ();
220 log ("function: %s", function_name (fun
));
223 FOR_EACH_SSA_NAME (i
, name
, fun
)
225 /* For now, don't bother tracking the .MEM SSA names. */
226 if (tree var
= SSA_NAME_VAR (name
))
227 if (TREE_CODE (var
) == VAR_DECL
)
228 if (VAR_DECL_IS_VIRTUAL_OPERAND (var
))
230 m_ssa_map
.put (name
, new state_purge_per_ssa_name (*this, name
, *fun
));
234 /* Find all uses of local vars.
235 We iterate through all function points, finding loads, stores, and
236 address-taken operations on locals, building a pair of worklists. */
237 for (auto snode
: sg
.m_nodes
)
240 log ("SN: %i", snode
->m_index
);
241 /* We ignore m_returning_call and phi nodes. */
244 FOR_EACH_VEC_ELT (snode
->m_stmts
, i
, stmt
)
246 function
*fun
= snode
->get_function ();
248 function_point
point (function_point::before_stmt (snode
, i
));
249 gimple_op_visitor
v (this, point
, *fun
);
250 walk_stmt_load_store_addr_ops (stmt
, &v
,
251 my_load_cb
, my_store_cb
, my_addr_cb
);
255 /* Now iterate through the m_decl_map, processing each pair of worklists. */
256 for (state_purge_map::decl_iterator iter
= begin_decls ();
257 iter
!= end_decls ();
260 state_purge_per_decl
*per_decl_data
= (*iter
).second
;
261 per_decl_data
->process_worklists (*this, mgr
);
265 /* state_purge_map's dtor. */
267 state_purge_map::~state_purge_map ()
269 for (auto iter
: m_ssa_map
)
271 for (auto iter
: m_decl_map
)
275 /* Get the state_purge_per_decl for local DECL within FUN, creating it
278 state_purge_per_decl
&
279 state_purge_map::get_or_create_data_for_decl (const function
&fun
, tree decl
)
281 if (state_purge_per_decl
**slot
282 = const_cast <decl_map_t
&> (m_decl_map
).get (decl
))
284 state_purge_per_decl
*result
= new state_purge_per_decl (*this, decl
, fun
);
285 m_decl_map
.put (decl
, result
);
289 /* class state_purge_per_ssa_name : public state_purge_per_tree. */
291 /* state_purge_per_ssa_name's ctor.
293 Locate all uses of VAR within FUN.
294 Walk backwards from each use, marking program points, until
295 we reach the def stmt, populating m_points_needing_var.
297 We have to track program points rather than
298 just stmts since there could be empty basic blocks on the way. */
300 state_purge_per_ssa_name::state_purge_per_ssa_name (const state_purge_map
&map
,
303 : state_purge_per_tree (fun
), m_points_needing_name (), m_name (name
)
305 LOG_FUNC (map
.get_logger ());
307 if (map
.get_logger ())
309 map
.log ("SSA name: %qE within %qD", name
, fun
.decl
);
312 const gimple
*def_stmt
= SSA_NAME_DEF_STMT (name
);
314 pp_gimple_stmt_1 (&pp
, def_stmt
, 0, (dump_flags_t
)0);
315 map
.log ("def stmt: %s", pp_formatted_text (&pp
));
318 auto_vec
<function_point
> worklist
;
320 /* Add all immediate uses of name to the worklist.
321 Compare with debug_immediate_uses. */
322 imm_use_iterator iter
;
324 FOR_EACH_IMM_USE_FAST (use_p
, iter
, name
)
326 if (USE_STMT (use_p
))
328 const gimple
*use_stmt
= USE_STMT (use_p
);
329 if (map
.get_logger ())
332 pp_gimple_stmt_1 (&pp
, use_stmt
, 0, (dump_flags_t
)0);
333 map
.log ("used by stmt: %s", pp_formatted_text (&pp
));
336 if (is_gimple_debug (use_stmt
))
338 /* We skipped debug stmts when building the supergraph,
339 so ignore them now. */
340 if (map
.get_logger ())
341 map
.log ("skipping debug stmt");
345 const supernode
*snode
346 = map
.get_sg ().get_supernode_for_stmt (use_stmt
);
348 /* If it's a use within a phi node, then we care about
349 which in-edge we came from. */
350 if (use_stmt
->code
== GIMPLE_PHI
)
352 for (gphi_iterator gpi
353 = const_cast<supernode
*> (snode
)->start_phis ();
354 !gsi_end_p (gpi
); gsi_next (&gpi
))
356 gphi
*phi
= gpi
.phi ();
359 /* Find arguments (and thus in-edges) which use NAME. */
360 for (unsigned arg_idx
= 0;
361 arg_idx
< gimple_phi_num_args (phi
);
364 if (name
== gimple_phi_arg (phi
, arg_idx
)->def
)
366 edge in_edge
= gimple_phi_arg_edge (phi
, arg_idx
);
367 const superedge
*in_sedge
368 = map
.get_sg ().get_edge_for_cfg_edge (in_edge
);
370 = function_point::before_supernode
372 add_to_worklist (point
, &worklist
,
374 m_points_needing_name
.add (point
);
382 function_point point
= before_use_stmt (map
, use_stmt
);
383 add_to_worklist (point
, &worklist
, map
.get_logger ());
384 m_points_needing_name
.add (point
);
386 /* We also need to add uses for conditionals and switches,
387 where the stmt "happens" at the after_supernode, for filtering
389 if (use_stmt
== snode
->get_last_stmt ())
391 if (map
.get_logger ())
392 map
.log ("last stmt in BB");
394 = function_point::after_supernode (snode
);
395 add_to_worklist (point
, &worklist
, map
.get_logger ());
396 m_points_needing_name
.add (point
);
399 if (map
.get_logger ())
400 map
.log ("not last stmt in BB");
405 /* Process worklist by walking backwards until we reach the def stmt. */
407 log_scope
s (map
.get_logger (), "processing worklist");
408 while (worklist
.length () > 0)
410 function_point point
= worklist
.pop ();
411 process_point (point
, &worklist
, map
);
415 if (map
.get_logger ())
417 map
.log ("%qE in %qD is needed to process:", name
, fun
.decl
);
418 /* Log m_points_needing_name, sorting it to avoid churn when comparing
420 auto_vec
<function_point
> points
;
421 for (point_set_t::iterator iter
= m_points_needing_name
.begin ();
422 iter
!= m_points_needing_name
.end ();
424 points
.safe_push (*iter
);
425 points
.qsort (function_point::cmp_ptr
);
427 function_point
*point
;
428 FOR_EACH_VEC_ELT (points
, i
, point
)
430 map
.start_log_line ();
431 map
.get_logger ()->log_partial (" point: ");
432 point
->print (map
.get_logger ()->get_printer (), format (false));
438 /* Return true if the SSA name is needed at POINT. */
441 state_purge_per_ssa_name::needed_at_point_p (const function_point
&point
) const
443 return const_cast <point_set_t
&> (m_points_needing_name
).contains (point
);
446 /* Get the function_point representing immediately before USE_STMT.
447 Subroutine of ctor. */
450 state_purge_per_ssa_name::before_use_stmt (const state_purge_map
&map
,
451 const gimple
*use_stmt
)
453 gcc_assert (use_stmt
->code
!= GIMPLE_PHI
);
455 const supernode
*supernode
456 = map
.get_sg ().get_supernode_for_stmt (use_stmt
);
457 unsigned int stmt_idx
= supernode
->get_stmt_index (use_stmt
);
458 return function_point::before_stmt (supernode
, stmt_idx
);
461 /* Add POINT to *WORKLIST if the point has not already been seen.
462 Subroutine of ctor. */
465 state_purge_per_ssa_name::add_to_worklist (const function_point
&point
,
466 auto_vec
<function_point
> *worklist
,
472 logger
->start_log_line ();
473 logger
->log_partial ("point: '");
474 point
.print (logger
->get_printer (), format (false));
475 logger
->log_partial ("' for worklist for %qE", m_name
);
476 logger
->end_log_line ();
479 gcc_assert (point
.get_function () == &get_function ());
480 if (point
.get_from_edge ())
481 gcc_assert (point
.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE
);
483 if (m_points_needing_name
.contains (point
))
486 logger
->log ("already seen for %qE", m_name
);
491 logger
->log ("not seen; adding to worklist for %qE", m_name
);
492 m_points_needing_name
.add (point
);
493 worklist
->safe_push (point
);
497 /* Return true iff NAME is used by any of the phi nodes in SNODE
498 when processing the in-edge with PHI_ARG_IDX. */
501 name_used_by_phis_p (tree name
, const supernode
*snode
,
504 gcc_assert (TREE_CODE (name
) == SSA_NAME
);
506 for (gphi_iterator gpi
507 = const_cast<supernode
*> (snode
)->start_phis ();
508 !gsi_end_p (gpi
); gsi_next (&gpi
))
510 gphi
*phi
= gpi
.phi ();
511 if (gimple_phi_arg_def (phi
, phi_arg_idx
) == name
)
517 /* Process POINT, popped from WORKLIST.
518 Iterate over predecessors of POINT, adding to WORKLIST. */
521 state_purge_per_ssa_name::process_point (const function_point
&point
,
522 auto_vec
<function_point
> *worklist
,
523 const state_purge_map
&map
)
525 logger
*logger
= map
.get_logger ();
529 logger
->start_log_line ();
530 logger
->log_partial ("considering point: '");
531 point
.print (logger
->get_printer (), format (false));
532 logger
->log_partial ("' for %qE", m_name
);
533 logger
->end_log_line ();
536 gimple
*def_stmt
= SSA_NAME_DEF_STMT (m_name
);
538 const supernode
*snode
= point
.get_supernode ();
540 switch (point
.get_kind ())
548 case PK_BEFORE_SUPERNODE
:
550 for (gphi_iterator gpi
551 = const_cast<supernode
*> (snode
)->start_phis ();
552 !gsi_end_p (gpi
); gsi_next (&gpi
))
554 gcc_assert (point
.get_from_edge ());
555 const cfg_superedge
*cfg_sedge
556 = point
.get_from_edge ()->dyn_cast_cfg_superedge ();
557 gcc_assert (cfg_sedge
);
559 gphi
*phi
= gpi
.phi ();
560 /* Are we at the def-stmt for m_name? */
563 if (name_used_by_phis_p (m_name
, snode
,
564 cfg_sedge
->get_phi_arg_idx ()))
567 logger
->log ("name in def stmt used within phis;"
573 logger
->log ("name in def stmt not used within phis;"
580 /* Add given pred to worklist. */
581 if (point
.get_from_edge ())
583 gcc_assert (point
.get_from_edge ()->m_src
);
585 (function_point::after_supernode (point
.get_from_edge ()->m_src
),
590 /* Add any intraprocedually edge for a call. */
591 if (snode
->m_returning_call
)
593 gcall
*returning_call
= snode
->m_returning_call
;
595 = supergraph_call_edge (snode
->m_fun
,
600 = map
.get_sg ().get_intraprocedural_edge_for_call (cedge
);
603 (function_point::after_supernode (sedge
->m_src
),
608 supernode
*callernode
609 = map
.get_sg ().get_supernode_for_stmt (returning_call
);
611 gcc_assert (callernode
);
613 (function_point::after_supernode (callernode
),
623 if (def_stmt
== point
.get_stmt ())
626 logger
->log ("def stmt; terminating");
629 if (point
.get_stmt_idx () > 0)
630 add_to_worklist (function_point::before_stmt
631 (snode
, point
.get_stmt_idx () - 1),
635 /* Add before_supernode to worklist. This captures the in-edge,
636 so we have to do it once per in-edge. */
639 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
640 add_to_worklist (function_point::before_supernode (snode
,
647 case PK_AFTER_SUPERNODE
:
649 if (snode
->m_stmts
.length ())
651 (function_point::before_stmt (snode
,
652 snode
->m_stmts
.length () - 1),
656 /* Add before_supernode to worklist. This captures the in-edge,
657 so we have to do it once per in-edge. */
660 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
661 add_to_worklist (function_point::before_supernode (snode
,
664 /* If it's the initial BB, add it, to ensure that we
665 have "before supernode" for the initial ENTRY block, and don't
666 erroneously purge SSA names for initial values of parameters. */
667 if (snode
->entry_p ())
670 (function_point::before_supernode (snode
, NULL
),
679 /* class state_purge_per_decl : public state_purge_per_tree. */
681 /* state_purge_per_decl's ctor. */
683 state_purge_per_decl::state_purge_per_decl (const state_purge_map
&map
,
686 : state_purge_per_tree (fun
),
689 /* The RESULT_DECL is always needed at the end of its function. */
690 if (TREE_CODE (decl
) == RESULT_DECL
)
692 supernode
*exit_snode
= map
.get_sg ().get_node_for_function_exit (fun
);
693 add_needed_at (function_point::after_supernode (exit_snode
));
697 /* Mark the value of the decl (or a subvalue within it) as being needed
701 state_purge_per_decl::add_needed_at (const function_point
&point
)
703 m_points_needing_decl
.add (point
);
706 /* Mark that a pointer to the decl (or a region within it) is taken
710 state_purge_per_decl::add_pointed_to_at (const function_point
&point
)
712 m_points_taking_address
.add (point
);
715 /* Process the worklists for this decl:
716 (a) walk backwards from points where we know the value of the decl
717 is needed, marking points until we get to a stmt that fully overwrites
719 (b) walk forwards from points where the address of the decl is taken,
720 marking points as potentially needing the value of the decl. */
723 state_purge_per_decl::process_worklists (const state_purge_map
&map
,
724 region_model_manager
*mgr
)
726 logger
*logger
= map
.get_logger ();
729 logger
->log ("decl: %qE within %qD", m_decl
, get_fndecl ());
731 /* Worklist for walking backwards from uses. */
733 auto_vec
<function_point
> worklist
;
736 /* Add all uses of the decl to the worklist. */
737 for (auto iter
: m_points_needing_decl
)
738 worklist
.safe_push (iter
);
740 region_model
model (mgr
);
741 model
.push_frame (get_function (), NULL
, NULL
);
743 /* Process worklist by walking backwards until we reach a stmt
744 that fully overwrites the decl. */
746 log_scope
s (logger
, "processing worklist");
747 while (worklist
.length () > 0)
749 function_point point
= worklist
.pop ();
750 process_point_backwards (point
, &worklist
, &seen
, map
, model
);
755 /* Worklist for walking forwards from address-taken points. */
757 auto_vec
<function_point
> worklist
;
760 /* Add all uses of the decl to the worklist. */
761 for (auto iter
: m_points_taking_address
)
763 worklist
.safe_push (iter
);
765 /* Add to m_points_needing_decl (now that we traversed
766 it above for the backward worklist). */
767 m_points_needing_decl
.add (iter
);
770 /* Process worklist by walking forwards. */
772 log_scope
s (logger
, "processing worklist");
773 while (worklist
.length () > 0)
775 function_point point
= worklist
.pop ();
776 process_point_forwards (point
, &worklist
, &seen
, map
);
782 /* Add POINT to *WORKLIST if the point is not already in *seen.
783 Add newly seen points to *SEEN and to m_points_needing_name. */
786 state_purge_per_decl::add_to_worklist (const function_point
&point
,
787 auto_vec
<function_point
> *worklist
,
794 logger
->start_log_line ();
795 logger
->log_partial ("point: '");
796 point
.print (logger
->get_printer (), format (false));
797 logger
->log_partial ("' for worklist for %qE", m_decl
);
798 logger
->end_log_line ();
801 gcc_assert (point
.get_function () == &get_function ());
802 if (point
.get_from_edge ())
803 gcc_assert (point
.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE
);
805 if (seen
->contains (point
))
808 logger
->log ("already seen for %qE", m_decl
);
813 logger
->log ("not seen; adding to worklist for %qE", m_decl
);
814 m_points_needing_decl
.add (point
);
816 worklist
->safe_push (point
);
820 /* Determine if REG_A and REG_B express writing to exactly the same
822 For example, when "s.field" is the only field of "s", and there's no
823 padding, this should return true. */
826 same_binding_p (const region
*reg_a
, const region
*reg_b
,
827 store_manager
*store_mgr
)
829 if (reg_a
->get_base_region () != reg_b
->get_base_region ())
831 if (reg_a
->empty_p ())
833 const binding_key
*bind_key_a
= binding_key::make (store_mgr
, reg_a
);
834 if (reg_b
->empty_p ())
836 const binding_key
*bind_key_b
= binding_key::make (store_mgr
, reg_b
);
837 return bind_key_a
== bind_key_b
;
840 /* Return true if STMT fully overwrites DECL. */
843 fully_overwrites_p (const gimple
*stmt
, tree decl
,
844 const region_model
&model
)
846 if (tree lhs
= gimple_get_lhs (stmt
))
848 /* Determine if LHS fully overwrites DECL.
849 We can't just check for equality; consider the case of
850 "s.field = EXPR;" where the stmt writes to the only field
851 of "s", and there's no padding. */
852 const region
*lhs_reg
= model
.get_lvalue (lhs
, NULL
);
853 const region
*decl_reg
= model
.get_lvalue (decl
, NULL
);
854 if (same_binding_p (lhs_reg
, decl_reg
,
855 model
.get_manager ()->get_store_manager ()))
861 /* Process POINT, popped from *WORKLIST.
862 Iterate over predecessors of POINT, adding to *WORKLIST and *SEEN,
863 until we get to a stmt that fully overwrites the decl. */
866 state_purge_per_decl::
867 process_point_backwards (const function_point
&point
,
868 auto_vec
<function_point
> *worklist
,
870 const state_purge_map
&map
,
871 const region_model
&model
)
873 logger
*logger
= map
.get_logger ();
877 logger
->start_log_line ();
878 logger
->log_partial ("considering point: '");
879 point
.print (logger
->get_printer (), format (false));
880 logger
->log_partial ("' for %qE", m_decl
);
881 logger
->end_log_line ();
883 const supernode
*snode
= point
.get_supernode ();
885 switch (point
.get_kind ())
893 case PK_BEFORE_SUPERNODE
:
895 /* Add given pred to worklist. */
896 if (point
.get_from_edge ())
898 gcc_assert (point
.get_from_edge ()->m_src
);
900 (function_point::after_supernode (point
.get_from_edge ()->m_src
),
901 worklist
, seen
, logger
);
905 /* Add any intraprocedually edge for a call. */
906 if (snode
->m_returning_call
)
908 gcall
*returning_call
= snode
->m_returning_call
;
910 = supergraph_call_edge (snode
->m_fun
,
915 = map
.get_sg ().get_intraprocedural_edge_for_call (cedge
);
918 (function_point::after_supernode (sedge
->m_src
),
919 worklist
, seen
, logger
);
923 supernode
*callernode
924 = map
.get_sg ().get_supernode_for_stmt (returning_call
);
926 gcc_assert (callernode
);
928 (function_point::after_supernode (callernode
),
929 worklist
, seen
, logger
);
938 /* This is somewhat equivalent to how the SSA case handles
940 if (fully_overwrites_p (point
.get_stmt (), m_decl
, model
)
941 /* ...but we mustn't be at a point that also consumes the
942 current value of the decl when it's generating the new
943 value, for cases such as
947 where we want to make sure that we don't stop at the:
949 since otherwise we would erroneously purge the state of "s"
953 && !m_points_needing_decl
.contains (point
))
956 logger
->log ("stmt fully overwrites %qE; terminating", m_decl
);
959 if (point
.get_stmt_idx () > 0)
960 add_to_worklist (function_point::before_stmt
961 (snode
, point
.get_stmt_idx () - 1),
962 worklist
, seen
, logger
);
965 /* Add before_supernode to worklist. This captures the in-edge,
966 so we have to do it once per in-edge. */
969 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
970 add_to_worklist (function_point::before_supernode (snode
,
972 worklist
, seen
, logger
);
977 case PK_AFTER_SUPERNODE
:
979 if (snode
->m_stmts
.length ())
981 (function_point::before_stmt (snode
,
982 snode
->m_stmts
.length () - 1),
983 worklist
, seen
, logger
);
986 /* Add before_supernode to worklist. This captures the in-edge,
987 so we have to do it once per in-edge. */
990 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
991 add_to_worklist (function_point::before_supernode (snode
,
993 worklist
, seen
, logger
);
1001 /* Process POINT, popped from *WORKLIST.
1002 Iterate over successors of POINT, adding to *WORKLIST and *SEEN. */
1005 state_purge_per_decl::
1006 process_point_forwards (const function_point
&point
,
1007 auto_vec
<function_point
> *worklist
,
1009 const state_purge_map
&map
)
1011 logger
*logger
= map
.get_logger ();
1015 logger
->start_log_line ();
1016 logger
->log_partial ("considering point: '");
1017 point
.print (logger
->get_printer (), format (false));
1018 logger
->log_partial ("' for %qE", m_decl
);
1019 logger
->end_log_line ();
1021 const supernode
*snode
= point
.get_supernode ();
1023 switch (point
.get_kind ())
1029 case PK_BEFORE_SUPERNODE
:
1031 function_point next
= point
.get_next ();
1032 add_to_worklist (next
, worklist
, seen
, logger
);
1036 case PK_BEFORE_STMT
:
1038 /* Perhaps we should stop at a clobber of the decl,
1039 but that ought to purge state for us explicitly. */
1040 function_point next
= point
.get_next ();
1041 add_to_worklist (next
, worklist
, seen
, logger
);
1045 case PK_AFTER_SUPERNODE
:
1047 /* Look at interprocedural out-edges. */
1050 FOR_EACH_VEC_ELT (snode
->m_succs
, i
, succ
)
1052 enum edge_kind kind
= succ
->get_kind ();
1053 if (kind
== SUPEREDGE_CFG_EDGE
1054 || kind
== SUPEREDGE_INTRAPROCEDURAL_CALL
)
1055 add_to_worklist (function_point::before_supernode (succ
->m_dest
,
1057 worklist
, seen
, logger
);
1064 /* Return true if the decl is needed at POINT. */
1067 state_purge_per_decl::needed_at_point_p (const function_point
&point
) const
1069 return const_cast <point_set_t
&> (m_points_needing_decl
).contains (point
);
1072 /* class state_purge_annotator : public dot_annotator. */
1074 /* Implementation of dot_annotator::add_node_annotations vfunc for
1075 state_purge_annotator.
1077 Add an additional record showing which names are purged on entry
1078 to the supernode N. */
1081 state_purge_annotator::add_node_annotations (graphviz_out
*gv
,
1083 bool within_table
) const
1091 pretty_printer
*pp
= gv
->get_pp ();
1093 pp_printf (pp
, "annotation_for_node_%i", n
.m_index
);
1094 pp_printf (pp
, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
1096 pp_write_text_to_stream (pp
);
1098 /* Different in-edges mean different names need purging.
1099 Determine which points to dump. */
1100 auto_vec
<function_point
> points
;
1101 if (n
.entry_p () || n
.m_returning_call
)
1102 points
.safe_push (function_point::before_supernode (&n
, NULL
));
1104 for (auto inedge
: n
.m_preds
)
1105 points
.safe_push (function_point::before_supernode (&n
, inedge
));
1106 points
.safe_push (function_point::after_supernode (&n
));
1108 for (auto & point
: points
)
1110 point
.print (pp
, format (true));
1112 print_needed (gv
, point
, false);
1116 pp_string (pp
, "\"];\n\n");
1121 /* Print V to GV as a comma-separated list in braces, titling it with TITLE.
1122 If WITHIN_TABLE is true, print it within a <TR>
1124 Subroutine of state_purge_annotator::print_needed. */
1127 print_vec_of_names (graphviz_out
*gv
, const char *title
,
1128 const auto_vec
<tree
> &v
, bool within_table
)
1130 pretty_printer
*pp
= gv
->get_pp ();
1135 pp_printf (pp
, "%s: {", title
);
1136 FOR_EACH_VEC_ELT (v
, i
, name
)
1139 pp_string (pp
, ", ");
1140 pp_printf (pp
, "%qE", name
);
1142 pp_printf (pp
, "}");
1145 pp_write_text_as_html_like_dot_to_stream (pp
);
1151 /* Implementation of dot_annotator::add_stmt_annotations for
1152 state_purge_annotator.
1154 Add text showing which names are purged at STMT. */
1157 state_purge_annotator::add_stmt_annotations (graphviz_out
*gv
,
1159 bool within_row
) const
1167 if (stmt
->code
== GIMPLE_PHI
)
1170 pretty_printer
*pp
= gv
->get_pp ();
1174 const supernode
*supernode
= m_map
->get_sg ().get_supernode_for_stmt (stmt
);
1175 unsigned int stmt_idx
= supernode
->get_stmt_index (stmt
);
1176 function_point before_stmt
1177 (function_point::before_stmt (supernode
, stmt_idx
));
1179 print_needed (gv
, before_stmt
, true);
1182 /* Get the decls and SSA names needed and not-needed at POINT, and
1184 If WITHIN_TABLE is true, print them within <TR> elements. */
1187 state_purge_annotator::print_needed (graphviz_out
*gv
,
1188 const function_point
&point
,
1189 bool within_table
) const
1191 auto_vec
<tree
> needed
;
1192 auto_vec
<tree
> not_needed
;
1193 for (state_purge_map::ssa_iterator iter
= m_map
->begin_ssas ();
1194 iter
!= m_map
->end_ssas ();
1197 tree name
= (*iter
).first
;
1198 state_purge_per_ssa_name
*per_name_data
= (*iter
).second
;
1199 if (&per_name_data
->get_function () == point
.get_function ())
1201 if (per_name_data
->needed_at_point_p (point
))
1202 needed
.safe_push (name
);
1204 not_needed
.safe_push (name
);
1207 for (state_purge_map::decl_iterator iter
= m_map
->begin_decls ();
1208 iter
!= m_map
->end_decls ();
1211 tree decl
= (*iter
).first
;
1212 state_purge_per_decl
*per_decl_data
= (*iter
).second
;
1213 if (&per_decl_data
->get_function () == point
.get_function ())
1215 if (per_decl_data
->needed_at_point_p (point
))
1216 needed
.safe_push (decl
);
1218 not_needed
.safe_push (decl
);
1222 print_vec_of_names (gv
, "needed here", needed
, within_table
);
1223 print_vec_of_names (gv
, "not needed here", not_needed
, within_table
);
1226 #endif /* #if ENABLE_ANALYZER */