1 /* Classes for purging state at function_points.
2 Copyright (C) 2019-2020 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"
40 #include "analyzer/analyzer.h"
41 #include "analyzer/call-string.h"
43 #include "ordered-hash-map.h"
45 #include "gimple-iterator.h"
47 #include "analyzer/supergraph.h"
48 #include "analyzer/program-point.h"
49 #include "analyzer/analyzer-logging.h"
50 #include "analyzer/state-purge.h"
54 /* state_purge_map's ctor. Walk all SSA names in all functions, building
55 a state_purge_per_ssa_name instance for each. */
57 state_purge_map::state_purge_map (const supergraph
&sg
,
59 : log_user (logger
), m_sg (sg
)
63 auto_timevar
tv (TV_ANALYZER_STATE_PURGE
);
66 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node
)
68 function
*fun
= node
->get_fun ();
70 log ("function: %s", function_name (fun
));
73 FOR_EACH_SSA_NAME (i
, name
, fun
)
75 /* For now, don't bother tracking the .MEM SSA names. */
76 if (tree var
= SSA_NAME_VAR (name
))
77 if (TREE_CODE (var
) == VAR_DECL
)
78 if (VAR_DECL_IS_VIRTUAL_OPERAND (var
))
80 m_map
.put (name
, new state_purge_per_ssa_name (*this, name
, fun
));
85 /* state_purge_map's dtor. */
87 state_purge_map::~state_purge_map ()
89 for (iterator iter
= m_map
.begin (); iter
!= m_map
.end (); ++iter
)
90 delete (*iter
).second
;
93 /* state_purge_per_ssa_name's ctor.
95 Locate all uses of VAR within FUN.
96 Walk backwards from each use, marking program points, until
97 we reach the def stmt, populating m_points_needing_var.
99 We have to track program points rather than
100 just stmts since there could be empty basic blocks on the way. */
102 state_purge_per_ssa_name::state_purge_per_ssa_name (const state_purge_map
&map
,
105 : m_points_needing_name (), m_name (name
), m_fun (fun
)
107 LOG_FUNC (map
.get_logger ());
109 if (map
.get_logger ())
111 map
.log ("SSA name: %qE within %qD", name
, fun
->decl
);
114 const gimple
*def_stmt
= SSA_NAME_DEF_STMT (name
);
116 pp_gimple_stmt_1 (&pp
, def_stmt
, 0, (dump_flags_t
)0);
117 map
.log ("def stmt: %s", pp_formatted_text (&pp
));
120 auto_vec
<function_point
> worklist
;
122 /* Add all immediate uses of name to the worklist.
123 Compare with debug_immediate_uses. */
124 imm_use_iterator iter
;
126 FOR_EACH_IMM_USE_FAST (use_p
, iter
, name
)
128 if (USE_STMT (use_p
))
130 const gimple
*use_stmt
= USE_STMT (use_p
);
131 if (map
.get_logger ())
134 pp_gimple_stmt_1 (&pp
, use_stmt
, 0, (dump_flags_t
)0);
135 map
.log ("used by stmt: %s", pp_formatted_text (&pp
));
138 const supernode
*snode
139 = map
.get_sg ().get_supernode_for_stmt (use_stmt
);
141 /* If it's a use within a phi node, then we care about
142 which in-edge we came from. */
143 if (use_stmt
->code
== GIMPLE_PHI
)
145 for (gphi_iterator gpi
146 = const_cast<supernode
*> (snode
)->start_phis ();
147 !gsi_end_p (gpi
); gsi_next (&gpi
))
149 gphi
*phi
= gpi
.phi ();
152 /* Find arguments (and thus in-edges) which use NAME. */
153 for (unsigned arg_idx
= 0;
154 arg_idx
< gimple_phi_num_args (phi
);
157 if (name
== gimple_phi_arg (phi
, arg_idx
)->def
)
159 edge in_edge
= gimple_phi_arg_edge (phi
, arg_idx
);
160 const superedge
*in_sedge
161 = map
.get_sg ().get_edge_for_cfg_edge (in_edge
);
163 = function_point::before_supernode
165 add_to_worklist (point
, &worklist
,
167 m_points_needing_name
.add (point
);
175 function_point point
= before_use_stmt (map
, use_stmt
);
176 add_to_worklist (point
, &worklist
, map
.get_logger ());
177 m_points_needing_name
.add (point
);
179 /* We also need to add uses for conditionals and switches,
180 where the stmt "happens" at the after_supernode, for filtering
182 if (use_stmt
== snode
->get_last_stmt ())
184 if (map
.get_logger ())
185 map
.log ("last stmt in BB");
187 = function_point::after_supernode (snode
);
188 add_to_worklist (point
, &worklist
, map
.get_logger ());
189 m_points_needing_name
.add (point
);
192 if (map
.get_logger ())
193 map
.log ("not last stmt in BB");
198 /* Process worklist by walking backwards until we reach the def stmt. */
200 log_scope
s (map
.get_logger (), "processing worklist");
201 while (worklist
.length () > 0)
203 function_point point
= worklist
.pop ();
204 process_point (point
, &worklist
, map
);
208 if (map
.get_logger ())
210 map
.log ("%qE in %qD is needed to process:", name
, fun
->decl
);
211 for (point_set_t::iterator iter
= m_points_needing_name
.begin ();
212 iter
!= m_points_needing_name
.end ();
215 map
.start_log_line ();
216 map
.get_logger ()->log_partial (" point: ");
217 (*iter
).print (map
.get_logger ()->get_printer (), format (false));
223 /* Return true if the SSA name is needed at POINT. */
226 state_purge_per_ssa_name::needed_at_point_p (const function_point
&point
) const
228 return const_cast <point_set_t
&> (m_points_needing_name
).contains (point
);
231 /* Get the function_point representing immediately before USE_STMT.
232 Subroutine of ctor. */
235 state_purge_per_ssa_name::before_use_stmt (const state_purge_map
&map
,
236 const gimple
*use_stmt
)
238 gcc_assert (use_stmt
->code
!= GIMPLE_PHI
);
240 const supernode
*supernode
241 = map
.get_sg ().get_supernode_for_stmt (use_stmt
);
242 unsigned int stmt_idx
= supernode
->get_stmt_index (use_stmt
);
243 return function_point::before_stmt (supernode
, stmt_idx
);
246 /* Add POINT to *WORKLIST if the point has not already been seen.
247 Subroutine of ctor. */
250 state_purge_per_ssa_name::add_to_worklist (const function_point
&point
,
251 auto_vec
<function_point
> *worklist
,
257 logger
->start_log_line ();
258 logger
->log_partial ("point: '");
259 point
.print (logger
->get_printer (), format (false));
260 logger
->log_partial ("' for worklist for %qE", m_name
);
261 logger
->end_log_line ();
264 gcc_assert (point
.get_function () == m_fun
);
265 if (point
.get_from_edge ())
266 gcc_assert (point
.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE
);
268 if (m_points_needing_name
.contains (point
))
271 logger
->log ("already seen for %qE", m_name
);
276 logger
->log ("not seen; adding to worklist for %qE", m_name
);
277 m_points_needing_name
.add (point
);
278 worklist
->safe_push (point
);
282 /* Process POINT, popped from WORKLIST.
283 Iterate over predecessors of POINT, adding to WORKLIST. */
286 state_purge_per_ssa_name::process_point (const function_point
&point
,
287 auto_vec
<function_point
> *worklist
,
288 const state_purge_map
&map
)
290 logger
*logger
= map
.get_logger ();
294 logger
->start_log_line ();
295 logger
->log_partial ("considering point: '");
296 point
.print (logger
->get_printer (), format (false));
297 logger
->log_partial ("' for %qE", m_name
);
298 logger
->end_log_line ();
301 gimple
*def_stmt
= SSA_NAME_DEF_STMT (m_name
);
303 const supernode
*snode
= point
.get_supernode ();
305 switch (point
.get_kind ())
313 case PK_BEFORE_SUPERNODE
:
315 for (gphi_iterator gpi
316 = const_cast<supernode
*> (snode
)->start_phis ();
317 !gsi_end_p (gpi
); gsi_next (&gpi
))
319 gphi
*phi
= gpi
.phi ();
323 logger
->log ("def stmt within phis; terminating");
328 /* Add given pred to worklist. */
329 if (point
.get_from_edge ())
331 gcc_assert (point
.get_from_edge ()->m_src
);
333 (function_point::after_supernode (point
.get_from_edge ()->m_src
),
338 /* Add any intraprocedually edge for a call. */
339 if (snode
->m_returning_call
)
342 = supergraph_call_edge (snode
->m_fun
,
343 snode
->m_returning_call
);
346 = map
.get_sg ().get_intraprocedural_edge_for_call (cedge
);
349 (function_point::after_supernode (sedge
->m_src
),
358 if (def_stmt
== point
.get_stmt ())
361 logger
->log ("def stmt; terminating");
364 if (point
.get_stmt_idx () > 0)
365 add_to_worklist (function_point::before_stmt
366 (snode
, point
.get_stmt_idx () - 1),
370 /* Add before_supernode to worklist. This captures the in-edge,
371 so we have to do it once per in-edge. */
374 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
375 add_to_worklist (function_point::before_supernode (snode
,
382 case PK_AFTER_SUPERNODE
:
384 if (snode
->m_stmts
.length ())
386 (function_point::before_stmt (snode
,
387 snode
->m_stmts
.length () - 1),
391 /* Add before_supernode to worklist. This captures the in-edge,
392 so we have to do it once per in-edge. */
395 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
396 add_to_worklist (function_point::before_supernode (snode
,
399 /* If it's the initial BB, add it, to ensure that we
400 have "before supernode" for the initial ENTRY block, and don't
401 erroneously purge SSA names for initial values of parameters. */
402 if (snode
->entry_p ())
405 (function_point::before_supernode (snode
, NULL
),
414 /* class state_purge_annotator : public dot_annotator. */
416 /* Implementation of dot_annotator::add_node_annotations vfunc for
417 state_purge_annotator.
419 Add an additional record showing which names are purged on entry
420 to the supernode N. */
423 state_purge_annotator::add_node_annotations (graphviz_out
*gv
,
424 const supernode
&n
) const
429 pretty_printer
*pp
= gv
->get_pp ();
431 pp_printf (pp
, "annotation_for_node_%i", n
.m_index
);
432 pp_printf (pp
, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
434 pp_write_text_to_stream (pp
);
436 // FIXME: passing in a NULL in-edge means we get no hits
437 function_point before_supernode
438 (function_point::before_supernode (&n
, NULL
));
440 for (state_purge_map::iterator iter
= m_map
->begin ();
441 iter
!= m_map
->end ();
444 tree name
= (*iter
).first
;
445 state_purge_per_ssa_name
*per_name_data
= (*iter
).second
;
446 if (per_name_data
->get_function () == n
.m_fun
)
448 if (per_name_data
->needed_at_point_p (before_supernode
))
449 pp_printf (pp
, "%qE needed here", name
);
451 pp_printf (pp
, "%qE not needed here", name
);
456 pp_string (pp
, "\"];\n\n");
460 /* Print V to GV as a comma-separated list in braces within a <TR>,
461 titling it with TITLE.
463 Subroutine of state_purge_annotator::add_stmt_annotations. */
466 print_vec_of_names (graphviz_out
*gv
, const char *title
,
467 const auto_vec
<tree
> &v
)
469 pretty_printer
*pp
= gv
->get_pp ();
473 pp_printf (pp
, "%s: {", title
);
474 FOR_EACH_VEC_ELT (v
, i
, name
)
477 pp_string (pp
, ", ");
478 pp_printf (pp
, "%qE", name
);
481 pp_write_text_as_html_like_dot_to_stream (pp
);
486 /* Implementation of dot_annotator::add_stmt_annotations for
487 state_purge_annotator.
489 Add text showing which names are purged at STMT. */
492 state_purge_annotator::add_stmt_annotations (graphviz_out
*gv
,
493 const gimple
*stmt
) const
498 if (stmt
->code
== GIMPLE_PHI
)
501 pretty_printer
*pp
= gv
->get_pp ();
505 const supernode
*supernode
= m_map
->get_sg ().get_supernode_for_stmt (stmt
);
506 unsigned int stmt_idx
= supernode
->get_stmt_index (stmt
);
507 function_point before_stmt
508 (function_point::before_stmt (supernode
, stmt_idx
));
510 auto_vec
<tree
> needed
;
511 auto_vec
<tree
> not_needed
;
512 for (state_purge_map::iterator iter
= m_map
->begin ();
513 iter
!= m_map
->end ();
516 tree name
= (*iter
).first
;
517 state_purge_per_ssa_name
*per_name_data
= (*iter
).second
;
518 if (per_name_data
->get_function () == supernode
->m_fun
)
520 if (per_name_data
->needed_at_point_p (before_stmt
))
521 needed
.safe_push (name
);
523 not_needed
.safe_push (name
);
527 print_vec_of_names (gv
, "needed here", needed
);
528 print_vec_of_names (gv
, "not needed here", not_needed
);
531 #endif /* #if ENABLE_ANALYZER */