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"
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"
55 /* state_purge_map's ctor. Walk all SSA names in all functions, building
56 a state_purge_per_ssa_name instance for each. */
58 state_purge_map::state_purge_map (const supergraph
&sg
,
60 : log_user (logger
), m_sg (sg
)
64 auto_timevar
tv (TV_ANALYZER_STATE_PURGE
);
67 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node
)
69 function
*fun
= node
->get_fun ();
71 log ("function: %s", function_name (fun
));
74 FOR_EACH_SSA_NAME (i
, name
, fun
)
76 /* For now, don't bother tracking the .MEM SSA names. */
77 if (tree var
= SSA_NAME_VAR (name
))
78 if (TREE_CODE (var
) == VAR_DECL
)
79 if (VAR_DECL_IS_VIRTUAL_OPERAND (var
))
81 m_map
.put (name
, new state_purge_per_ssa_name (*this, name
, fun
));
86 /* state_purge_map's dtor. */
88 state_purge_map::~state_purge_map ()
90 for (iterator iter
= m_map
.begin (); iter
!= m_map
.end (); ++iter
)
91 delete (*iter
).second
;
94 /* state_purge_per_ssa_name's ctor.
96 Locate all uses of VAR within FUN.
97 Walk backwards from each use, marking program points, until
98 we reach the def stmt, populating m_points_needing_var.
100 We have to track program points rather than
101 just stmts since there could be empty basic blocks on the way. */
103 state_purge_per_ssa_name::state_purge_per_ssa_name (const state_purge_map
&map
,
106 : m_points_needing_name (), m_name (name
), m_fun (fun
)
108 LOG_FUNC (map
.get_logger ());
110 if (map
.get_logger ())
112 map
.log ("SSA name: %qE within %qD", name
, fun
->decl
);
115 const gimple
*def_stmt
= SSA_NAME_DEF_STMT (name
);
117 pp_gimple_stmt_1 (&pp
, def_stmt
, 0, (dump_flags_t
)0);
118 map
.log ("def stmt: %s", pp_formatted_text (&pp
));
121 auto_vec
<function_point
> worklist
;
123 /* Add all immediate uses of name to the worklist.
124 Compare with debug_immediate_uses. */
125 imm_use_iterator iter
;
127 FOR_EACH_IMM_USE_FAST (use_p
, iter
, name
)
129 if (USE_STMT (use_p
))
131 const gimple
*use_stmt
= USE_STMT (use_p
);
132 if (map
.get_logger ())
135 pp_gimple_stmt_1 (&pp
, use_stmt
, 0, (dump_flags_t
)0);
136 map
.log ("used by stmt: %s", pp_formatted_text (&pp
));
139 const supernode
*snode
140 = map
.get_sg ().get_supernode_for_stmt (use_stmt
);
142 /* If it's a use within a phi node, then we care about
143 which in-edge we came from. */
144 if (use_stmt
->code
== GIMPLE_PHI
)
146 for (gphi_iterator gpi
147 = const_cast<supernode
*> (snode
)->start_phis ();
148 !gsi_end_p (gpi
); gsi_next (&gpi
))
150 gphi
*phi
= gpi
.phi ();
153 /* Find arguments (and thus in-edges) which use NAME. */
154 for (unsigned arg_idx
= 0;
155 arg_idx
< gimple_phi_num_args (phi
);
158 if (name
== gimple_phi_arg (phi
, arg_idx
)->def
)
160 edge in_edge
= gimple_phi_arg_edge (phi
, arg_idx
);
161 const superedge
*in_sedge
162 = map
.get_sg ().get_edge_for_cfg_edge (in_edge
);
164 = function_point::before_supernode
166 add_to_worklist (point
, &worklist
,
168 m_points_needing_name
.add (point
);
176 function_point point
= before_use_stmt (map
, use_stmt
);
177 add_to_worklist (point
, &worklist
, map
.get_logger ());
178 m_points_needing_name
.add (point
);
180 /* We also need to add uses for conditionals and switches,
181 where the stmt "happens" at the after_supernode, for filtering
183 if (use_stmt
== snode
->get_last_stmt ())
185 if (map
.get_logger ())
186 map
.log ("last stmt in BB");
188 = function_point::after_supernode (snode
);
189 add_to_worklist (point
, &worklist
, map
.get_logger ());
190 m_points_needing_name
.add (point
);
193 if (map
.get_logger ())
194 map
.log ("not last stmt in BB");
199 /* Process worklist by walking backwards until we reach the def stmt. */
201 log_scope
s (map
.get_logger (), "processing worklist");
202 while (worklist
.length () > 0)
204 function_point point
= worklist
.pop ();
205 process_point (point
, &worklist
, map
);
209 if (map
.get_logger ())
211 map
.log ("%qE in %qD is needed to process:", name
, fun
->decl
);
212 /* Log m_points_needing_name, sorting it to avoid churn when comparing
214 auto_vec
<function_point
> points
;
215 for (point_set_t::iterator iter
= m_points_needing_name
.begin ();
216 iter
!= m_points_needing_name
.end ();
218 points
.safe_push (*iter
);
219 points
.qsort (function_point::cmp_ptr
);
221 function_point
*point
;
222 FOR_EACH_VEC_ELT (points
, i
, point
)
224 map
.start_log_line ();
225 map
.get_logger ()->log_partial (" point: ");
226 point
->print (map
.get_logger ()->get_printer (), format (false));
232 /* Return true if the SSA name is needed at POINT. */
235 state_purge_per_ssa_name::needed_at_point_p (const function_point
&point
) const
237 return const_cast <point_set_t
&> (m_points_needing_name
).contains (point
);
240 /* Get the function_point representing immediately before USE_STMT.
241 Subroutine of ctor. */
244 state_purge_per_ssa_name::before_use_stmt (const state_purge_map
&map
,
245 const gimple
*use_stmt
)
247 gcc_assert (use_stmt
->code
!= GIMPLE_PHI
);
249 const supernode
*supernode
250 = map
.get_sg ().get_supernode_for_stmt (use_stmt
);
251 unsigned int stmt_idx
= supernode
->get_stmt_index (use_stmt
);
252 return function_point::before_stmt (supernode
, stmt_idx
);
255 /* Add POINT to *WORKLIST if the point has not already been seen.
256 Subroutine of ctor. */
259 state_purge_per_ssa_name::add_to_worklist (const function_point
&point
,
260 auto_vec
<function_point
> *worklist
,
266 logger
->start_log_line ();
267 logger
->log_partial ("point: '");
268 point
.print (logger
->get_printer (), format (false));
269 logger
->log_partial ("' for worklist for %qE", m_name
);
270 logger
->end_log_line ();
273 gcc_assert (point
.get_function () == m_fun
);
274 if (point
.get_from_edge ())
275 gcc_assert (point
.get_from_edge ()->get_kind () == SUPEREDGE_CFG_EDGE
);
277 if (m_points_needing_name
.contains (point
))
280 logger
->log ("already seen for %qE", m_name
);
285 logger
->log ("not seen; adding to worklist for %qE", m_name
);
286 m_points_needing_name
.add (point
);
287 worklist
->safe_push (point
);
291 /* Process POINT, popped from WORKLIST.
292 Iterate over predecessors of POINT, adding to WORKLIST. */
295 state_purge_per_ssa_name::process_point (const function_point
&point
,
296 auto_vec
<function_point
> *worklist
,
297 const state_purge_map
&map
)
299 logger
*logger
= map
.get_logger ();
303 logger
->start_log_line ();
304 logger
->log_partial ("considering point: '");
305 point
.print (logger
->get_printer (), format (false));
306 logger
->log_partial ("' for %qE", m_name
);
307 logger
->end_log_line ();
310 gimple
*def_stmt
= SSA_NAME_DEF_STMT (m_name
);
312 const supernode
*snode
= point
.get_supernode ();
314 switch (point
.get_kind ())
322 case PK_BEFORE_SUPERNODE
:
324 for (gphi_iterator gpi
325 = const_cast<supernode
*> (snode
)->start_phis ();
326 !gsi_end_p (gpi
); gsi_next (&gpi
))
328 gphi
*phi
= gpi
.phi ();
332 logger
->log ("def stmt within phis; terminating");
337 /* Add given pred to worklist. */
338 if (point
.get_from_edge ())
340 gcc_assert (point
.get_from_edge ()->m_src
);
342 (function_point::after_supernode (point
.get_from_edge ()->m_src
),
347 /* Add any intraprocedually edge for a call. */
348 if (snode
->m_returning_call
)
351 = supergraph_call_edge (snode
->m_fun
,
352 snode
->m_returning_call
);
355 = map
.get_sg ().get_intraprocedural_edge_for_call (cedge
);
358 (function_point::after_supernode (sedge
->m_src
),
367 if (def_stmt
== point
.get_stmt ())
370 logger
->log ("def stmt; terminating");
373 if (point
.get_stmt_idx () > 0)
374 add_to_worklist (function_point::before_stmt
375 (snode
, point
.get_stmt_idx () - 1),
379 /* Add before_supernode to worklist. This captures the in-edge,
380 so we have to do it once per in-edge. */
383 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
384 add_to_worklist (function_point::before_supernode (snode
,
391 case PK_AFTER_SUPERNODE
:
393 if (snode
->m_stmts
.length ())
395 (function_point::before_stmt (snode
,
396 snode
->m_stmts
.length () - 1),
400 /* Add before_supernode to worklist. This captures the in-edge,
401 so we have to do it once per in-edge. */
404 FOR_EACH_VEC_ELT (snode
->m_preds
, i
, pred
)
405 add_to_worklist (function_point::before_supernode (snode
,
408 /* If it's the initial BB, add it, to ensure that we
409 have "before supernode" for the initial ENTRY block, and don't
410 erroneously purge SSA names for initial values of parameters. */
411 if (snode
->entry_p ())
414 (function_point::before_supernode (snode
, NULL
),
423 /* class state_purge_annotator : public dot_annotator. */
425 /* Implementation of dot_annotator::add_node_annotations vfunc for
426 state_purge_annotator.
428 Add an additional record showing which names are purged on entry
429 to the supernode N. */
432 state_purge_annotator::add_node_annotations (graphviz_out
*gv
,
434 bool within_table
) const
442 pretty_printer
*pp
= gv
->get_pp ();
444 pp_printf (pp
, "annotation_for_node_%i", n
.m_index
);
445 pp_printf (pp
, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
447 pp_write_text_to_stream (pp
);
449 // FIXME: passing in a NULL in-edge means we get no hits
450 function_point before_supernode
451 (function_point::before_supernode (&n
, NULL
));
453 for (state_purge_map::iterator iter
= m_map
->begin ();
454 iter
!= m_map
->end ();
457 tree name
= (*iter
).first
;
458 state_purge_per_ssa_name
*per_name_data
= (*iter
).second
;
459 if (per_name_data
->get_function () == n
.m_fun
)
461 if (per_name_data
->needed_at_point_p (before_supernode
))
462 pp_printf (pp
, "%qE needed here", name
);
464 pp_printf (pp
, "%qE not needed here", name
);
469 pp_string (pp
, "\"];\n\n");
474 /* Print V to GV as a comma-separated list in braces within a <TR>,
475 titling it with TITLE.
477 Subroutine of state_purge_annotator::add_stmt_annotations. */
480 print_vec_of_names (graphviz_out
*gv
, const char *title
,
481 const auto_vec
<tree
> &v
)
483 pretty_printer
*pp
= gv
->get_pp ();
487 pp_printf (pp
, "%s: {", title
);
488 FOR_EACH_VEC_ELT (v
, i
, name
)
491 pp_string (pp
, ", ");
492 pp_printf (pp
, "%qE", name
);
495 pp_write_text_as_html_like_dot_to_stream (pp
);
500 /* Implementation of dot_annotator::add_stmt_annotations for
501 state_purge_annotator.
503 Add text showing which names are purged at STMT. */
506 state_purge_annotator::add_stmt_annotations (graphviz_out
*gv
,
508 bool within_row
) const
516 if (stmt
->code
== GIMPLE_PHI
)
519 pretty_printer
*pp
= gv
->get_pp ();
523 const supernode
*supernode
= m_map
->get_sg ().get_supernode_for_stmt (stmt
);
524 unsigned int stmt_idx
= supernode
->get_stmt_index (stmt
);
525 function_point before_stmt
526 (function_point::before_stmt (supernode
, stmt_idx
));
528 auto_vec
<tree
> needed
;
529 auto_vec
<tree
> not_needed
;
530 for (state_purge_map::iterator iter
= m_map
->begin ();
531 iter
!= m_map
->end ();
534 tree name
= (*iter
).first
;
535 state_purge_per_ssa_name
*per_name_data
= (*iter
).second
;
536 if (per_name_data
->get_function () == supernode
->m_fun
)
538 if (per_name_data
->needed_at_point_p (before_stmt
))
539 needed
.safe_push (name
);
541 not_needed
.safe_push (name
);
545 print_vec_of_names (gv
, "needed here", needed
);
546 print_vec_of_names (gv
, "not needed here", not_needed
);
549 #endif /* #if ENABLE_ANALYZER */