testsuite: Correct vec-rlmi-rlnm.c testsuite expected result
[official-gcc.git] / gcc / analyzer / engine.cc
blobd4c654a34978ce93d952570315412b3522855986
1 /* The analysis "engine".
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)
10 any later version.
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/>. */
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "tree.h"
25 #include "fold-const.h"
26 #include "gcc-rich-location.h"
27 #include "alloc-pool.h"
28 #include "fibonacci_heap.h"
29 #include "shortest-paths.h"
30 #include "diagnostic-core.h"
31 #include "diagnostic-event-id.h"
32 #include "diagnostic-path.h"
33 #include "function.h"
34 #include "pretty-print.h"
35 #include "sbitmap.h"
36 #include "bitmap.h"
37 #include "tristate.h"
38 #include "ordered-hash-map.h"
39 #include "selftest.h"
40 #include "json.h"
41 #include "analyzer/analyzer.h"
42 #include "analyzer/analyzer-logging.h"
43 #include "analyzer/call-string.h"
44 #include "analyzer/program-point.h"
45 #include "analyzer/store.h"
46 #include "analyzer/region-model.h"
47 #include "analyzer/constraint-manager.h"
48 #include "analyzer/sm.h"
49 #include "analyzer/pending-diagnostic.h"
50 #include "analyzer/diagnostic-manager.h"
51 #include "cfg.h"
52 #include "basic-block.h"
53 #include "gimple.h"
54 #include "gimple-iterator.h"
55 #include "gimple-pretty-print.h"
56 #include "cgraph.h"
57 #include "digraph.h"
58 #include "analyzer/supergraph.h"
59 #include "analyzer/program-state.h"
60 #include "analyzer/exploded-graph.h"
61 #include "analyzer/analysis-plan.h"
62 #include "analyzer/checker-path.h"
63 #include "analyzer/state-purge.h"
64 #include "analyzer/bar-chart.h"
65 #include <zlib.h>
67 /* For an overview, see gcc/doc/analyzer.texi. */
69 #if ENABLE_ANALYZER
71 namespace ana {
73 /* class impl_region_model_context : public region_model_context. */
75 impl_region_model_context::
76 impl_region_model_context (exploded_graph &eg,
77 const exploded_node *enode_for_diag,
78 const program_state *old_state,
79 program_state *new_state,
80 const gimple *stmt,
81 stmt_finder *stmt_finder)
82 : m_eg (&eg), m_logger (eg.get_logger ()),
83 m_enode_for_diag (enode_for_diag),
84 m_old_state (old_state),
85 m_new_state (new_state),
86 m_stmt (stmt),
87 m_stmt_finder (stmt_finder),
88 m_ext_state (eg.get_ext_state ())
92 impl_region_model_context::
93 impl_region_model_context (program_state *state,
94 const extrinsic_state &ext_state,
95 logger *logger)
96 : m_eg (NULL), m_logger (logger), m_enode_for_diag (NULL),
97 m_old_state (NULL),
98 m_new_state (state),
99 m_stmt (NULL),
100 m_stmt_finder (NULL),
101 m_ext_state (ext_state)
105 void
106 impl_region_model_context::warn (pending_diagnostic *d)
108 LOG_FUNC (get_logger ());
109 if (m_eg)
110 m_eg->get_diagnostic_manager ().add_diagnostic
111 (m_enode_for_diag, m_enode_for_diag->get_supernode (),
112 m_stmt, m_stmt_finder, d);
115 void
116 impl_region_model_context::on_svalue_leak (const svalue *sval)
119 int sm_idx;
120 sm_state_map *smap;
121 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
122 smap->on_svalue_leak (sval, this);
125 void
126 impl_region_model_context::
127 on_liveness_change (const svalue_set &live_svalues,
128 const region_model *model)
130 int sm_idx;
131 sm_state_map *smap;
132 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
133 smap->on_liveness_change (live_svalues, model, this);
136 void
137 impl_region_model_context::on_unknown_change (const svalue *sval,
138 bool is_mutable)
140 int sm_idx;
141 sm_state_map *smap;
142 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
143 smap->on_unknown_change (sval, is_mutable, m_ext_state);
146 void
147 impl_region_model_context::on_escaped_function (tree fndecl)
149 m_eg->on_escaped_function (fndecl);
152 /* class setjmp_svalue : public svalue. */
154 /* Implementation of svalue::accept vfunc for setjmp_svalue. */
156 void
157 setjmp_svalue::accept (visitor *v) const
159 v->visit_setjmp_svalue (this);
162 /* Implementation of svalue::dump_to_pp vfunc for setjmp_svalue. */
164 void
165 setjmp_svalue::dump_to_pp (pretty_printer *pp, bool simple) const
167 if (simple)
168 pp_printf (pp, "SETJMP(EN: %i)", get_enode_index ());
169 else
170 pp_printf (pp, "setjmp_svalue(EN%i)", get_enode_index ());
173 /* Get the index of the stored exploded_node. */
176 setjmp_svalue::get_enode_index () const
178 return m_setjmp_record.m_enode->m_index;
181 /* Concrete implementation of sm_context, wiring it up to the rest of this
182 file. */
184 class impl_sm_context : public sm_context
186 public:
187 impl_sm_context (exploded_graph &eg,
188 int sm_idx,
189 const state_machine &sm,
190 const exploded_node *enode_for_diag,
191 const program_state *old_state,
192 program_state *new_state,
193 const sm_state_map *old_smap,
194 sm_state_map *new_smap,
195 stmt_finder *stmt_finder = NULL)
196 : sm_context (sm_idx, sm),
197 m_logger (eg.get_logger ()),
198 m_eg (eg), m_enode_for_diag (enode_for_diag),
199 m_old_state (old_state), m_new_state (new_state),
200 m_old_smap (old_smap), m_new_smap (new_smap),
201 m_stmt_finder (stmt_finder)
205 logger *get_logger () const { return m_logger.get_logger (); }
207 tree get_fndecl_for_call (const gcall *call) FINAL OVERRIDE
209 impl_region_model_context old_ctxt
210 (m_eg, m_enode_for_diag, NULL, NULL/*m_enode->get_state ()*/,
211 call);
212 region_model *model = m_new_state->m_region_model;
213 return model->get_fndecl_for_call (call, &old_ctxt);
216 state_machine::state_t get_state (const gimple *stmt,
217 tree var)
219 logger * const logger = get_logger ();
220 LOG_FUNC (logger);
221 impl_region_model_context old_ctxt
222 (m_eg, m_enode_for_diag, NULL, NULL/*m_enode->get_state ()*/,
223 stmt);
224 const svalue *var_old_sval
225 = m_old_state->m_region_model->get_rvalue (var, &old_ctxt);
227 state_machine::state_t current
228 = m_old_smap->get_state (var_old_sval, m_eg.get_ext_state ());
229 return current;
232 void set_next_state (const gimple *stmt,
233 tree var,
234 state_machine::state_t to,
235 tree origin)
237 logger * const logger = get_logger ();
238 LOG_FUNC (logger);
239 impl_region_model_context old_ctxt
240 (m_eg, m_enode_for_diag, NULL, NULL/*m_enode->get_state ()*/,
241 stmt);
242 const svalue *var_old_sval
243 = m_old_state->m_region_model->get_rvalue (var, &old_ctxt);
245 impl_region_model_context new_ctxt (m_eg, m_enode_for_diag,
246 m_old_state, m_new_state,
247 stmt);
248 const svalue *var_new_sval
249 = m_new_state->m_region_model->get_rvalue (var, &new_ctxt);
250 const svalue *origin_new_sval
251 = m_new_state->m_region_model->get_rvalue (origin, &new_ctxt);
253 state_machine::state_t current
254 = m_old_smap->get_state (var_old_sval, m_eg.get_ext_state ());
255 if (logger)
256 logger->log ("%s: state transition of %qE: %s -> %s",
257 m_sm.get_name (),
258 var,
259 current->get_name (),
260 to->get_name ());
261 m_new_smap->set_state (m_new_state->m_region_model, var_new_sval,
262 to, origin_new_sval, m_eg.get_ext_state ());
265 void warn (const supernode *snode, const gimple *stmt,
266 tree var, pending_diagnostic *d) FINAL OVERRIDE
268 LOG_FUNC (get_logger ());
269 gcc_assert (d); // take ownership
270 impl_region_model_context old_ctxt
271 (m_eg, m_enode_for_diag, m_old_state, m_new_state, NULL);
273 const svalue *var_old_sval
274 = m_old_state->m_region_model->get_rvalue (var, &old_ctxt);
275 state_machine::state_t current
276 = (var
277 ? m_old_smap->get_state (var_old_sval, m_eg.get_ext_state ())
278 : m_old_smap->get_global_state ());
279 m_eg.get_diagnostic_manager ().add_diagnostic
280 (&m_sm, m_enode_for_diag, snode, stmt, m_stmt_finder,
281 var, var_old_sval, current, d);
284 /* Hook for picking more readable trees for SSA names of temporaries,
285 so that rather than e.g.
286 "double-free of '<unknown>'"
287 we can print:
288 "double-free of 'inbuf.data'". */
290 tree get_diagnostic_tree (tree expr) FINAL OVERRIDE
292 /* Only for SSA_NAMEs of temporaries; otherwise, return EXPR, as it's
293 likely to be the least surprising tree to report. */
294 if (TREE_CODE (expr) != SSA_NAME)
295 return expr;
296 if (SSA_NAME_VAR (expr) != NULL)
297 return expr;
299 gcc_assert (m_new_state);
300 const svalue *sval = m_new_state->m_region_model->get_rvalue (expr, NULL);
301 /* Find trees for all regions storing the value. */
302 if (tree t = m_new_state->m_region_model->get_representative_tree (sval))
303 return t;
304 else
305 return expr;
308 state_machine::state_t get_global_state () const FINAL OVERRIDE
310 return m_old_state->m_checker_states[m_sm_idx]->get_global_state ();
313 void set_global_state (state_machine::state_t state) FINAL OVERRIDE
315 m_new_state->m_checker_states[m_sm_idx]->set_global_state (state);
318 void on_custom_transition (custom_transition *transition) FINAL OVERRIDE
320 transition->impl_transition (&m_eg,
321 const_cast<exploded_node *> (m_enode_for_diag),
322 m_sm_idx);
325 tree is_zero_assignment (const gimple *stmt) FINAL OVERRIDE
327 const gassign *assign_stmt = dyn_cast <const gassign *> (stmt);
328 if (!assign_stmt)
329 return NULL_TREE;
330 impl_region_model_context old_ctxt
331 (m_eg, m_enode_for_diag, m_old_state, m_new_state, stmt);
332 if (const svalue *sval
333 = m_new_state->m_region_model->get_gassign_result (assign_stmt,
334 &old_ctxt))
335 if (tree cst = sval->maybe_get_constant ())
336 if (::zerop(cst))
337 return gimple_assign_lhs (assign_stmt);
338 return NULL_TREE;
341 log_user m_logger;
342 exploded_graph &m_eg;
343 const exploded_node *m_enode_for_diag;
344 const program_state *m_old_state;
345 program_state *m_new_state;
346 const sm_state_map *m_old_smap;
347 sm_state_map *m_new_smap;
348 stmt_finder *m_stmt_finder;
351 /* Subclass of stmt_finder for finding the best stmt to report the leak at,
352 given the emission path. */
354 class leak_stmt_finder : public stmt_finder
356 public:
357 leak_stmt_finder (const exploded_graph &eg, tree var)
358 : m_eg (eg), m_var (var) {}
360 stmt_finder *clone () const FINAL OVERRIDE
362 return new leak_stmt_finder (m_eg, m_var);
365 const gimple *find_stmt (const exploded_path &epath)
366 FINAL OVERRIDE
368 logger * const logger = m_eg.get_logger ();
369 LOG_FUNC (logger);
371 if (m_var && TREE_CODE (m_var) == SSA_NAME)
373 /* Locate the final write to this SSA name in the path. */
374 const gimple *def_stmt = SSA_NAME_DEF_STMT (m_var);
376 int idx_of_def_stmt;
377 bool found = epath.find_stmt_backwards (def_stmt, &idx_of_def_stmt);
378 if (!found)
379 goto not_found;
381 /* What was the next write to the underlying var
382 after the SSA name was set? (if any). */
384 for (unsigned idx = idx_of_def_stmt + 1;
385 idx < epath.m_edges.length ();
386 ++idx)
388 const exploded_edge *eedge = epath.m_edges[idx];
389 if (logger)
390 logger->log ("eedge[%i]: EN %i -> EN %i",
391 idx,
392 eedge->m_src->m_index,
393 eedge->m_dest->m_index);
394 const exploded_node *dst_node = eedge->m_dest;
395 const program_point &dst_point = dst_node->get_point ();
396 const gimple *stmt = dst_point.get_stmt ();
397 if (!stmt)
398 continue;
399 if (const gassign *assign = dyn_cast <const gassign *> (stmt))
401 tree lhs = gimple_assign_lhs (assign);
402 if (TREE_CODE (lhs) == SSA_NAME
403 && SSA_NAME_VAR (lhs) == SSA_NAME_VAR (m_var))
404 return assign;
409 not_found:
411 /* Look backwards for the first statement with a location. */
412 int i;
413 const exploded_edge *eedge;
414 FOR_EACH_VEC_ELT_REVERSE (epath.m_edges, i, eedge)
416 if (logger)
417 logger->log ("eedge[%i]: EN %i -> EN %i",
419 eedge->m_src->m_index,
420 eedge->m_dest->m_index);
421 const exploded_node *dst_node = eedge->m_dest;
422 const program_point &dst_point = dst_node->get_point ();
423 const gimple *stmt = dst_point.get_stmt ();
424 if (stmt)
425 if (get_pure_location (stmt->location) != UNKNOWN_LOCATION)
426 return stmt;
429 gcc_unreachable ();
430 return NULL;
433 private:
434 const exploded_graph &m_eg;
435 tree m_var;
438 /* A measurement of how good EXPR is for presenting to the user, so
439 that e.g. we can say prefer printing
440 "leak of 'tmp.m_ptr'"
441 over:
442 "leak of '<unknown>'". */
444 static int
445 readability (const_tree expr)
447 gcc_assert (expr);
448 switch (TREE_CODE (expr))
450 case COMPONENT_REF:
451 case MEM_REF:
452 /* Impose a slight readability penalty relative to that of
453 operand 0. */
454 return readability (TREE_OPERAND (expr, 0)) - 16;
456 case SSA_NAME:
458 if (tree var = SSA_NAME_VAR (expr))
459 /* Slightly favor the underlying var over the SSA name to
460 avoid having them compare equal. */
461 return readability (var) - 1;
462 /* Avoid printing '<unknown>' for SSA names for temporaries. */
463 return -1;
465 break;
467 case PARM_DECL:
468 case VAR_DECL:
469 if (DECL_NAME (expr))
470 /* Arbitrarily-chosen "high readability" value. */
471 return 65536;
472 else
473 /* We don't want to print temporaries. For example, the C FE
474 prints them as e.g. "<Uxxxx>" where "xxxx" is the low 16 bits
475 of the tree pointer (see pp_c_tree_decl_identifier). */
476 return -1;
478 case RESULT_DECL:
479 /* Printing "<return-value>" isn't ideal, but is less awful than
480 trying to print a temporary. */
481 return 32768;
483 default:
484 return 0;
487 return 0;
490 /* A qsort comparator for trees to sort them into most user-readable to
491 least user-readable. */
494 readability_comparator (const void *p1, const void *p2)
496 path_var pv1 = *(path_var const *)p1;
497 path_var pv2 = *(path_var const *)p2;
499 int r1 = readability (pv1.m_tree);
500 int r2 = readability (pv2.m_tree);
501 if (int cmp = r2 - r1)
502 return cmp;
504 /* Favor items that are deeper on the stack and hence more recent;
505 this also favors locals over globals. */
506 if (int cmp = pv2.m_stack_depth - pv1.m_stack_depth)
507 return cmp;
509 /* TODO: We ought to find ways of sorting such cases. */
510 return 0;
513 /* Find the best tree for SVAL and call SM's on_leak vfunc with it.
514 If on_leak returns a pending_diagnostic, queue it up to be reported,
515 so that we potentially complain about a leak of SVAL in the given STATE. */
517 void
518 impl_region_model_context::on_state_leak (const state_machine &sm,
519 const svalue *sval,
520 state_machine::state_t state)
522 logger * const logger = get_logger ();
523 LOG_SCOPE (logger);
524 if (logger)
526 logger->start_log_line ();
527 logger->log_partial ("considering leak of ");
528 sval->dump_to_pp (logger->get_printer (), true);
529 logger->end_log_line ();
532 if (!m_eg)
533 return;
535 /* m_old_state also needs to be non-NULL so that the sm_ctxt can look
536 up the old state of SVAL. */
537 gcc_assert (m_old_state);
539 /* SVAL has leaked within the new state: it is not used by any reachable
540 regions.
541 We need to convert it back to a tree, but since it's likely no regions
542 use it, we have to find the "best" tree for it in the old_state. */
543 svalue_set visited;
544 path_var leaked_pv
545 = m_old_state->m_region_model->get_representative_path_var (sval,
546 &visited);
548 /* This might be NULL; the pending_diagnostic subclasses need to cope
549 with this. */
550 tree leaked_tree = leaked_pv.m_tree;
551 if (logger)
553 if (leaked_tree)
554 logger->log ("best leaked_tree: %qE", leaked_tree);
555 else
556 logger->log ("best leaked_tree: NULL");
559 leak_stmt_finder stmt_finder (*m_eg, leaked_tree);
560 gcc_assert (m_enode_for_diag);
562 /* Don't complain about leaks when returning from "main". */
563 if (m_enode_for_diag->get_supernode ()
564 && m_enode_for_diag->get_supernode ()->return_p ())
566 tree fndecl = m_enode_for_diag->get_function ()->decl;
567 if (id_equal (DECL_NAME (fndecl), "main"))
569 if (logger)
570 logger->log ("not reporting leak from main");
571 return;
575 pending_diagnostic *pd = sm.on_leak (leaked_tree);
576 if (pd)
577 m_eg->get_diagnostic_manager ().add_diagnostic
578 (&sm, m_enode_for_diag, m_enode_for_diag->get_supernode (),
579 m_stmt, &stmt_finder,
580 leaked_tree, sval, state, pd);
583 /* Implementation of region_model_context::on_condition vfunc.
584 Notify all state machines about the condition, which could lead to
585 state transitions. */
587 void
588 impl_region_model_context::on_condition (tree lhs, enum tree_code op, tree rhs)
590 int sm_idx;
591 sm_state_map *smap;
592 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
594 const state_machine &sm = m_ext_state.get_sm (sm_idx);
595 impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
596 m_old_state, m_new_state,
597 m_old_state->m_checker_states[sm_idx],
598 m_new_state->m_checker_states[sm_idx]);
599 sm.on_condition (&sm_ctxt,
600 m_enode_for_diag->get_supernode (), m_stmt,
601 lhs, op, rhs);
605 /* Implementation of region_model_context::on_phi vfunc.
606 Notify all state machines about the phi, which could lead to
607 state transitions. */
609 void
610 impl_region_model_context::on_phi (const gphi *phi, tree rhs)
612 int sm_idx;
613 sm_state_map *smap;
614 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
616 const state_machine &sm = m_ext_state.get_sm (sm_idx);
617 impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
618 m_old_state, m_new_state,
619 m_old_state->m_checker_states[sm_idx],
620 m_new_state->m_checker_states[sm_idx]);
621 sm.on_phi (&sm_ctxt, m_enode_for_diag->get_supernode (), phi, rhs);
625 /* Implementation of region_model_context::on_unexpected_tree_code vfunc.
626 Mark the new state as being invalid for further exploration.
627 TODO(stage1): introduce a warning for when this occurs. */
629 void
630 impl_region_model_context::on_unexpected_tree_code (tree t,
631 const dump_location_t &loc)
633 logger * const logger = get_logger ();
634 if (logger)
635 logger->log ("unhandled tree code: %qs in %qs at %s:%i",
636 get_tree_code_name (TREE_CODE (t)),
637 loc.get_impl_location ().m_function,
638 loc.get_impl_location ().m_file,
639 loc.get_impl_location ().m_line);
640 if (m_new_state)
641 m_new_state->m_valid = false;
644 /* struct point_and_state. */
646 /* Assert that this object is sane. */
648 void
649 point_and_state::validate (const extrinsic_state &ext_state) const
651 /* Skip this in a release build. */
652 #if !CHECKING_P
653 return;
654 #endif
656 m_point.validate ();
658 m_state.validate (ext_state);
660 /* Verify that the callstring's model of the stack corresponds to that
661 of the region_model. */
662 /* They should have the same depth. */
663 gcc_assert (m_point.get_stack_depth ()
664 == m_state.m_region_model->get_stack_depth ());
665 /* Check the functions in the callstring vs those in the frames
666 at each depth. */
667 for (const frame_region *iter_frame
668 = m_state.m_region_model->get_current_frame ();
669 iter_frame; iter_frame = iter_frame->get_calling_frame ())
671 int index = iter_frame->get_index ();
672 gcc_assert (m_point.get_function_at_depth (index)
673 == iter_frame->get_function ());
677 /* Subroutine of print_enode_indices: print a run of indices from START_IDX
678 to END_IDX to PP, using and updating *FIRST_RUN. */
680 static void
681 print_run (pretty_printer *pp, int start_idx, int end_idx,
682 bool *first_run)
684 if (!(*first_run))
685 pp_string (pp, ", ");
686 *first_run = false;
687 if (start_idx == end_idx)
688 pp_printf (pp, "EN: %i", start_idx);
689 else
690 pp_printf (pp, "EN: %i-%i", start_idx, end_idx);
693 /* Print the indices within ENODES to PP, collecting them as
694 runs/singletons e.g. "EN: 4-7, EN: 20-23, EN: 42". */
696 static void
697 print_enode_indices (pretty_printer *pp,
698 const auto_vec<exploded_node *> &enodes)
700 int cur_start_idx = -1;
701 int cur_finish_idx = -1;
702 bool first_run = true;
703 unsigned i;
704 exploded_node *enode;
705 FOR_EACH_VEC_ELT (enodes, i, enode)
707 if (cur_start_idx == -1)
709 gcc_assert (cur_finish_idx == -1);
710 cur_start_idx = cur_finish_idx = enode->m_index;
712 else
714 if (enode->m_index == cur_finish_idx + 1)
715 /* Continuation of a run. */
716 cur_finish_idx = enode->m_index;
717 else
719 /* Finish existing run, start a new one. */
720 gcc_assert (cur_start_idx >= 0);
721 gcc_assert (cur_finish_idx >= 0);
722 print_run (pp, cur_start_idx, cur_finish_idx,
723 &first_run);
724 cur_start_idx = cur_finish_idx = enode->m_index;
728 /* Finish any existing run. */
729 if (cur_start_idx >= 0)
731 gcc_assert (cur_finish_idx >= 0);
732 print_run (pp, cur_start_idx, cur_finish_idx,
733 &first_run);
737 /* struct eg_traits::dump_args_t. */
739 /* The <FILENAME>.eg.dot output can quickly become unwieldy if we show
740 full details for all enodes (both in terms of CPU time to render it,
741 and in terms of being meaningful to a human viewing it).
743 If we show just the IDs then the resulting graph is usually viewable,
744 but then we have to keep switching back and forth between the .dot
745 view and other dumps.
747 This function implements a heuristic for showing detail at the enodes
748 that (we hope) matter, and just the ID at other enodes, fixing the CPU
749 usage of the .dot viewer, and drawing the attention of the viewer
750 to these enodes.
752 Return true if ENODE should be shown in detail in .dot output.
753 Return false if no detail should be shown for ENODE. */
755 bool
756 eg_traits::dump_args_t::show_enode_details_p (const exploded_node &enode) const
758 /* If the number of exploded nodes isn't too large, we may as well show
759 all enodes in full detail in the .dot output. */
760 if (m_eg.m_nodes.length ()
761 <= (unsigned) param_analyzer_max_enodes_for_full_dump)
762 return true;
764 /* Otherwise, assume that what's most interesting are state explosions,
765 and thus the places where this happened.
766 Expand enodes at program points where we hit the per-enode limit, so we
767 can investigate what exploded. */
768 const per_program_point_data *per_point_data
769 = m_eg.get_per_program_point_data (enode.get_point ());
770 return per_point_data->m_excess_enodes > 0;
773 /* class exploded_node : public dnode<eg_traits>. */
775 const char *
776 exploded_node::status_to_str (enum status s)
778 switch (s)
780 default: gcc_unreachable ();
781 case STATUS_WORKLIST: return "WORKLIST";
782 case STATUS_PROCESSED: return "PROCESSED";
783 case STATUS_MERGER: return "MERGER";
784 case STATUS_BULK_MERGED: return "BULK_MERGED";
788 /* exploded_node's ctor. */
790 exploded_node::exploded_node (const point_and_state &ps,
791 int index)
792 : m_ps (ps), m_status (STATUS_WORKLIST), m_index (index),
793 m_num_processed_stmts (0)
795 gcc_checking_assert (ps.get_state ().m_region_model->canonicalized_p ());
798 /* Get the stmt that was processed in this enode at index IDX.
799 IDX is an index within the stmts processed at this enode, rather
800 than within those of the supernode. */
802 const gimple *
803 exploded_node::get_processed_stmt (unsigned idx) const
805 gcc_assert (idx < m_num_processed_stmts);
806 const program_point &point = get_point ();
807 gcc_assert (point.get_kind () == PK_BEFORE_STMT);
808 const supernode *snode = get_supernode ();
809 const unsigned int point_stmt_idx = point.get_stmt_idx ();
810 const unsigned int idx_within_snode = point_stmt_idx + idx;
811 const gimple *stmt = snode->m_stmts[idx_within_snode];
812 return stmt;
815 /* For use by dump_dot, get a value for the .dot "fillcolor" attribute.
816 Colorize by sm-state, to make it easier to see how sm-state propagates
817 through the exploded_graph. */
819 const char *
820 exploded_node::get_dot_fillcolor () const
822 const program_state &state = get_state ();
824 /* We want to be able to easily distinguish the no-sm-state case,
825 and to be able to distinguish cases where there's a single state
826 from each other.
828 Sum the sm_states, and use the result to choose from a table,
829 modulo table-size, special-casing the "no sm-state" case. */
830 int total_sm_state = 0;
831 int i;
832 sm_state_map *smap;
833 FOR_EACH_VEC_ELT (state.m_checker_states, i, smap)
835 for (sm_state_map::iterator_t iter = smap->begin ();
836 iter != smap->end ();
837 ++iter)
838 total_sm_state += (*iter).second.m_state->get_id ();
839 total_sm_state += smap->get_global_state ()->get_id ();
842 if (total_sm_state > 0)
844 /* An arbitrarily-picked collection of light colors. */
845 const char * const colors[]
846 = {"azure", "coral", "cornsilk", "lightblue", "yellow",
847 "honeydew", "lightpink", "lightsalmon", "palegreen1",
848 "wheat", "seashell"};
849 const int num_colors = sizeof (colors) / sizeof (colors[0]);
850 return colors[total_sm_state % num_colors];
852 else
853 /* No sm-state. */
854 return "lightgrey";
857 /* Implementation of dnode::dump_dot vfunc for exploded_node. */
859 void
860 exploded_node::dump_dot (graphviz_out *gv, const dump_args_t &args) const
862 pretty_printer *pp = gv->get_pp ();
864 dump_dot_id (pp);
865 pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
866 get_dot_fillcolor ());
867 pp_write_text_to_stream (pp);
869 pp_printf (pp, "EN: %i", m_index);
870 if (m_status == STATUS_MERGER)
871 pp_string (pp, " (merger)");
872 else if (m_status == STATUS_BULK_MERGED)
873 pp_string (pp, " (bulk merged)");
874 pp_newline (pp);
876 if (args.show_enode_details_p (*this))
878 format f (true);
879 m_ps.get_point ().print (pp, f);
880 pp_newline (pp);
882 const extrinsic_state &ext_state = args.m_eg.get_ext_state ();
883 const program_state &state = m_ps.get_state ();
884 state.dump_to_pp (ext_state, false, true, pp);
885 pp_newline (pp);
887 /* Show any stmts that were processed within this enode,
888 and their index within the supernode. */
889 if (m_num_processed_stmts > 0)
891 const program_point &point = get_point ();
892 gcc_assert (point.get_kind () == PK_BEFORE_STMT);
893 const supernode *snode = get_supernode ();
894 const unsigned int point_stmt_idx = point.get_stmt_idx ();
896 pp_printf (pp, "stmts: %i", m_num_processed_stmts);
897 pp_newline (pp);
898 for (unsigned i = 0; i < m_num_processed_stmts; i++)
900 const unsigned int idx_within_snode = point_stmt_idx + i;
901 const gimple *stmt = snode->m_stmts[idx_within_snode];
902 pp_printf (pp, " %i: ", idx_within_snode);
903 pp_gimple_stmt_1 (pp, stmt, 0, (dump_flags_t)0);
904 pp_newline (pp);
909 /* Dump any saved_diagnostics at this enode. */
911 const diagnostic_manager &dm = args.m_eg.get_diagnostic_manager ();
912 for (unsigned i = 0; i < dm.get_num_diagnostics (); i++)
914 const saved_diagnostic *sd = dm.get_saved_diagnostic (i);
915 if (sd->m_enode == this)
917 pp_printf (pp, "DIAGNOSTIC: %s", sd->m_d->get_kind ());
918 pp_newline (pp);
923 pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
925 pp_string (pp, "\"];\n\n");
926 pp_flush (pp);
929 /* Dump this to PP in a form suitable for use as an id in .dot output. */
931 void
932 exploded_node::dump_dot_id (pretty_printer *pp) const
934 pp_printf (pp, "exploded_node_%i", m_index);
937 /* Dump a multiline representation of this node to PP. */
939 void
940 exploded_node::dump_to_pp (pretty_printer *pp,
941 const extrinsic_state &ext_state) const
943 pp_printf (pp, "EN: %i", m_index);
944 pp_newline (pp);
946 format f (true);
947 m_ps.get_point ().print (pp, f);
948 pp_newline (pp);
950 m_ps.get_state ().dump_to_pp (ext_state, false, true, pp);
951 pp_newline (pp);
954 /* Dump a multiline representation of this node to FILE. */
956 void
957 exploded_node::dump (FILE *fp,
958 const extrinsic_state &ext_state) const
960 pretty_printer pp;
961 pp_format_decoder (&pp) = default_tree_printer;
962 pp_show_color (&pp) = pp_show_color (global_dc->printer);
963 pp.buffer->stream = fp;
964 dump_to_pp (&pp, ext_state);
965 pp_flush (&pp);
968 /* Dump a multiline representation of this node to stderr. */
970 DEBUG_FUNCTION void
971 exploded_node::dump (const extrinsic_state &ext_state) const
973 dump (stderr, ext_state);
976 /* Return a new json::object of the form
977 {"point" : object for program_point,
978 "state" : object for program_state,
979 "status" : str,
980 "idx" : int,
981 "processed_stmts" : int}. */
983 json::object *
984 exploded_node::to_json (const extrinsic_state &ext_state) const
986 json::object *enode_obj = new json::object ();
988 enode_obj->set ("point", get_point ().to_json ());
989 enode_obj->set ("state", get_state ().to_json (ext_state));
990 enode_obj->set ("status", new json::string (status_to_str (m_status)));
991 enode_obj->set ("idx", new json::integer_number (m_index));
992 enode_obj->set ("processed_stmts",
993 new json::integer_number (m_num_processed_stmts));
995 return enode_obj;
998 } // namespace ana
1000 /* Return true if FNDECL has a gimple body. */
1001 // TODO: is there a pre-canned way to do this?
1003 bool
1004 fndecl_has_gimple_body_p (tree fndecl)
1006 if (fndecl == NULL_TREE)
1007 return false;
1009 cgraph_node *n = cgraph_node::get (fndecl);
1010 if (!n)
1011 return false;
1013 return n->has_gimple_body_p ();
1016 namespace ana {
1018 /* A pending_diagnostic subclass for implementing "__analyzer_dump_path". */
1020 class dump_path_diagnostic
1021 : public pending_diagnostic_subclass<dump_path_diagnostic>
1023 public:
1024 bool emit (rich_location *richloc) FINAL OVERRIDE
1026 inform (richloc, "path");
1027 return true;
1030 const char *get_kind () const FINAL OVERRIDE { return "dump_path_diagnostic"; }
1032 bool operator== (const dump_path_diagnostic &) const
1034 return true;
1038 /* Modify STATE in place, applying the effects of the stmt at this node's
1039 point. */
1041 exploded_node::on_stmt_flags
1042 exploded_node::on_stmt (exploded_graph &eg,
1043 const supernode *snode,
1044 const gimple *stmt,
1045 program_state *state) const
1047 logger *logger = eg.get_logger ();
1048 LOG_SCOPE (logger);
1049 if (logger)
1051 logger->start_log_line ();
1052 pp_gimple_stmt_1 (logger->get_printer (), stmt, 0, (dump_flags_t)0);
1053 logger->end_log_line ();
1056 /* Update input_location in case of ICE: make it easier to track down which
1057 source construct we're failing to handle. */
1058 input_location = stmt->location;
1060 gcc_assert (state->m_region_model);
1062 /* Preserve the old state. It is used here for looking
1063 up old checker states, for determining state transitions, and
1064 also within impl_region_model_context and impl_sm_context for
1065 going from tree to svalue_id. */
1066 const program_state old_state (*state);
1068 impl_region_model_context ctxt (eg, this,
1069 &old_state, state,
1070 stmt);
1072 bool unknown_side_effects = false;
1074 switch (gimple_code (stmt))
1076 default:
1077 /* No-op for now. */
1078 break;
1080 case GIMPLE_ASSIGN:
1082 const gassign *assign = as_a <const gassign *> (stmt);
1083 state->m_region_model->on_assignment (assign, &ctxt);
1085 break;
1087 case GIMPLE_ASM:
1088 /* No-op for now. */
1089 break;
1091 case GIMPLE_CALL:
1093 /* Track whether we have a gcall to a function that's not recognized by
1094 anything, for which we don't have a function body, or for which we
1095 don't know the fndecl. */
1096 const gcall *call = as_a <const gcall *> (stmt);
1098 /* Debugging/test support. */
1099 if (is_special_named_call_p (call, "__analyzer_describe", 2))
1100 state->m_region_model->impl_call_analyzer_describe (call, &ctxt);
1101 else if (is_special_named_call_p (call, "__analyzer_dump", 0))
1103 /* Handle the builtin "__analyzer_dump" by dumping state
1104 to stderr. */
1105 state->dump (eg.get_ext_state (), true);
1107 else if (is_special_named_call_p (call, "__analyzer_dump_path", 0))
1109 /* Handle the builtin "__analyzer_dump_path" by queuing a
1110 diagnostic at this exploded_node. */
1111 ctxt.warn (new dump_path_diagnostic ());
1113 else if (is_special_named_call_p (call, "__analyzer_dump_region_model",
1116 /* Handle the builtin "__analyzer_dump_region_model" by dumping
1117 the region model's state to stderr. */
1118 state->m_region_model->dump (false);
1120 else if (is_special_named_call_p (call, "__analyzer_eval", 1))
1121 state->m_region_model->impl_call_analyzer_eval (call, &ctxt);
1122 else if (is_special_named_call_p (call, "__analyzer_break", 0))
1124 /* Handle the builtin "__analyzer_break" by triggering a
1125 breakpoint. */
1126 /* TODO: is there a good cross-platform way to do this? */
1127 raise (SIGINT);
1129 else if (is_special_named_call_p (call,
1130 "__analyzer_dump_exploded_nodes",
1133 /* This is handled elsewhere. */
1135 else if (is_setjmp_call_p (call))
1136 state->m_region_model->on_setjmp (call, this, &ctxt);
1137 else if (is_longjmp_call_p (call))
1139 on_longjmp (eg, call, state, &ctxt);
1140 return on_stmt_flags::terminate_path ();
1142 else
1143 unknown_side_effects
1144 = state->m_region_model->on_call_pre (call, &ctxt);
1146 break;
1148 case GIMPLE_RETURN:
1150 const greturn *return_ = as_a <const greturn *> (stmt);
1151 state->m_region_model->on_return (return_, &ctxt);
1153 break;
1156 bool any_sm_changes = false;
1157 int sm_idx;
1158 sm_state_map *smap;
1159 FOR_EACH_VEC_ELT (old_state.m_checker_states, sm_idx, smap)
1161 const state_machine &sm = eg.get_ext_state ().get_sm (sm_idx);
1162 const sm_state_map *old_smap
1163 = old_state.m_checker_states[sm_idx];
1164 sm_state_map *new_smap = state->m_checker_states[sm_idx];
1165 impl_sm_context sm_ctxt (eg, sm_idx, sm, this, &old_state, state,
1166 old_smap, new_smap);
1167 /* Allow the state_machine to handle the stmt. */
1168 if (sm.on_stmt (&sm_ctxt, snode, stmt))
1169 unknown_side_effects = false;
1170 if (*old_smap != *new_smap)
1171 any_sm_changes = true;
1174 if (const gcall *call = dyn_cast <const gcall *> (stmt))
1175 state->m_region_model->on_call_post (call, unknown_side_effects, &ctxt);
1177 return on_stmt_flags (any_sm_changes);
1180 /* Consider the effect of following superedge SUCC from this node.
1182 Return true if it's feasible to follow the edge, or false
1183 if it's infeasible.
1185 Examples: if it's the "true" branch within
1186 a CFG and we know the conditional is false, we know it's infeasible.
1187 If it's one of multiple interprocedual "return" edges, then only
1188 the edge back to the most recent callsite is feasible.
1190 Update NEXT_STATE accordingly (e.g. to record that a condition was
1191 true or false, or that the NULL-ness of a pointer has been checked,
1192 pushing/popping stack frames, etc).
1194 Update NEXT_POINT accordingly (updating the call string). */
1196 bool
1197 exploded_node::on_edge (exploded_graph &eg,
1198 const superedge *succ,
1199 program_point *next_point,
1200 program_state *next_state) const
1202 LOG_FUNC (eg.get_logger ());
1204 if (!next_point->on_edge (eg, succ))
1205 return false;
1207 if (!next_state->on_edge (eg, *this, succ))
1208 return false;
1210 return true;
1213 /* Verify that the stack at LONGJMP_POINT is still valid, given a call
1214 to "setjmp" at SETJMP_POINT - the stack frame that "setjmp" was
1215 called in must still be valid.
1217 Caveat: this merely checks the call_strings in the points; it doesn't
1218 detect the case where a frame returns and is then called again. */
1220 static bool
1221 valid_longjmp_stack_p (const program_point &longjmp_point,
1222 const program_point &setjmp_point)
1224 const call_string &cs_at_longjmp = longjmp_point.get_call_string ();
1225 const call_string &cs_at_setjmp = setjmp_point.get_call_string ();
1227 if (cs_at_longjmp.length () < cs_at_setjmp.length ())
1228 return false;
1230 /* Check that the call strings match, up to the depth of the
1231 setjmp point. */
1232 for (unsigned depth = 0; depth < cs_at_setjmp.length (); depth++)
1233 if (cs_at_longjmp[depth] != cs_at_setjmp[depth])
1234 return false;
1236 return true;
1239 /* A pending_diagnostic subclass for complaining about bad longjmps,
1240 where the enclosing function of the "setjmp" has returned (and thus
1241 the stack frame no longer exists). */
1243 class stale_jmp_buf : public pending_diagnostic_subclass<dump_path_diagnostic>
1245 public:
1246 stale_jmp_buf (const gcall *setjmp_call, const gcall *longjmp_call)
1247 : m_setjmp_call (setjmp_call), m_longjmp_call (longjmp_call)
1250 bool emit (rich_location *richloc) FINAL OVERRIDE
1252 return warning_at
1253 (richloc, OPT_Wanalyzer_stale_setjmp_buffer,
1254 "%qs called after enclosing function of %qs has returned",
1255 get_user_facing_name (m_longjmp_call),
1256 get_user_facing_name (m_setjmp_call));
1259 const char *get_kind () const FINAL OVERRIDE
1260 { return "stale_jmp_buf"; }
1262 bool operator== (const stale_jmp_buf &other) const
1264 return (m_setjmp_call == other.m_setjmp_call
1265 && m_longjmp_call == other.m_longjmp_call);
1268 private:
1269 const gcall *m_setjmp_call;
1270 const gcall *m_longjmp_call;
1273 /* Handle LONGJMP_CALL, a call to longjmp or siglongjmp.
1275 Attempt to locate where setjmp/sigsetjmp was called on the jmp_buf and build
1276 an exploded_node and exploded_edge to it representing a rewind to that frame,
1277 handling the various kinds of failure that can occur. */
1279 void
1280 exploded_node::on_longjmp (exploded_graph &eg,
1281 const gcall *longjmp_call,
1282 program_state *new_state,
1283 region_model_context *ctxt) const
1285 tree buf_ptr = gimple_call_arg (longjmp_call, 0);
1286 gcc_assert (POINTER_TYPE_P (TREE_TYPE (buf_ptr)));
1288 region_model *new_region_model = new_state->m_region_model;
1289 const svalue *buf_ptr_sval = new_region_model->get_rvalue (buf_ptr, ctxt);
1290 const region *buf = new_region_model->deref_rvalue (buf_ptr_sval, buf_ptr,
1291 ctxt);
1293 const svalue *buf_content_sval = new_region_model->get_store_value (buf);
1294 const setjmp_svalue *setjmp_sval
1295 = buf_content_sval->dyn_cast_setjmp_svalue ();
1296 if (!setjmp_sval)
1297 return;
1299 const setjmp_record tmp_setjmp_record = setjmp_sval->get_setjmp_record ();
1301 /* Build a custom enode and eedge for rewinding from the longjmp/siglongjmp
1302 call back to the setjmp/sigsetjmp. */
1303 rewind_info_t rewind_info (tmp_setjmp_record, longjmp_call);
1305 const gcall *setjmp_call = rewind_info.get_setjmp_call ();
1306 const program_point &setjmp_point = rewind_info.get_setjmp_point ();
1308 const program_point &longjmp_point = get_point ();
1310 /* Verify that the setjmp's call_stack hasn't been popped. */
1311 if (!valid_longjmp_stack_p (longjmp_point, setjmp_point))
1313 ctxt->warn (new stale_jmp_buf (setjmp_call, longjmp_call));
1314 return;
1317 gcc_assert (longjmp_point.get_stack_depth ()
1318 >= setjmp_point.get_stack_depth ());
1320 /* Update the state for use by the destination node. */
1322 /* Stash the current number of diagnostics so that we can update
1323 any that this adds to show where the longjmp is rewinding to. */
1325 diagnostic_manager *dm = &eg.get_diagnostic_manager ();
1326 unsigned prev_num_diagnostics = dm->get_num_diagnostics ();
1328 new_region_model->on_longjmp (longjmp_call, setjmp_call,
1329 setjmp_point.get_stack_depth (), ctxt);
1331 /* Detect leaks in the new state relative to the old state. */
1332 program_state::detect_leaks (get_state (), *new_state, NULL,
1333 eg.get_ext_state (), ctxt);
1335 program_point next_point
1336 = program_point::after_supernode (setjmp_point.get_supernode (),
1337 setjmp_point.get_call_string ());
1339 exploded_node *next
1340 = eg.get_or_create_node (next_point, *new_state, this);
1342 /* Create custom exploded_edge for a longjmp. */
1343 if (next)
1345 exploded_edge *eedge
1346 = eg.add_edge (const_cast<exploded_node *> (this), next, NULL,
1347 new rewind_info_t (tmp_setjmp_record, longjmp_call));
1349 /* For any diagnostics that were queued here (such as leaks) we want
1350 the checker_path to show the rewinding events after the "final event"
1351 so that the user sees where the longjmp is rewinding to (otherwise the
1352 path is meaningless).
1354 For example, we want to emit something like:
1355 | NN | {
1356 | NN | longjmp (env, 1);
1357 | | ~~~~~~~~~~~~~~~~
1358 | | |
1359 | | (10) 'ptr' leaks here; was allocated at (7)
1360 | | (11) rewinding from 'longjmp' in 'inner'...
1362 <-------------+
1364 'outer': event 12
1366 | NN | i = setjmp(env);
1367 | | ^~~~~~
1368 | | |
1369 | | (12) ...to 'setjmp' in 'outer' (saved at (2))
1371 where the "final" event above is event (10), but we want to append
1372 events (11) and (12) afterwards.
1374 Do this by setting m_trailing_eedge on any diagnostics that were
1375 just saved. */
1376 unsigned num_diagnostics = dm->get_num_diagnostics ();
1377 for (unsigned i = prev_num_diagnostics; i < num_diagnostics; i++)
1379 saved_diagnostic *sd = dm->get_saved_diagnostic (i);
1380 sd->m_trailing_eedge = eedge;
1385 /* Subroutine of exploded_graph::process_node for finding the successors
1386 of the supernode for a function exit basic block.
1388 Ensure that pop_frame is called, potentially queuing diagnostics about
1389 leaks. */
1391 void
1392 exploded_node::detect_leaks (exploded_graph &eg) const
1394 LOG_FUNC_1 (eg.get_logger (), "EN: %i", m_index);
1396 gcc_assert (get_point ().get_supernode ()->return_p ());
1398 /* If we're not a "top-level" function, do nothing; pop_frame
1399 will be called when handling the return superedge. */
1400 if (get_point ().get_stack_depth () > 1)
1401 return;
1403 /* We have a "top-level" function. */
1404 gcc_assert (get_point ().get_stack_depth () == 1);
1406 const program_state &old_state = get_state ();
1408 /* Work with a temporary copy of the state: pop the frame, and see
1409 what leaks (via purge_unused_svalues). */
1410 program_state new_state (old_state);
1412 gcc_assert (new_state.m_region_model);
1414 impl_region_model_context ctxt (eg, this,
1415 &old_state, &new_state,
1416 get_stmt ());
1417 const svalue *result = NULL;
1418 new_state.m_region_model->pop_frame (NULL, &result, &ctxt);
1419 program_state::detect_leaks (old_state, new_state, result,
1420 eg.get_ext_state (), &ctxt);
1423 /* Dump the successors and predecessors of this enode to OUTF. */
1425 void
1426 exploded_node::dump_succs_and_preds (FILE *outf) const
1428 unsigned i;
1429 exploded_edge *e;
1431 auto_vec<exploded_node *> preds (m_preds.length ());
1432 FOR_EACH_VEC_ELT (m_preds, i, e)
1433 preds.quick_push (e->m_src);
1434 pretty_printer pp;
1435 print_enode_indices (&pp, preds);
1436 fprintf (outf, "preds: %s\n",
1437 pp_formatted_text (&pp));
1440 auto_vec<exploded_node *> succs (m_succs.length ());
1441 FOR_EACH_VEC_ELT (m_succs, i, e)
1442 succs.quick_push (e->m_dest);
1443 pretty_printer pp;
1444 print_enode_indices (&pp, succs);
1445 fprintf (outf, "succs: %s\n",
1446 pp_formatted_text (&pp));
1450 /* class rewind_info_t : public exploded_edge::custom_info_t. */
1452 /* Implementation of exploded_edge::custom_info_t::update_model vfunc
1453 for rewind_info_t.
1455 Update state for the special-case of a rewind of a longjmp
1456 to a setjmp (which doesn't have a superedge, but does affect
1457 state). */
1459 void
1460 rewind_info_t::update_model (region_model *model,
1461 const exploded_edge &eedge)
1463 const program_point &longjmp_point = eedge.m_src->get_point ();
1464 const program_point &setjmp_point = eedge.m_dest->get_point ();
1466 gcc_assert (longjmp_point.get_stack_depth ()
1467 >= setjmp_point.get_stack_depth ());
1469 model->on_longjmp (get_longjmp_call (),
1470 get_setjmp_call (),
1471 setjmp_point.get_stack_depth (), NULL);
1474 /* Implementation of exploded_edge::custom_info_t::add_events_to_path vfunc
1475 for rewind_info_t. */
1477 void
1478 rewind_info_t::add_events_to_path (checker_path *emission_path,
1479 const exploded_edge &eedge)
1481 const exploded_node *src_node = eedge.m_src;
1482 const program_point &src_point = src_node->get_point ();
1483 const int src_stack_depth = src_point.get_stack_depth ();
1484 const exploded_node *dst_node = eedge.m_dest;
1485 const program_point &dst_point = dst_node->get_point ();
1486 const int dst_stack_depth = dst_point.get_stack_depth ();
1488 emission_path->add_event
1489 (new rewind_from_longjmp_event
1490 (&eedge, get_longjmp_call ()->location,
1491 src_point.get_fndecl (),
1492 src_stack_depth, this));
1493 emission_path->add_event
1494 (new rewind_to_setjmp_event
1495 (&eedge, get_setjmp_call ()->location,
1496 dst_point.get_fndecl (),
1497 dst_stack_depth, this));
1500 /* class exploded_edge : public dedge<eg_traits>. */
1502 /* exploded_edge's ctor. */
1504 exploded_edge::exploded_edge (exploded_node *src, exploded_node *dest,
1505 const superedge *sedge,
1506 custom_info_t *custom_info)
1507 : dedge<eg_traits> (src, dest), m_sedge (sedge),
1508 m_custom_info (custom_info)
1512 /* exploded_edge's dtor. */
1514 exploded_edge::~exploded_edge ()
1516 delete m_custom_info;
1519 /* Implementation of dedge::dump_dot vfunc for exploded_edge.
1520 Use the label of the underlying superedge, if any. */
1522 void
1523 exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &) const
1525 pretty_printer *pp = gv->get_pp ();
1527 const char *style = "\"solid,bold\"";
1528 const char *color = "black";
1529 int weight = 10;
1530 const char *constraint = "true";
1532 if (m_sedge)
1533 switch (m_sedge->m_kind)
1535 default:
1536 gcc_unreachable ();
1537 case SUPEREDGE_CFG_EDGE:
1538 break;
1539 case SUPEREDGE_CALL:
1540 color = "red";
1541 //constraint = "false";
1542 break;
1543 case SUPEREDGE_RETURN:
1544 color = "green";
1545 //constraint = "false";
1546 break;
1547 case SUPEREDGE_INTRAPROCEDURAL_CALL:
1548 style = "\"dotted\"";
1549 break;
1551 if (m_custom_info)
1553 color = "red";
1554 style = "\"dotted\"";
1557 m_src->dump_dot_id (pp);
1558 pp_string (pp, " -> ");
1559 m_dest->dump_dot_id (pp);
1560 pp_printf (pp,
1561 (" [style=%s, color=%s, weight=%d, constraint=%s,"
1562 " headlabel=\""),
1563 style, color, weight, constraint);
1565 if (m_sedge)
1566 m_sedge->dump_label_to_pp (pp, false);
1567 else if (m_custom_info)
1568 m_custom_info->print (pp);
1570 //pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
1572 pp_printf (pp, "\"];\n");
1575 /* Return a new json::object of the form
1576 {"src_idx": int, the index of the source exploded edge,
1577 "dst_idx": int, the index of the destination exploded edge,
1578 "sedge": (optional) object for the superedge, if any,
1579 "custom": (optional) str, a description, if this is a custom edge}. */
1581 json::object *
1582 exploded_edge::to_json () const
1584 json::object *eedge_obj = new json::object ();
1585 eedge_obj->set ("src_idx", new json::integer_number (m_src->m_index));
1586 eedge_obj->set ("dst_idx", new json::integer_number (m_dest->m_index));
1587 if (m_sedge)
1588 eedge_obj->set ("sedge", m_sedge->to_json ());
1589 if (m_custom_info)
1591 pretty_printer pp;
1592 pp_format_decoder (&pp) = default_tree_printer;
1593 m_custom_info->print (&pp);
1594 eedge_obj->set ("custom", new json::string (pp_formatted_text (&pp)));
1596 return eedge_obj;
1599 /* struct stats. */
1601 /* stats' ctor. */
1603 stats::stats (int num_supernodes)
1604 : m_node_reuse_count (0),
1605 m_node_reuse_after_merge_count (0),
1606 m_num_supernodes (num_supernodes)
1608 for (int i = 0; i < NUM_POINT_KINDS; i++)
1609 m_num_nodes[i] = 0;
1612 /* Log these stats in multiline form to LOGGER. */
1614 void
1615 stats::log (logger *logger) const
1617 gcc_assert (logger);
1618 for (int i = 0; i < NUM_POINT_KINDS; i++)
1619 if (m_num_nodes[i] > 0)
1620 logger->log ("m_num_nodes[%s]: %i",
1621 point_kind_to_string (static_cast <enum point_kind> (i)),
1622 m_num_nodes[i]);
1623 logger->log ("m_node_reuse_count: %i", m_node_reuse_count);
1624 logger->log ("m_node_reuse_after_merge_count: %i",
1625 m_node_reuse_after_merge_count);
1628 /* Dump these stats in multiline form to OUT. */
1630 void
1631 stats::dump (FILE *out) const
1633 for (int i = 0; i < NUM_POINT_KINDS; i++)
1634 if (m_num_nodes[i] > 0)
1635 fprintf (out, "m_num_nodes[%s]: %i\n",
1636 point_kind_to_string (static_cast <enum point_kind> (i)),
1637 m_num_nodes[i]);
1638 fprintf (out, "m_node_reuse_count: %i\n", m_node_reuse_count);
1639 fprintf (out, "m_node_reuse_after_merge_count: %i\n",
1640 m_node_reuse_after_merge_count);
1642 if (m_num_supernodes > 0)
1643 fprintf (out, "PK_AFTER_SUPERNODE nodes per supernode: %.2f\n",
1644 (float)m_num_nodes[PK_AFTER_SUPERNODE] / (float)m_num_supernodes);
1647 /* Return the total number of enodes recorded within this object. */
1650 stats::get_total_enodes () const
1652 int result = 0;
1653 for (int i = 0; i < NUM_POINT_KINDS; i++)
1654 result += m_num_nodes[i];
1655 return result;
1658 /* strongly_connected_components's ctor. Tarjan's SCC algorithm. */
1660 strongly_connected_components::
1661 strongly_connected_components (const supergraph &sg, logger *logger)
1662 : m_sg (sg), m_per_node (m_sg.num_nodes ())
1664 LOG_SCOPE (logger);
1665 auto_timevar tv (TV_ANALYZER_SCC);
1667 for (int i = 0; i < m_sg.num_nodes (); i++)
1668 m_per_node.quick_push (per_node_data ());
1670 for (int i = 0; i < m_sg.num_nodes (); i++)
1671 if (m_per_node[i].m_index == -1)
1672 strong_connect (i);
1674 if (0)
1675 dump ();
1678 /* Dump this object to stderr. */
1680 DEBUG_FUNCTION void
1681 strongly_connected_components::dump () const
1683 for (int i = 0; i < m_sg.num_nodes (); i++)
1685 const per_node_data &v = m_per_node[i];
1686 fprintf (stderr, "SN %i: index: %i lowlink: %i on_stack: %i\n",
1687 i, v.m_index, v.m_lowlink, v.m_on_stack);
1691 /* Subroutine of strongly_connected_components's ctor, part of Tarjan's
1692 SCC algorithm. */
1694 void
1695 strongly_connected_components::strong_connect (unsigned index)
1697 supernode *v_snode = m_sg.get_node_by_index (index);
1699 /* Set the depth index for v to the smallest unused index. */
1700 per_node_data *v = &m_per_node[index];
1701 v->m_index = index;
1702 v->m_lowlink = index;
1703 m_stack.safe_push (index);
1704 v->m_on_stack = true;
1705 index++;
1707 /* Consider successors of v. */
1708 unsigned i;
1709 superedge *sedge;
1710 FOR_EACH_VEC_ELT (v_snode->m_succs, i, sedge)
1712 if (sedge->get_kind () != SUPEREDGE_CFG_EDGE
1713 && sedge->get_kind () != SUPEREDGE_INTRAPROCEDURAL_CALL)
1714 continue;
1715 supernode *w_snode = sedge->m_dest;
1716 per_node_data *w = &m_per_node[w_snode->m_index];
1717 if (w->m_index == -1)
1719 /* Successor w has not yet been visited; recurse on it. */
1720 strong_connect (w_snode->m_index);
1721 v->m_lowlink = MIN (v->m_lowlink, w->m_lowlink);
1723 else if (w->m_on_stack)
1725 /* Successor w is in stack S and hence in the current SCC
1726 If w is not on stack, then (v, w) is a cross-edge in the DFS
1727 tree and must be ignored. */
1728 v->m_lowlink = MIN (v->m_lowlink, w->m_index);
1732 /* If v is a root node, pop the stack and generate an SCC. */
1734 if (v->m_lowlink == v->m_index)
1736 per_node_data *w;
1737 do {
1738 int idx = m_stack.pop ();
1739 w = &m_per_node[idx];
1740 w->m_on_stack = false;
1741 } while (w != v);
1745 /* worklist's ctor. */
1747 worklist::worklist (const exploded_graph &eg, const analysis_plan &plan)
1748 : m_scc (eg.get_supergraph (), eg.get_logger ()),
1749 m_plan (plan),
1750 m_queue (key_t (*this, NULL))
1754 /* Return the number of nodes in the worklist. */
1756 unsigned
1757 worklist::length () const
1759 return m_queue.nodes ();
1762 /* Return the next node in the worklist, removing it. */
1764 exploded_node *
1765 worklist::take_next ()
1767 return m_queue.extract_min ();
1770 /* Return the next node in the worklist without removing it. */
1772 exploded_node *
1773 worklist::peek_next ()
1775 return m_queue.min ();
1778 /* Add ENODE to the worklist. */
1780 void
1781 worklist::add_node (exploded_node *enode)
1783 gcc_assert (enode->get_status () == exploded_node::STATUS_WORKLIST);
1784 m_queue.insert (key_t (*this, enode), enode);
1787 /* Comparator for implementing worklist::key_t comparison operators.
1788 Return negative if KA is before KB
1789 Return positive if KA is after KB
1790 Return 0 if they are equal.
1792 The ordering of the worklist is critical for performance and for
1793 avoiding node explosions. Ideally we want all enodes at a CFG join-point
1794 with the same callstring to be sorted next to each other in the worklist
1795 so that a run of consecutive enodes can be merged and processed "in bulk"
1796 rather than individually or pairwise, minimizing the number of new enodes
1797 created. */
1800 worklist::key_t::cmp (const worklist::key_t &ka, const worklist::key_t &kb)
1802 const program_point &point_a = ka.m_enode->get_point ();
1803 const program_point &point_b = kb.m_enode->get_point ();
1804 const call_string &call_string_a = point_a.get_call_string ();
1805 const call_string &call_string_b = point_b.get_call_string ();
1807 /* Order empty-callstring points with different functions based on the
1808 analysis_plan, so that we generate summaries before they are used. */
1809 if (flag_analyzer_call_summaries
1810 && call_string_a.empty_p ()
1811 && call_string_b.empty_p ()
1812 && point_a.get_function () != NULL
1813 && point_b.get_function () != NULL
1814 && point_a.get_function () != point_b.get_function ())
1816 return ka.m_worklist.m_plan.cmp_function (point_a.get_function (),
1817 point_b.get_function ());
1820 /* First, order by SCC. */
1821 int scc_id_a = ka.get_scc_id (ka.m_enode);
1822 int scc_id_b = kb.get_scc_id (kb.m_enode);
1823 if (scc_id_a != scc_id_b)
1824 return scc_id_a - scc_id_b;
1826 /* If in same SCC, order by supernode index (an arbitrary but stable
1827 ordering). */
1828 const supernode *snode_a = ka.m_enode->get_supernode ();
1829 const supernode *snode_b = kb.m_enode->get_supernode ();
1830 if (snode_a == NULL)
1832 if (snode_b != NULL)
1833 /* One is NULL. */
1834 return -1;
1835 else
1836 /* Both are NULL. */
1837 return 0;
1839 if (snode_b == NULL)
1840 /* One is NULL. */
1841 return 1;
1842 /* Neither are NULL. */
1843 gcc_assert (snode_a && snode_b);
1844 if (snode_a->m_index != snode_b->m_index)
1845 return snode_a->m_index - snode_b->m_index;
1847 gcc_assert (snode_a == snode_b);
1849 /* The points might vary by callstring; try sorting by callstring. */
1850 int cs_cmp = call_string::cmp (call_string_a, call_string_b);
1851 if (cs_cmp)
1852 return cs_cmp;
1854 /* Order within supernode via program point. */
1855 int within_snode_cmp
1856 = function_point::cmp_within_supernode (point_a.get_function_point (),
1857 point_b.get_function_point ());
1858 if (within_snode_cmp)
1859 return within_snode_cmp;
1861 /* Otherwise, we ought to have the same program_point. */
1862 gcc_assert (point_a == point_b);
1864 const program_state &state_a = ka.m_enode->get_state ();
1865 const program_state &state_b = kb.m_enode->get_state ();
1867 /* Sort by sm-state, so that identical sm-states are grouped
1868 together in the worklist.
1869 For now, sort by the hash value (might not be deterministic). */
1870 for (unsigned sm_idx = 0; sm_idx < state_a.m_checker_states.length ();
1871 ++sm_idx)
1873 sm_state_map *smap_a = state_a.m_checker_states[sm_idx];
1874 sm_state_map *smap_b = state_b.m_checker_states[sm_idx];
1876 hashval_t hash_a = smap_a->hash ();
1877 hashval_t hash_b = smap_b->hash ();
1878 if (hash_a < hash_b)
1879 return -1;
1880 else if (hash_a > hash_b)
1881 return 1;
1884 /* Otherwise, we have two enodes at the same program point but with
1885 different states. We don't have a good total ordering on states,
1886 so order them by enode index, so that we have at least have a
1887 stable sort. */
1888 return ka.m_enode->m_index - kb.m_enode->m_index;
1891 /* exploded_graph's ctor. */
1893 exploded_graph::exploded_graph (const supergraph &sg, logger *logger,
1894 const extrinsic_state &ext_state,
1895 const state_purge_map *purge_map,
1896 const analysis_plan &plan,
1897 int verbosity)
1898 : m_sg (sg), m_logger (logger),
1899 m_worklist (*this, plan),
1900 m_ext_state (ext_state),
1901 m_purge_map (purge_map),
1902 m_plan (plan),
1903 m_diagnostic_manager (logger, ext_state.get_engine (), verbosity),
1904 m_global_stats (m_sg.num_nodes ()),
1905 m_functionless_stats (m_sg.num_nodes ()),
1906 m_PK_AFTER_SUPERNODE_per_snode (m_sg.num_nodes ())
1908 m_origin = get_or_create_node (program_point::origin (),
1909 program_state (ext_state), NULL);
1910 for (int i = 0; i < m_sg.num_nodes (); i++)
1911 m_PK_AFTER_SUPERNODE_per_snode.quick_push (i);
1914 /* exploded_graph's dtor. */
1916 exploded_graph::~exploded_graph ()
1918 for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
1919 iter != m_per_function_stats.end ();
1920 ++iter)
1921 delete (*iter).second;
1923 for (point_map_t::iterator iter = m_per_point_data.begin ();
1924 iter != m_per_point_data.end ();
1925 ++iter)
1926 delete (*iter).second;
1929 /* Ensure that there is an exploded_node representing an external call to
1930 FUN, adding it to the worklist if creating it.
1932 Add an edge from the origin exploded_node to the function entrypoint
1933 exploded_node.
1935 Return the exploded_node for the entrypoint to the function. */
1937 exploded_node *
1938 exploded_graph::add_function_entry (function *fun)
1940 gcc_assert (gimple_has_body_p (fun->decl));
1942 /* Be idempotent. */
1943 if (m_functions_with_enodes.contains (fun))
1945 logger * const logger = get_logger ();
1946 if (logger)
1947 logger->log ("entrypoint for %qE already exists", fun->decl);
1948 return NULL;
1951 program_point point = program_point::from_function_entry (m_sg, fun);
1952 program_state state (m_ext_state);
1953 state.push_frame (m_ext_state, fun);
1955 if (!state.m_valid)
1956 return NULL;
1958 exploded_node *enode = get_or_create_node (point, state, NULL);
1959 if (!enode)
1960 return NULL;
1962 add_edge (m_origin, enode, NULL);
1964 m_functions_with_enodes.add (fun);
1966 return enode;
1969 /* Get or create an exploded_node for (POINT, STATE).
1970 If a new node is created, it is added to the worklist.
1972 Use ENODE_FOR_DIAG, a pre-existing enode, for any diagnostics
1973 that need to be emitted (e.g. when purging state *before* we have
1974 a new enode). */
1976 exploded_node *
1977 exploded_graph::get_or_create_node (const program_point &point,
1978 const program_state &state,
1979 const exploded_node *enode_for_diag)
1981 logger * const logger = get_logger ();
1982 LOG_FUNC (logger);
1983 if (logger)
1985 format f (false);
1986 pretty_printer *pp = logger->get_printer ();
1987 logger->start_log_line ();
1988 pp_string (pp, "point: ");
1989 point.print (pp, f);
1990 logger->end_log_line ();
1991 logger->start_log_line ();
1992 pp_string (pp, "state: ");
1993 state.dump_to_pp (m_ext_state, true, false, pp);
1994 logger->end_log_line ();
1997 /* Stop exploring paths for which we don't know how to effectively
1998 model the state. */
1999 if (!state.m_valid)
2001 if (logger)
2002 logger->log ("invalid state; not creating node");
2003 return NULL;
2006 auto_cfun sentinel (point.get_function ());
2008 state.validate (get_ext_state ());
2010 //state.dump (get_ext_state ());
2012 /* Prune state to try to improve the chances of a cache hit,
2013 avoiding generating redundant nodes. */
2014 program_state pruned_state
2015 = state.prune_for_point (*this, point, enode_for_diag);
2017 pruned_state.validate (get_ext_state ());
2019 //pruned_state.dump (get_ext_state ());
2021 if (logger)
2023 pretty_printer *pp = logger->get_printer ();
2024 logger->start_log_line ();
2025 pp_string (pp, "pruned_state: ");
2026 pruned_state.dump_to_pp (m_ext_state, true, false, pp);
2027 logger->end_log_line ();
2028 pruned_state.m_region_model->dump_to_pp (logger->get_printer (), true,
2029 false);
2032 stats *per_fn_stats = get_or_create_function_stats (point.get_function ());
2034 stats *per_cs_stats
2035 = &get_or_create_per_call_string_data (point.get_call_string ())->m_stats;
2037 point_and_state ps (point, pruned_state);
2038 ps.validate (m_ext_state);
2039 if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
2041 /* An exploded_node for PS already exists. */
2042 if (logger)
2043 logger->log ("reused EN: %i", (*slot)->m_index);
2044 m_global_stats.m_node_reuse_count++;
2045 per_fn_stats->m_node_reuse_count++;
2046 per_cs_stats->m_node_reuse_count++;
2047 return *slot;
2050 per_program_point_data *per_point_data
2051 = get_or_create_per_program_point_data (point);
2053 /* Consider merging state with another enode at this program_point. */
2054 if (flag_analyzer_state_merge)
2056 exploded_node *existing_enode;
2057 unsigned i;
2058 FOR_EACH_VEC_ELT (per_point_data->m_enodes, i, existing_enode)
2060 if (logger)
2061 logger->log ("considering merging with existing EN: %i for point",
2062 existing_enode->m_index);
2063 gcc_assert (existing_enode->get_point () == point);
2064 const program_state &existing_state = existing_enode->get_state ();
2066 /* This merges successfully within the loop. */
2068 program_state merged_state (m_ext_state);
2069 if (pruned_state.can_merge_with_p (existing_state, point,
2070 &merged_state))
2072 if (logger)
2073 logger->log ("merging new state with that of EN: %i",
2074 existing_enode->m_index);
2076 /* Try again for a cache hit.
2077 Whether we get one or not, merged_state's value_ids have no
2078 relationship to those of the input state, and thus to those
2079 of CHANGE, so we must purge any svalue_ids from *CHANGE. */
2080 ps.set_state (merged_state);
2082 if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
2084 /* An exploded_node for PS already exists. */
2085 if (logger)
2086 logger->log ("reused EN: %i", (*slot)->m_index);
2087 m_global_stats.m_node_reuse_after_merge_count++;
2088 per_fn_stats->m_node_reuse_after_merge_count++;
2089 per_cs_stats->m_node_reuse_after_merge_count++;
2090 return *slot;
2093 else
2094 if (logger)
2095 logger->log ("not merging new state with that of EN: %i",
2096 existing_enode->m_index);
2100 /* Impose a limit on the number of enodes per program point, and
2101 simply stop if we exceed it. */
2102 if ((int)per_point_data->m_enodes.length ()
2103 > param_analyzer_max_enodes_per_program_point)
2105 pretty_printer pp;
2106 point.print (&pp, format (false));
2107 print_enode_indices (&pp, per_point_data->m_enodes);
2108 if (logger)
2109 logger->log ("not creating enode; too many at program point: %s",
2110 pp_formatted_text (&pp));
2111 warning_at (point.get_location (), OPT_Wanalyzer_too_complex,
2112 "terminating analysis for this program point: %s",
2113 pp_formatted_text (&pp));
2114 per_point_data->m_excess_enodes++;
2115 return NULL;
2118 ps.validate (m_ext_state);
2120 /* An exploded_node for "ps" doesn't already exist; create one. */
2121 exploded_node *node = new exploded_node (ps, m_nodes.length ());
2122 add_node (node);
2123 m_point_and_state_to_node.put (node->get_ps_key (), node);
2125 /* Update per-program_point data. */
2126 per_point_data->m_enodes.safe_push (node);
2128 const enum point_kind node_pk = node->get_point ().get_kind ();
2129 m_global_stats.m_num_nodes[node_pk]++;
2130 per_fn_stats->m_num_nodes[node_pk]++;
2131 per_cs_stats->m_num_nodes[node_pk]++;
2133 if (node_pk == PK_AFTER_SUPERNODE)
2134 m_PK_AFTER_SUPERNODE_per_snode[point.get_supernode ()->m_index]++;
2136 if (logger)
2138 format f (false);
2139 pretty_printer *pp = logger->get_printer ();
2140 logger->log ("created EN: %i", node->m_index);
2141 logger->start_log_line ();
2142 pp_string (pp, "point: ");
2143 point.print (pp, f);
2144 logger->end_log_line ();
2145 logger->start_log_line ();
2146 pp_string (pp, "pruned_state: ");
2147 pruned_state.dump_to_pp (m_ext_state, true, false, pp);
2148 logger->end_log_line ();
2151 /* Add the new node to the worlist. */
2152 m_worklist.add_node (node);
2153 return node;
2156 /* Add an exploded_edge from SRC to DEST, recording its association
2157 with SEDGE (which may be NULL), and, if non-NULL, taking ownership
2158 of REWIND_INFO.
2159 Return the newly-created eedge. */
2161 exploded_edge *
2162 exploded_graph::add_edge (exploded_node *src, exploded_node *dest,
2163 const superedge *sedge,
2164 exploded_edge::custom_info_t *custom_info)
2166 if (get_logger ())
2167 get_logger ()->log ("creating edge EN: %i -> EN: %i",
2168 src->m_index, dest->m_index);
2169 exploded_edge *e = new exploded_edge (src, dest, sedge, custom_info);
2170 digraph<eg_traits>::add_edge (e);
2171 return e;
2174 /* Ensure that this graph has per-program_point-data for POINT;
2175 borrow a pointer to it. */
2177 per_program_point_data *
2178 exploded_graph::
2179 get_or_create_per_program_point_data (const program_point &point)
2181 if (per_program_point_data **slot = m_per_point_data.get (&point))
2182 return *slot;
2184 per_program_point_data *per_point_data = new per_program_point_data (point);
2185 m_per_point_data.put (&per_point_data->m_key, per_point_data);
2186 return per_point_data;
2189 /* Get this graph's per-program-point-data for POINT if there is any,
2190 otherwise NULL. */
2192 per_program_point_data *
2193 exploded_graph::get_per_program_point_data (const program_point &point) const
2195 if (per_program_point_data **slot
2196 = const_cast <point_map_t &> (m_per_point_data).get (&point))
2197 return *slot;
2199 return NULL;
2202 /* Ensure that this graph has per-call_string-data for CS;
2203 borrow a pointer to it. */
2205 per_call_string_data *
2206 exploded_graph::get_or_create_per_call_string_data (const call_string &cs)
2208 if (per_call_string_data **slot = m_per_call_string_data.get (&cs))
2209 return *slot;
2211 per_call_string_data *data = new per_call_string_data (cs, m_sg.num_nodes ());
2212 m_per_call_string_data.put (&data->m_key,
2213 data);
2214 return data;
2217 /* Ensure that this graph has per-function-data for FUN;
2218 borrow a pointer to it. */
2220 per_function_data *
2221 exploded_graph::get_or_create_per_function_data (function *fun)
2223 if (per_function_data **slot = m_per_function_data.get (fun))
2224 return *slot;
2226 per_function_data *data = new per_function_data ();
2227 m_per_function_data.put (fun, data);
2228 return data;
2231 /* Get this graph's per-function-data for FUN if there is any,
2232 otherwise NULL. */
2234 per_function_data *
2235 exploded_graph::get_per_function_data (function *fun) const
2237 if (per_function_data **slot
2238 = const_cast <per_function_data_t &> (m_per_function_data).get (fun))
2239 return *slot;
2241 return NULL;
2244 /* Return true if NODE and FUN should be traversed directly, rather than
2245 called via other functions. */
2247 static bool
2248 toplevel_function_p (cgraph_node *node, function *fun, logger *logger)
2250 /* TODO: better logic here
2251 e.g. only if more than one caller, and significantly complicated.
2252 Perhaps some whole-callgraph analysis to decide if it's worth summarizing
2253 an edge, and if so, we need summaries. */
2254 if (flag_analyzer_call_summaries)
2256 int num_call_sites = 0;
2257 for (cgraph_edge *edge = node->callers; edge; edge = edge->next_caller)
2258 ++num_call_sites;
2260 /* For now, if there's more than one in-edge, and we want call
2261 summaries, do it at the top level so that there's a chance
2262 we'll have a summary when we need one. */
2263 if (num_call_sites > 1)
2265 if (logger)
2266 logger->log ("traversing %qE (%i call sites)",
2267 fun->decl, num_call_sites);
2268 return true;
2272 if (!TREE_PUBLIC (fun->decl))
2274 if (logger)
2275 logger->log ("not traversing %qE (static)", fun->decl);
2276 return false;
2279 if (logger)
2280 logger->log ("traversing %qE (all checks passed)", fun->decl);
2282 return true;
2285 /* Callback for walk_tree for finding callbacks within initializers;
2286 ensure they are treated as possible entrypoints to the analysis. */
2288 static tree
2289 add_any_callbacks (tree *tp, int *, void *data)
2291 exploded_graph *eg = (exploded_graph *)data;
2292 if (TREE_CODE (*tp) == FUNCTION_DECL)
2293 eg->on_escaped_function (*tp);
2294 return NULL_TREE;
2297 /* Add initial nodes to EG, with entrypoints for externally-callable
2298 functions. */
2300 void
2301 exploded_graph::build_initial_worklist ()
2303 logger * const logger = get_logger ();
2304 LOG_SCOPE (logger);
2306 cgraph_node *node;
2307 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
2309 function *fun = node->get_fun ();
2310 if (!toplevel_function_p (node, fun, logger))
2311 continue;
2312 exploded_node *enode = add_function_entry (fun);
2313 if (logger)
2315 if (enode)
2316 logger->log ("created EN %i for %qE entrypoint",
2317 enode->m_index, fun->decl);
2318 else
2319 logger->log ("did not create enode for %qE entrypoint", fun->decl);
2323 /* Find callbacks that are reachable from global initializers. */
2324 varpool_node *vpnode;
2325 FOR_EACH_VARIABLE (vpnode)
2327 tree decl = vpnode->decl;
2328 if (!TREE_PUBLIC (decl))
2329 continue;
2330 tree init = DECL_INITIAL (decl);
2331 if (!init)
2332 continue;
2333 walk_tree (&init, add_any_callbacks, this, NULL);
2337 /* The main loop of the analysis.
2338 Take freshly-created exploded_nodes from the worklist, calling
2339 process_node on them to explore the <point, state> graph.
2340 Add edges to their successors, potentially creating new successors
2341 (which are also added to the worklist). */
2343 void
2344 exploded_graph::process_worklist ()
2346 logger * const logger = get_logger ();
2347 LOG_SCOPE (logger);
2348 auto_timevar tv (TV_ANALYZER_WORKLIST);
2350 while (m_worklist.length () > 0)
2352 exploded_node *node = m_worklist.take_next ();
2353 gcc_assert (node->get_status () == exploded_node::STATUS_WORKLIST);
2354 gcc_assert (node->m_succs.length () == 0
2355 || node == m_origin);
2357 if (logger)
2358 logger->log ("next to process: EN: %i", node->m_index);
2360 /* If we have a run of nodes that are before-supernode, try merging and
2361 processing them together, rather than pairwise or individually. */
2362 if (flag_analyzer_state_merge && node != m_origin)
2363 if (maybe_process_run_of_before_supernode_enodes (node))
2364 goto handle_limit;
2366 /* Avoid exponential explosions of nodes by attempting to merge
2367 nodes that are at the same program point and which have
2368 sufficiently similar state. */
2369 if (flag_analyzer_state_merge && node != m_origin)
2370 if (exploded_node *node_2 = m_worklist.peek_next ())
2372 gcc_assert (node_2->get_status ()
2373 == exploded_node::STATUS_WORKLIST);
2374 gcc_assert (node->m_succs.length () == 0);
2375 gcc_assert (node_2->m_succs.length () == 0);
2377 gcc_assert (node != node_2);
2379 if (logger)
2380 logger->log ("peek worklist: EN: %i", node_2->m_index);
2382 if (node->get_point () == node_2->get_point ())
2384 const program_point &point = node->get_point ();
2385 if (logger)
2387 format f (false);
2388 pretty_printer *pp = logger->get_printer ();
2389 logger->start_log_line ();
2390 logger->log_partial
2391 ("got potential merge EN: %i and EN: %i at ",
2392 node->m_index, node_2->m_index);
2393 point.print (pp, f);
2394 logger->end_log_line ();
2396 const program_state &state = node->get_state ();
2397 const program_state &state_2 = node_2->get_state ();
2399 /* They shouldn't be equal, or we wouldn't have two
2400 separate nodes. */
2401 gcc_assert (state != state_2);
2403 program_state merged_state (m_ext_state);
2404 if (state.can_merge_with_p (state_2, point, &merged_state))
2406 if (logger)
2407 logger->log ("merging EN: %i and EN: %i",
2408 node->m_index, node_2->m_index);
2410 if (merged_state == state)
2412 /* Then merge node_2 into node by adding an edge. */
2413 add_edge (node_2, node, NULL);
2415 /* Remove node_2 from the worklist. */
2416 m_worklist.take_next ();
2417 node_2->set_status (exploded_node::STATUS_MERGER);
2419 /* Continue processing "node" below. */
2421 else if (merged_state == state_2)
2423 /* Then merge node into node_2, and leave node_2
2424 in the worklist, to be processed on the next
2425 iteration. */
2426 add_edge (node, node_2, NULL);
2427 node->set_status (exploded_node::STATUS_MERGER);
2428 continue;
2430 else
2432 /* We have a merged state that differs from
2433 both state and state_2. */
2435 /* Remove node_2 from the worklist. */
2436 m_worklist.take_next ();
2438 /* Create (or get) an exploded node for the merged
2439 states, adding to the worklist. */
2440 exploded_node *merged_enode
2441 = get_or_create_node (node->get_point (),
2442 merged_state, node);
2443 if (merged_enode == NULL)
2444 continue;
2446 if (logger)
2447 logger->log ("merged EN: %i and EN: %i into EN: %i",
2448 node->m_index, node_2->m_index,
2449 merged_enode->m_index);
2451 /* "node" and "node_2" have both now been removed
2452 from the worklist; we should not process them.
2454 "merged_enode" may be a new node; if so it will be
2455 processed in a subsequent iteration.
2456 Alternatively, "merged_enode" could be an existing
2457 node; one way the latter can
2458 happen is if we end up merging a succession of
2459 similar nodes into one. */
2461 /* If merged_node is one of the two we were merging,
2462 add it back to the worklist to ensure it gets
2463 processed.
2465 Add edges from the merged nodes to it (but not a
2466 self-edge). */
2467 if (merged_enode == node)
2468 m_worklist.add_node (merged_enode);
2469 else
2471 add_edge (node, merged_enode, NULL);
2472 node->set_status (exploded_node::STATUS_MERGER);
2475 if (merged_enode == node_2)
2476 m_worklist.add_node (merged_enode);
2477 else
2479 add_edge (node_2, merged_enode, NULL);
2480 node_2->set_status (exploded_node::STATUS_MERGER);
2483 continue;
2487 /* TODO: should we attempt more than two nodes,
2488 or just do pairs of nodes? (and hope that we get
2489 a cascade of mergers). */
2493 process_node (node);
2495 handle_limit:
2496 /* Impose a hard limit on the number of exploded nodes, to ensure
2497 that the analysis terminates in the face of pathological state
2498 explosion (or bugs).
2500 Specifically, the limit is on the number of PK_AFTER_SUPERNODE
2501 exploded nodes, looking at supernode exit events.
2503 We use exit rather than entry since there can be multiple
2504 entry ENs, one per phi; the number of PK_AFTER_SUPERNODE ought
2505 to be equivalent to the number of supernodes multiplied by the
2506 number of states. */
2507 const int limit = m_sg.num_nodes () * param_analyzer_bb_explosion_factor;
2508 if (m_global_stats.m_num_nodes[PK_AFTER_SUPERNODE] > limit)
2510 if (logger)
2511 logger->log ("bailing out; too many nodes");
2512 warning_at (node->get_point ().get_location (),
2513 OPT_Wanalyzer_too_complex,
2514 "analysis bailed out early"
2515 " (%i 'after-snode' enodes; %i enodes)",
2516 m_global_stats.m_num_nodes[PK_AFTER_SUPERNODE],
2517 m_nodes.length ());
2518 return;
2523 /* Attempt to process a consecutive run of sufficiently-similar nodes in
2524 the worklist at a CFG join-point (having already popped ENODE from the
2525 head of the worklist).
2527 If ENODE's point is of the form (before-supernode, SNODE) and the next
2528 nodes in the worklist are a consecutive run of enodes of the same form,
2529 for the same supernode as ENODE (but potentially from different in-edges),
2530 process them all together, setting their status to STATUS_BULK_MERGED,
2531 and return true.
2532 Otherwise, return false, in which case ENODE must be processed in the
2533 normal way.
2535 When processing them all together, generate successor states based
2536 on phi nodes for the appropriate CFG edges, and then attempt to merge
2537 these states into a minimal set of merged successor states, partitioning
2538 the inputs by merged successor state.
2540 Create new exploded nodes for all of the merged states, and add edges
2541 connecting the input enodes to the corresponding merger exploded nodes.
2543 We hope we have a much smaller number of merged successor states
2544 compared to the number of input enodes - ideally just one,
2545 if all successor states can be merged.
2547 Processing and merging many together as one operation rather than as
2548 pairs avoids scaling issues where per-pair mergers could bloat the
2549 graph with merger nodes (especially so after switch statements). */
2551 bool
2552 exploded_graph::
2553 maybe_process_run_of_before_supernode_enodes (exploded_node *enode)
2555 /* A struct for tracking per-input state. */
2556 struct item
2558 item (exploded_node *input_enode)
2559 : m_input_enode (input_enode),
2560 m_processed_state (input_enode->get_state ()),
2561 m_merger_idx (-1)
2564 exploded_node *m_input_enode;
2565 program_state m_processed_state;
2566 int m_merger_idx;
2569 gcc_assert (enode->get_status () == exploded_node::STATUS_WORKLIST);
2570 gcc_assert (enode->m_succs.length () == 0);
2572 const program_point &point = enode->get_point ();
2574 if (point.get_kind () != PK_BEFORE_SUPERNODE)
2575 return false;
2577 const supernode *snode = point.get_supernode ();
2579 logger * const logger = get_logger ();
2580 LOG_SCOPE (logger);
2582 /* Find a run of enodes in the worklist that are before the same supernode,
2583 but potentially from different in-edges. */
2584 auto_vec <exploded_node *> enodes;
2585 enodes.safe_push (enode);
2586 while (exploded_node *enode_2 = m_worklist.peek_next ())
2588 gcc_assert (enode_2->get_status ()
2589 == exploded_node::STATUS_WORKLIST);
2590 gcc_assert (enode_2->m_succs.length () == 0);
2592 const program_point &point_2 = enode_2->get_point ();
2594 if (point_2.get_kind () == PK_BEFORE_SUPERNODE
2595 && point_2.get_supernode () == snode
2596 && point_2.get_call_string () == point.get_call_string ())
2598 enodes.safe_push (enode_2);
2599 m_worklist.take_next ();
2601 else
2602 break;
2605 /* If the only node is ENODE, then give up. */
2606 if (enodes.length () == 1)
2607 return false;
2609 if (logger)
2610 logger->log ("got run of %i enodes for SN: %i",
2611 enodes.length (), snode->m_index);
2613 /* All of these enodes have a shared successor point (even if they
2614 were for different in-edges). */
2615 program_point next_point (point.get_next ());
2617 /* Calculate the successor state for each enode in enodes. */
2618 auto_delete_vec<item> items (enodes.length ());
2619 unsigned i;
2620 exploded_node *iter_enode;
2621 FOR_EACH_VEC_ELT (enodes, i, iter_enode)
2623 item *it = new item (iter_enode);
2624 items.quick_push (it);
2625 const program_state &state = iter_enode->get_state ();
2626 program_state *next_state = &it->m_processed_state;
2627 const program_point &iter_point = iter_enode->get_point ();
2628 if (const superedge *iter_sedge = iter_point.get_from_edge ())
2630 impl_region_model_context ctxt (*this, iter_enode,
2631 &state, next_state, NULL);
2632 const cfg_superedge *last_cfg_superedge
2633 = iter_sedge->dyn_cast_cfg_superedge ();
2634 if (last_cfg_superedge)
2635 next_state->m_region_model->update_for_phis
2636 (snode, last_cfg_superedge, &ctxt);
2640 /* Attempt to partition the items into a set of merged states.
2641 We hope we have a much smaller number of merged states
2642 compared to the number of input enodes - ideally just one,
2643 if all can be merged. */
2644 auto_delete_vec <program_state> merged_states;
2645 auto_vec<item *> first_item_for_each_merged_state;
2646 item *it;
2647 FOR_EACH_VEC_ELT (items, i, it)
2649 const program_state &it_state = it->m_processed_state;
2650 program_state *merged_state;
2651 unsigned iter_merger_idx;
2652 FOR_EACH_VEC_ELT (merged_states, iter_merger_idx, merged_state)
2654 program_state merge (m_ext_state);
2655 if (it_state.can_merge_with_p (*merged_state, next_point, &merge))
2657 *merged_state = merge;
2658 it->m_merger_idx = iter_merger_idx;
2659 if (logger)
2660 logger->log ("reusing merger state %i for item %i (EN: %i)",
2661 it->m_merger_idx, i, it->m_input_enode->m_index);
2662 goto got_merger;
2665 /* If it couldn't be merged with any existing merged_states,
2666 create a new one. */
2667 if (it->m_merger_idx == -1)
2669 it->m_merger_idx = merged_states.length ();
2670 merged_states.safe_push (new program_state (it_state));
2671 first_item_for_each_merged_state.safe_push (it);
2672 if (logger)
2673 logger->log ("using new merger state %i for item %i (EN: %i)",
2674 it->m_merger_idx, i, it->m_input_enode->m_index);
2676 got_merger:
2677 gcc_assert (it->m_merger_idx >= 0);
2678 gcc_assert ((unsigned)it->m_merger_idx < merged_states.length ());
2681 /* Create merger nodes. */
2682 auto_vec<exploded_node *> next_enodes (merged_states.length ());
2683 program_state *merged_state;
2684 FOR_EACH_VEC_ELT (merged_states, i, merged_state)
2686 exploded_node *src_enode
2687 = first_item_for_each_merged_state[i]->m_input_enode;
2688 exploded_node *next
2689 = get_or_create_node (next_point, *merged_state, src_enode);
2690 /* "next" could be NULL; we handle that when adding the edges below. */
2691 next_enodes.quick_push (next);
2692 if (logger)
2694 if (next)
2695 logger->log ("using EN: %i for merger state %i", next->m_index, i);
2696 else
2697 logger->log ("using NULL enode for merger state %i", i);
2701 /* Create edges from each input enode to the appropriate successor enode.
2702 Update the status of the now-processed input enodes. */
2703 FOR_EACH_VEC_ELT (items, i, it)
2705 exploded_node *next = next_enodes[it->m_merger_idx];
2706 if (next)
2707 add_edge (it->m_input_enode, next, NULL);
2708 it->m_input_enode->set_status (exploded_node::STATUS_BULK_MERGED);
2711 if (logger)
2712 logger->log ("merged %i in-enodes into %i out-enode(s) at SN: %i",
2713 items.length (), merged_states.length (), snode->m_index);
2715 return true;
2718 /* Return true if STMT must appear at the start of its exploded node, and
2719 thus we can't consolidate its effects within a run of other statements,
2720 where PREV_STMT was the previous statement. */
2722 static bool
2723 stmt_requires_new_enode_p (const gimple *stmt,
2724 const gimple *prev_stmt)
2726 if (const gcall *call = dyn_cast <const gcall *> (stmt))
2728 /* Stop consolidating at calls to
2729 "__analyzer_dump_exploded_nodes", so they always appear at the
2730 start of an exploded_node. */
2731 if (is_special_named_call_p (call, "__analyzer_dump_exploded_nodes",
2733 return true;
2735 /* sm-signal.cc injects an additional custom eedge at "signal" calls
2736 from the registration enode to the handler enode, separate from the
2737 regular next state, which defeats the "detect state change" logic
2738 in process_node. Work around this via special-casing, to ensure
2739 we split the enode immediately before any "signal" call. */
2740 if (is_special_named_call_p (call, "signal", 2))
2741 return true;
2744 /* If we had a PREV_STMT with an unknown location, and this stmt
2745 has a known location, then if a state change happens here, it
2746 could be consolidated into PREV_STMT, giving us an event with
2747 no location. Ensure that STMT gets its own exploded_node to
2748 avoid this. */
2749 if (get_pure_location (prev_stmt->location) == UNKNOWN_LOCATION
2750 && get_pure_location (stmt->location) != UNKNOWN_LOCATION)
2751 return true;
2753 return false;
2756 /* The core of exploded_graph::process_worklist (the main analysis loop),
2757 handling one node in the worklist.
2759 Get successor <point, state> pairs for NODE, calling get_or_create on
2760 them, and adding an exploded_edge to each successors.
2762 Freshly-created nodes will be added to the worklist. */
2764 void
2765 exploded_graph::process_node (exploded_node *node)
2767 logger * const logger = get_logger ();
2768 LOG_FUNC_1 (logger, "EN: %i", node->m_index);
2770 node->set_status (exploded_node::STATUS_PROCESSED);
2772 const program_point &point = node->get_point ();
2774 /* Update cfun and input_location in case of an ICE: make it easier to
2775 track down which source construct we're failing to handle. */
2776 auto_cfun sentinel (node->get_function ());
2777 const gimple *stmt = point.get_stmt ();
2778 if (stmt)
2779 input_location = stmt->location;
2781 const program_state &state = node->get_state ();
2782 if (logger)
2784 pretty_printer *pp = logger->get_printer ();
2785 logger->start_log_line ();
2786 pp_string (pp, "point: ");
2787 point.print (pp, format (false));
2788 pp_string (pp, ", state: ");
2789 state.dump_to_pp (m_ext_state, true, false, pp);
2790 logger->end_log_line ();
2793 switch (point.get_kind ())
2795 default:
2796 gcc_unreachable ();
2797 case PK_ORIGIN:
2798 /* This node exists to simplify finding the shortest path
2799 to an exploded_node. */
2800 break;
2802 case PK_BEFORE_SUPERNODE:
2804 program_state next_state (state);
2806 if (point.get_from_edge ())
2808 impl_region_model_context ctxt (*this, node,
2809 &state, &next_state, NULL);
2810 const cfg_superedge *last_cfg_superedge
2811 = point.get_from_edge ()->dyn_cast_cfg_superedge ();
2812 if (last_cfg_superedge)
2813 next_state.m_region_model->update_for_phis
2814 (node->get_supernode (),
2815 last_cfg_superedge,
2816 &ctxt);
2819 program_point next_point (point.get_next ());
2820 exploded_node *next = get_or_create_node (next_point, next_state, node);
2821 if (next)
2822 add_edge (node, next, NULL);
2824 break;
2825 case PK_BEFORE_STMT:
2827 /* Determine the effect of a run of one or more statements
2828 within one supernode, generating an edge to the program_point
2829 after the last statement that's processed.
2831 Stop iterating statements and thus consolidating into one enode
2832 when:
2833 - reaching the end of the statements in the supernode
2834 - if an sm-state-change occurs (so that it gets its own
2835 exploded_node)
2836 - if "-fanalyzer-fine-grained" is active
2837 - encountering certain statements must appear at the start of
2838 their enode (for which stmt_requires_new_enode_p returns true)
2840 Update next_state in-place, to get the result of the one
2841 or more stmts that are processed.
2843 Split the node in-place if an sm-state-change occurs, so that
2844 the sm-state-change occurs on an edge where the src enode has
2845 exactly one stmt, the one that caused the change. */
2846 program_state next_state (state);
2847 const supernode *snode = point.get_supernode ();
2848 unsigned stmt_idx;
2849 const gimple *prev_stmt = NULL;
2850 for (stmt_idx = point.get_stmt_idx ();
2851 stmt_idx < snode->m_stmts.length ();
2852 stmt_idx++)
2854 const gimple *stmt = snode->m_stmts[stmt_idx];
2856 if (stmt_idx > point.get_stmt_idx ())
2857 if (stmt_requires_new_enode_p (stmt, prev_stmt))
2859 stmt_idx--;
2860 break;
2862 prev_stmt = stmt;
2864 program_state old_state (next_state);
2866 /* Process the stmt. */
2867 exploded_node::on_stmt_flags flags
2868 = node->on_stmt (*this, snode, stmt, &next_state);
2869 node->m_num_processed_stmts++;
2871 /* If flags.m_terminate_path, stop analyzing; any nodes/edges
2872 will have been added by on_stmt (e.g. for handling longjmp). */
2873 if (flags.m_terminate_path)
2874 return;
2876 if (next_state.m_region_model)
2878 impl_region_model_context ctxt (*this, node,
2879 &old_state, &next_state, stmt);
2880 program_state::detect_leaks (old_state, next_state, NULL,
2881 get_ext_state (), &ctxt);
2884 unsigned next_idx = stmt_idx + 1;
2885 program_point next_point
2886 = (next_idx < point.get_supernode ()->m_stmts.length ()
2887 ? program_point::before_stmt (point.get_supernode (), next_idx,
2888 point.get_call_string ())
2889 : program_point::after_supernode (point.get_supernode (),
2890 point.get_call_string ()));
2891 next_state = next_state.prune_for_point (*this, next_point, node);
2893 if (flags.m_sm_changes || flag_analyzer_fine_grained)
2895 program_point split_point
2896 = program_point::before_stmt (point.get_supernode (),
2897 stmt_idx,
2898 point.get_call_string ());
2899 if (split_point != node->get_point ())
2901 /* If we're not at the start of NODE, split the enode at
2902 this stmt, so we have:
2903 node -> split_enode
2904 so that when split_enode is processed the next edge
2905 we add will be:
2906 split_enode -> next
2907 and any state change will effectively occur on that
2908 latter edge, and split_enode will contain just stmt. */
2909 if (logger)
2910 logger->log ("getting split_enode");
2911 exploded_node *split_enode
2912 = get_or_create_node (split_point, old_state, node);
2913 if (!split_enode)
2914 return;
2915 /* "stmt" will be reprocessed when split_enode is
2916 processed. */
2917 node->m_num_processed_stmts--;
2918 if (logger)
2919 logger->log ("creating edge to split_enode");
2920 add_edge (node, split_enode, NULL);
2921 return;
2923 else
2924 /* If we're at the start of NODE, stop iterating,
2925 so that an edge will be created from NODE to
2926 (next_point, next_state) below. */
2927 break;
2930 unsigned next_idx = stmt_idx + 1;
2931 program_point next_point
2932 = (next_idx < point.get_supernode ()->m_stmts.length ()
2933 ? program_point::before_stmt (point.get_supernode (), next_idx,
2934 point.get_call_string ())
2935 : program_point::after_supernode (point.get_supernode (),
2936 point.get_call_string ()));
2937 exploded_node *next = get_or_create_node (next_point, next_state, node);
2938 if (next)
2939 add_edge (node, next, NULL);
2941 break;
2942 case PK_AFTER_SUPERNODE:
2944 /* If this is an EXIT BB, detect leaks, and potentially
2945 create a function summary. */
2946 if (point.get_supernode ()->return_p ())
2948 node->detect_leaks (*this);
2949 if (flag_analyzer_call_summaries
2950 && point.get_call_string ().empty_p ())
2952 /* TODO: create function summary
2953 There can be more than one; each corresponds to a different
2954 final enode in the function. */
2955 if (logger)
2957 pretty_printer *pp = logger->get_printer ();
2958 logger->start_log_line ();
2959 logger->log_partial
2960 ("would create function summary for %qE; state: ",
2961 point.get_fndecl ());
2962 state.dump_to_pp (m_ext_state, true, false, pp);
2963 logger->end_log_line ();
2965 per_function_data *per_fn_data
2966 = get_or_create_per_function_data (point.get_function ());
2967 per_fn_data->add_call_summary (node);
2970 /* Traverse into successors of the supernode. */
2971 int i;
2972 superedge *succ;
2973 FOR_EACH_VEC_ELT (point.get_supernode ()->m_succs, i, succ)
2975 if (logger)
2976 logger->log ("considering SN: %i -> SN: %i",
2977 succ->m_src->m_index, succ->m_dest->m_index);
2979 program_point next_point
2980 = program_point::before_supernode (succ->m_dest, succ,
2981 point.get_call_string ());
2982 program_state next_state (state);
2984 if (!node->on_edge (*this, succ, &next_point, &next_state))
2986 if (logger)
2987 logger->log ("skipping impossible edge to SN: %i",
2988 succ->m_dest->m_index);
2989 continue;
2992 exploded_node *next = get_or_create_node (next_point, next_state,
2993 node);
2994 if (next)
2995 add_edge (node, next, succ);
2998 break;
3002 /* Ensure that this graph has a stats instance for FN, return it.
3003 FN can be NULL, in which case a stats instances is returned covering
3004 "functionless" parts of the graph (the origin node). */
3006 stats *
3007 exploded_graph::get_or_create_function_stats (function *fn)
3009 if (!fn)
3010 return &m_functionless_stats;
3012 if (stats **slot = m_per_function_stats.get (fn))
3013 return *slot;
3014 else
3016 int num_supernodes = fn ? n_basic_blocks_for_fn (fn) : 0;
3017 /* not quite the num supernodes, but nearly. */
3018 stats *new_stats = new stats (num_supernodes);
3019 m_per_function_stats.put (fn, new_stats);
3020 return new_stats;
3024 /* Print bar charts to PP showing:
3025 - the number of enodes per function, and
3026 - for each function:
3027 - the number of enodes per supernode/BB
3028 - the number of excess enodes per supernode/BB beyond the
3029 per-program-point limit, if there were any. */
3031 void
3032 exploded_graph::print_bar_charts (pretty_printer *pp) const
3034 cgraph_node *cgnode;
3036 pp_string (pp, "enodes per function:");
3037 pp_newline (pp);
3038 bar_chart enodes_per_function;
3039 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode)
3041 function *fn = cgnode->get_fun ();
3042 const stats * const *s_ptr
3043 = const_cast <function_stat_map_t &> (m_per_function_stats).get (fn);
3044 enodes_per_function.add_item (function_name (fn),
3045 s_ptr ? (*s_ptr)->get_total_enodes () : 0);
3047 enodes_per_function.print (pp);
3049 /* Accumulate number of enodes per supernode. */
3050 auto_vec<unsigned> enodes_per_supernode (m_sg.num_nodes ());
3051 for (int i = 0; i < m_sg.num_nodes (); i++)
3052 enodes_per_supernode.quick_push (0);
3053 int i;
3054 exploded_node *enode;
3055 FOR_EACH_VEC_ELT (m_nodes, i, enode)
3057 const supernode *iter_snode = enode->get_supernode ();
3058 if (!iter_snode)
3059 continue;
3060 enodes_per_supernode[iter_snode->m_index]++;
3063 /* Accumulate excess enodes per supernode. */
3064 auto_vec<unsigned> excess_enodes_per_supernode (m_sg.num_nodes ());
3065 for (int i = 0; i < m_sg.num_nodes (); i++)
3066 excess_enodes_per_supernode.quick_push (0);
3067 for (point_map_t::iterator iter = m_per_point_data.begin ();
3068 iter != m_per_point_data.end (); ++iter)
3070 const program_point *point = (*iter).first;
3071 const supernode *iter_snode = point->get_supernode ();
3072 if (!iter_snode)
3073 continue;
3074 const per_program_point_data *point_data = (*iter).second;
3075 excess_enodes_per_supernode[iter_snode->m_index]
3076 += point_data->m_excess_enodes;
3079 /* Show per-function bar_charts of enodes per supernode/BB. */
3080 pp_string (pp, "per-function enodes per supernode/BB:");
3081 pp_newline (pp);
3082 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode)
3084 function *fn = cgnode->get_fun ();
3085 pp_printf (pp, "function: %qs", function_name (fn));
3086 pp_newline (pp);
3088 bar_chart enodes_per_snode;
3089 bar_chart excess_enodes_per_snode;
3090 bool have_excess_enodes = false;
3091 for (int i = 0; i < m_sg.num_nodes (); i++)
3093 const supernode *iter_snode = m_sg.get_node_by_index (i);
3094 if (iter_snode->get_function () != fn)
3095 continue;
3096 pretty_printer tmp_pp;
3097 pp_printf (&tmp_pp, "sn %i (bb %i)",
3098 iter_snode->m_index, iter_snode->m_bb->index);
3099 enodes_per_snode.add_item (pp_formatted_text (&tmp_pp),
3100 enodes_per_supernode[iter_snode->m_index]);
3101 const int num_excess
3102 = excess_enodes_per_supernode[iter_snode->m_index];
3103 excess_enodes_per_snode.add_item (pp_formatted_text (&tmp_pp),
3104 num_excess);
3105 if (num_excess)
3106 have_excess_enodes = true;
3108 enodes_per_snode.print (pp);
3109 if (have_excess_enodes)
3111 pp_printf (pp, "EXCESS ENODES:");
3112 pp_newline (pp);
3113 excess_enodes_per_snode.print (pp);
3118 /* Write all stats information to this graph's logger, if any. */
3120 void
3121 exploded_graph::log_stats () const
3123 logger * const logger = get_logger ();
3124 if (!logger)
3125 return;
3127 LOG_SCOPE (logger);
3129 m_ext_state.get_engine ()->log_stats (logger);
3131 logger->log ("m_sg.num_nodes (): %i", m_sg.num_nodes ());
3132 logger->log ("m_nodes.length (): %i", m_nodes.length ());
3133 logger->log ("m_edges.length (): %i", m_edges.length ());
3134 logger->log ("remaining enodes in worklist: %i", m_worklist.length ());
3136 logger->log ("global stats:");
3137 m_global_stats.log (logger);
3139 for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
3140 iter != m_per_function_stats.end ();
3141 ++iter)
3143 function *fn = (*iter).first;
3144 log_scope s (logger, function_name (fn));
3145 (*iter).second->log (logger);
3148 print_bar_charts (logger->get_printer ());
3151 /* Dump all stats information to OUT. */
3153 void
3154 exploded_graph::dump_stats (FILE *out) const
3156 fprintf (out, "m_sg.num_nodes (): %i\n", m_sg.num_nodes ());
3157 fprintf (out, "m_nodes.length (): %i\n", m_nodes.length ());
3158 fprintf (out, "m_edges.length (): %i\n", m_edges.length ());
3159 fprintf (out, "remaining enodes in worklist: %i", m_worklist.length ());
3161 fprintf (out, "global stats:\n");
3162 m_global_stats.dump (out);
3164 for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
3165 iter != m_per_function_stats.end ();
3166 ++iter)
3168 function *fn = (*iter).first;
3169 fprintf (out, "function: %s\n", function_name (fn));
3170 (*iter).second->dump (out);
3173 fprintf (out, "PK_AFTER_SUPERNODE per supernode:\n");
3174 for (unsigned i = 0; i < m_PK_AFTER_SUPERNODE_per_snode.length (); i++)
3175 fprintf (out, " SN %i: %3i\n", i, m_PK_AFTER_SUPERNODE_per_snode[i]);
3178 void
3179 exploded_graph::dump_states_for_supernode (FILE *out,
3180 const supernode *snode) const
3182 fprintf (out, "PK_AFTER_SUPERNODE nodes for SN: %i\n", snode->m_index);
3183 int i;
3184 exploded_node *enode;
3185 int state_idx = 0;
3186 FOR_EACH_VEC_ELT (m_nodes, i, enode)
3188 const supernode *iter_snode = enode->get_supernode ();
3189 if (enode->get_point ().get_kind () == PK_AFTER_SUPERNODE
3190 && iter_snode == snode)
3192 pretty_printer pp;
3193 pp_format_decoder (&pp) = default_tree_printer;
3194 enode->get_state ().dump_to_pp (m_ext_state, true, false, &pp);
3195 fprintf (out, "state %i: EN: %i\n %s\n",
3196 state_idx++, enode->m_index,
3197 pp_formatted_text (&pp));
3200 fprintf (out, "#exploded_node for PK_AFTER_SUPERNODE for SN: %i = %i\n",
3201 snode->m_index, state_idx);
3204 /* Return a new json::object of the form
3205 {"nodes" : [objs for enodes],
3206 "edges" : [objs for eedges],
3207 "ext_state": object for extrinsic_state,
3208 "diagnostic_manager": object for diagnostic_manager}. */
3210 json::object *
3211 exploded_graph::to_json () const
3213 json::object *egraph_obj = new json::object ();
3215 /* Nodes. */
3217 json::array *nodes_arr = new json::array ();
3218 unsigned i;
3219 exploded_node *n;
3220 FOR_EACH_VEC_ELT (m_nodes, i, n)
3221 nodes_arr->append (n->to_json (m_ext_state));
3222 egraph_obj->set ("nodes", nodes_arr);
3225 /* Edges. */
3227 json::array *edges_arr = new json::array ();
3228 unsigned i;
3229 exploded_edge *n;
3230 FOR_EACH_VEC_ELT (m_edges, i, n)
3231 edges_arr->append (n->to_json ());
3232 egraph_obj->set ("edges", edges_arr);
3235 /* m_sg is JSONified at the top-level. */
3237 egraph_obj->set ("ext_state", m_ext_state.to_json ());
3238 egraph_obj->set ("diagnostic_manager", m_diagnostic_manager.to_json ());
3240 /* The following fields aren't yet being JSONified:
3241 worklist m_worklist;
3242 const state_purge_map *const m_purge_map;
3243 const analysis_plan &m_plan;
3244 stats m_global_stats;
3245 function_stat_map_t m_per_function_stats;
3246 stats m_functionless_stats;
3247 call_string_data_map_t m_per_call_string_data;
3248 auto_vec<int> m_PK_AFTER_SUPERNODE_per_snode; */
3250 return egraph_obj;
3253 /* Look for the last use of SEARCH_STMT within this path.
3254 If found write the edge's index to *OUT_IDX and return true, otherwise
3255 return false. */
3257 bool
3258 exploded_path::find_stmt_backwards (const gimple *search_stmt,
3259 int *out_idx) const
3261 int i;
3262 const exploded_edge *eedge;
3263 FOR_EACH_VEC_ELT_REVERSE (m_edges, i, eedge)
3265 const exploded_node *dst_node = eedge->m_dest;
3266 const program_point &dst_point = dst_node->get_point ();
3267 const gimple *stmt = dst_point.get_stmt ();
3268 if (stmt == search_stmt)
3270 *out_idx = i;
3271 return true;
3274 return false;
3277 /* Get the final exploded_node in this path, which must be non-empty. */
3279 exploded_node *
3280 exploded_path::get_final_enode () const
3282 gcc_assert (m_edges.length () > 0);
3283 return m_edges[m_edges.length () - 1]->m_dest;
3286 /* Check state along this path, returning true if it is feasible.
3287 If OUT is non-NULL, and the path is infeasible, write a new
3288 feasibility_problem to *OUT. */
3290 bool
3291 exploded_path::feasible_p (logger *logger, feasibility_problem **out,
3292 engine *eng, const exploded_graph *eg) const
3294 LOG_SCOPE (logger);
3296 auto_sbitmap snodes_visited (eg->get_supergraph ().m_nodes.length ());
3298 /* Traverse the path, updating this model. */
3299 region_model model (eng->get_model_manager ());
3300 for (unsigned edge_idx = 0; edge_idx < m_edges.length (); edge_idx++)
3302 const exploded_edge *eedge = m_edges[edge_idx];
3303 if (logger)
3304 logger->log ("considering edge %i: EN:%i -> EN:%i",
3305 edge_idx,
3306 eedge->m_src->m_index,
3307 eedge->m_dest->m_index);
3308 const exploded_node &src_enode = *eedge->m_src;
3309 const program_point &src_point = src_enode.get_point ();
3310 if (logger)
3312 logger->start_log_line ();
3313 src_point.print (logger->get_printer (), format (false));
3314 logger->end_log_line ();
3317 /* Update state for the stmts that were processed in each enode. */
3318 for (unsigned stmt_idx = 0; stmt_idx < src_enode.m_num_processed_stmts;
3319 stmt_idx++)
3321 const gimple *stmt = src_enode.get_processed_stmt (stmt_idx);
3323 /* Update cfun and input_location in case of ICE: make it easier to
3324 track down which source construct we're failing to handle. */
3325 auto_cfun sentinel (src_point.get_function ());
3326 input_location = stmt->location;
3328 if (const gassign *assign = dyn_cast <const gassign *> (stmt))
3329 model.on_assignment (assign, NULL);
3330 else if (const greturn *return_ = dyn_cast <const greturn *> (stmt))
3331 model.on_return (return_, NULL);
3334 const superedge *sedge = eedge->m_sedge;
3335 if (sedge)
3337 if (logger)
3338 logger->log (" sedge: SN:%i -> SN:%i %s",
3339 sedge->m_src->m_index,
3340 sedge->m_dest->m_index,
3341 sedge->get_description (false));
3343 const gimple *last_stmt = src_point.get_supernode ()->get_last_stmt ();
3344 rejected_constraint *rc = NULL;
3345 if (!model.maybe_update_for_edge (*sedge, last_stmt, NULL, &rc))
3347 if (logger)
3349 logger->log ("rejecting due to region model");
3350 model.dump_to_pp (logger->get_printer (), true, false);
3352 if (out)
3353 *out = new feasibility_problem (edge_idx, *eedge,
3354 last_stmt, rc);
3355 else
3356 delete rc;
3357 return false;
3360 else
3362 /* Special-case the initial eedge from the origin node to the
3363 initial function by pushing a frame for it. */
3364 if (edge_idx == 0)
3366 gcc_assert (eedge->m_src->m_index == 0);
3367 gcc_assert (src_point.get_kind () == PK_ORIGIN);
3368 gcc_assert (eedge->m_dest->get_point ().get_kind ()
3369 == PK_BEFORE_SUPERNODE);
3370 function *fun = eedge->m_dest->get_function ();
3371 gcc_assert (fun);
3372 model.push_frame (fun, NULL, NULL);
3373 if (logger)
3374 logger->log (" pushing frame for %qD", fun->decl);
3376 else if (eedge->m_custom_info)
3378 eedge->m_custom_info->update_model (&model, *eedge);
3382 /* Handle phi nodes on an edge leaving a PK_BEFORE_SUPERNODE (to
3383 a PK_BEFORE_STMT, or a PK_AFTER_SUPERNODE if no stmts).
3384 This will typically not be associated with a superedge. */
3385 if (src_point.get_from_edge ())
3387 const cfg_superedge *last_cfg_superedge
3388 = src_point.get_from_edge ()->dyn_cast_cfg_superedge ();
3389 const exploded_node &dst_enode = *eedge->m_dest;
3390 const unsigned dst_snode_idx = dst_enode.get_supernode ()->m_index;
3391 if (last_cfg_superedge)
3393 if (logger)
3394 logger->log (" update for phis");
3395 model.update_for_phis (src_enode.get_supernode (),
3396 last_cfg_superedge,
3397 NULL);
3398 /* If we've entering an snode that we've already visited on this
3399 epath, then we need do fix things up for loops; see the
3400 comment for store::loop_replay_fixup.
3401 Perhaps we should probably also verify the callstring,
3402 and track program_points, but hopefully doing it by supernode
3403 is good enough. */
3404 if (bitmap_bit_p (snodes_visited, dst_snode_idx))
3405 model.loop_replay_fixup (dst_enode.get_state ().m_region_model);
3407 bitmap_set_bit (snodes_visited, dst_snode_idx);
3410 if (logger)
3412 logger->log ("state after edge %i: EN:%i -> EN:%i",
3413 edge_idx,
3414 eedge->m_src->m_index,
3415 eedge->m_dest->m_index);
3416 logger->start_log_line ();
3417 model.dump_to_pp (logger->get_printer (), true, false);
3418 logger->end_log_line ();
3422 return true;
3425 /* Dump this path in multiline form to PP. */
3427 void
3428 exploded_path::dump_to_pp (pretty_printer *pp) const
3430 for (unsigned i = 0; i < m_edges.length (); i++)
3432 const exploded_edge *eedge = m_edges[i];
3433 pp_printf (pp, "m_edges[%i]: EN %i -> EN %i",
3435 eedge->m_src->m_index,
3436 eedge->m_dest->m_index);
3437 pp_newline (pp);
3441 /* Dump this path in multiline form to FP. */
3443 void
3444 exploded_path::dump (FILE *fp) const
3446 pretty_printer pp;
3447 pp_format_decoder (&pp) = default_tree_printer;
3448 pp_show_color (&pp) = pp_show_color (global_dc->printer);
3449 pp.buffer->stream = fp;
3450 dump_to_pp (&pp);
3451 pp_flush (&pp);
3454 /* Dump this path in multiline form to stderr. */
3456 DEBUG_FUNCTION void
3457 exploded_path::dump () const
3459 dump (stderr);
3462 /* class feasibility_problem. */
3464 void
3465 feasibility_problem::dump_to_pp (pretty_printer *pp) const
3467 pp_printf (pp, "edge from EN: %i to EN: %i",
3468 m_eedge.m_src->m_index, m_eedge.m_dest->m_index);
3469 if (m_rc)
3471 pp_string (pp, "; rejected constraint: ");
3472 m_rc->dump_to_pp (pp);
3473 pp_string (pp, "; rmodel: ");
3474 m_rc->m_model.dump_to_pp (pp, true, false);
3478 /* A family of cluster subclasses for use when generating .dot output for
3479 exploded graphs (-fdump-analyzer-exploded-graph), for grouping the
3480 enodes into hierarchical boxes.
3482 All functionless enodes appear in the top-level graph.
3483 Every (function, call_string) pair gets its own cluster. Within that
3484 cluster, each supernode gets its own cluster.
3486 Hence all enodes relating to a particular function with a particular
3487 callstring will be in a cluster together; all enodes for the same
3488 function but with a different callstring will be in a different
3489 cluster. */
3491 /* Base class of cluster for clustering exploded_node instances in .dot
3492 output, based on various subclass-specific criteria. */
3494 class exploded_cluster : public cluster<eg_traits>
3498 /* Cluster containing all exploded_node instances for one supernode. */
3500 class supernode_cluster : public exploded_cluster
3502 public:
3503 supernode_cluster (const supernode *supernode) : m_supernode (supernode) {}
3505 // TODO: dtor?
3507 void dump_dot (graphviz_out *gv, const dump_args_t &args) const FINAL OVERRIDE
3509 gv->println ("subgraph \"cluster_supernode_%p\" {",
3510 (const void *)this);
3511 gv->indent ();
3512 gv->println ("style=\"dashed\";");
3513 gv->println ("label=\"SN: %i (bb: %i; scc: %i)\";",
3514 m_supernode->m_index, m_supernode->m_bb->index,
3515 args.m_eg.get_scc_id (*m_supernode));
3517 int i;
3518 exploded_node *enode;
3519 FOR_EACH_VEC_ELT (m_enodes, i, enode)
3520 enode->dump_dot (gv, args);
3522 /* Terminate subgraph. */
3523 gv->outdent ();
3524 gv->println ("}");
3527 void add_node (exploded_node *en) FINAL OVERRIDE
3529 m_enodes.safe_push (en);
3532 private:
3533 const supernode *m_supernode;
3534 auto_vec <exploded_node *> m_enodes;
3537 /* Cluster containing all supernode_cluster instances for one
3538 (function, call_string) pair. */
3540 class function_call_string_cluster : public exploded_cluster
3542 public:
3543 function_call_string_cluster (function *fun, call_string cs)
3544 : m_fun (fun), m_cs (cs) {}
3546 ~function_call_string_cluster ()
3548 for (map_t::iterator iter = m_map.begin ();
3549 iter != m_map.end ();
3550 ++iter)
3551 delete (*iter).second;
3554 void dump_dot (graphviz_out *gv, const dump_args_t &args) const FINAL OVERRIDE
3556 const char *funcname = function_name (m_fun);
3558 gv->println ("subgraph \"cluster_function_%p\" {", (const void *)this);
3559 gv->indent ();
3560 gv->write_indent ();
3561 gv->print ("label=\"call string: ");
3562 m_cs.print (gv->get_pp ());
3563 gv->print (" function: %s \";", funcname);
3564 gv->print ("\n");
3566 for (map_t::iterator iter = m_map.begin ();
3567 iter != m_map.end ();
3568 ++iter)
3569 (*iter).second->dump_dot (gv, args);
3571 /* Terminate subgraph. */
3572 gv->outdent ();
3573 gv->println ("}");
3576 void add_node (exploded_node *en) FINAL OVERRIDE
3578 const supernode *supernode = en->get_supernode ();
3579 gcc_assert (supernode);
3580 supernode_cluster **slot = m_map.get (supernode);
3581 if (slot)
3582 (*slot)->add_node (en);
3583 else
3585 supernode_cluster *child = new supernode_cluster (supernode);
3586 m_map.put (supernode, child);
3587 child->add_node (en);
3591 private:
3592 function *m_fun;
3593 call_string m_cs;
3594 typedef ordered_hash_map<const supernode *, supernode_cluster *> map_t;
3595 map_t m_map;
3598 /* Keys for root_cluster. */
3600 struct function_call_string
3602 function_call_string (function *fun, call_string cs)
3603 : m_fun (fun), m_cs (cs)
3605 gcc_assert (fun);
3608 function *m_fun;
3609 call_string m_cs;
3612 } // namespace ana
3614 template <> struct default_hash_traits<function_call_string>
3615 : public pod_hash_traits<function_call_string>
3617 static const bool empty_zero_p = false;
3620 template <>
3621 inline hashval_t
3622 pod_hash_traits<function_call_string>::hash (value_type v)
3624 return pointer_hash <function>::hash (v.m_fun) ^ v.m_cs.hash ();
3627 template <>
3628 inline bool
3629 pod_hash_traits<function_call_string>::equal (const value_type &existing,
3630 const value_type &candidate)
3632 return existing.m_fun == candidate.m_fun && existing.m_cs == candidate.m_cs;
3634 template <>
3635 inline void
3636 pod_hash_traits<function_call_string>::mark_deleted (value_type &v)
3638 v.m_fun = reinterpret_cast<function *> (1);
3640 template <>
3641 inline void
3642 pod_hash_traits<function_call_string>::mark_empty (value_type &v)
3644 v.m_fun = NULL;
3646 template <>
3647 inline bool
3648 pod_hash_traits<function_call_string>::is_deleted (value_type v)
3650 return v.m_fun == reinterpret_cast<function *> (1);
3652 template <>
3653 inline bool
3654 pod_hash_traits<function_call_string>::is_empty (value_type v)
3656 return v.m_fun == NULL;
3659 namespace ana {
3661 /* Top-level cluster for generating .dot output for exploded graphs,
3662 handling the functionless nodes, and grouping the remaining nodes by
3663 callstring. */
3665 class root_cluster : public exploded_cluster
3667 public:
3668 ~root_cluster ()
3670 for (map_t::iterator iter = m_map.begin ();
3671 iter != m_map.end ();
3672 ++iter)
3673 delete (*iter).second;
3676 void dump_dot (graphviz_out *gv, const dump_args_t &args) const FINAL OVERRIDE
3678 int i;
3679 exploded_node *enode;
3680 FOR_EACH_VEC_ELT (m_functionless_enodes, i, enode)
3681 enode->dump_dot (gv, args);
3683 for (map_t::iterator iter = m_map.begin ();
3684 iter != m_map.end ();
3685 ++iter)
3686 (*iter).second->dump_dot (gv, args);
3689 void add_node (exploded_node *en) FINAL OVERRIDE
3691 function *fun = en->get_function ();
3692 if (!fun)
3694 m_functionless_enodes.safe_push (en);
3695 return;
3698 const call_string &cs = en->get_point ().get_call_string ();
3699 function_call_string key (fun, cs);
3700 function_call_string_cluster **slot = m_map.get (key);
3701 if (slot)
3702 (*slot)->add_node (en);
3703 else
3705 function_call_string_cluster *child
3706 = new function_call_string_cluster (fun, cs);
3707 m_map.put (key, child);
3708 child->add_node (en);
3712 private:
3713 /* This can't be an ordered_hash_map, as we can't store vec<call_string>,
3714 since it's not a POD; vec<>::quick_push has:
3715 *slot = obj;
3716 and the slot isn't initialized, so the assignment op dies when cleaning up
3717 un-inited *slot (within the truncate call). */
3718 typedef hash_map<function_call_string, function_call_string_cluster *> map_t;
3719 map_t m_map;
3721 /* This should just be the origin exploded_node. */
3722 auto_vec <exploded_node *> m_functionless_enodes;
3725 /* Subclass of range_label for use within
3726 exploded_graph::dump_exploded_nodes for implementing
3727 -fdump-analyzer-exploded-nodes: a label for a specific
3728 exploded_node. */
3730 class enode_label : public range_label
3732 public:
3733 enode_label (const extrinsic_state &ext_state,
3734 exploded_node *enode)
3735 : m_ext_state (ext_state), m_enode (enode) {}
3737 label_text get_text (unsigned) const FINAL OVERRIDE
3739 pretty_printer pp;
3740 pp_format_decoder (&pp) = default_tree_printer;
3741 m_enode->get_state ().dump_to_pp (m_ext_state, true, false, &pp);
3742 return make_label_text (false, "EN: %i: %s",
3743 m_enode->m_index, pp_formatted_text (&pp));
3746 private:
3747 const extrinsic_state &m_ext_state;
3748 exploded_node *m_enode;
3751 /* Postprocessing support for dumping the exploded nodes.
3752 Handle -fdump-analyzer-exploded-nodes,
3753 -fdump-analyzer-exploded-nodes-2, and the
3754 "__analyzer_dump_exploded_nodes" builtin. */
3756 void
3757 exploded_graph::dump_exploded_nodes () const
3759 // TODO
3760 /* Locate calls to __analyzer_dump_exploded_nodes. */
3761 // Print how many egs there are for them?
3762 /* Better: log them as we go, and record the exploded nodes
3763 in question. */
3765 /* Show every enode. */
3767 /* Gather them by stmt, so that we can more clearly see the
3768 "hotspots" requiring numerous exploded nodes. */
3770 /* Alternatively, simply throw them all into one big rich_location
3771 and see if the label-printing will sort it out...
3772 This requires them all to be in the same source file. */
3774 if (flag_dump_analyzer_exploded_nodes)
3776 auto_timevar tv (TV_ANALYZER_DUMP);
3777 gcc_rich_location richloc (UNKNOWN_LOCATION);
3778 unsigned i;
3779 exploded_node *enode;
3780 FOR_EACH_VEC_ELT (m_nodes, i, enode)
3782 if (const gimple *stmt = enode->get_stmt ())
3784 if (get_pure_location (richloc.get_loc ()) == UNKNOWN_LOCATION)
3785 richloc.set_range (0, stmt->location, SHOW_RANGE_WITH_CARET);
3786 else
3787 richloc.add_range (stmt->location,
3788 SHOW_RANGE_WITHOUT_CARET,
3789 new enode_label (m_ext_state, enode));
3792 warning_at (&richloc, 0, "%i exploded nodes", m_nodes.length ());
3794 /* Repeat the warning without all the labels, so that message is visible
3795 (the other one may well have scrolled past the terminal limit). */
3796 warning_at (richloc.get_loc (), 0,
3797 "%i exploded nodes", m_nodes.length ());
3799 if (m_worklist.length () > 0)
3800 warning_at (richloc.get_loc (), 0,
3801 "worklist still contains %i nodes", m_worklist.length ());
3804 /* Dump the egraph in textual form to a dump file. */
3805 if (flag_dump_analyzer_exploded_nodes_2)
3807 auto_timevar tv (TV_ANALYZER_DUMP);
3808 char *filename
3809 = concat (dump_base_name, ".eg.txt", NULL);
3810 FILE *outf = fopen (filename, "w");
3811 if (!outf)
3812 error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename);
3813 free (filename);
3815 fprintf (outf, "exploded graph for %s\n", dump_base_name);
3816 fprintf (outf, " nodes: %i\n", m_nodes.length ());
3817 fprintf (outf, " edges: %i\n", m_edges.length ());
3819 unsigned i;
3820 exploded_node *enode;
3821 FOR_EACH_VEC_ELT (m_nodes, i, enode)
3823 fprintf (outf, "\nEN %i:\n", enode->m_index);
3824 enode->dump_succs_and_preds (outf);
3825 pretty_printer pp;
3826 enode->get_point ().print (&pp, format (true));
3827 fprintf (outf, "%s\n", pp_formatted_text (&pp));
3828 enode->get_state ().dump_to_file (m_ext_state, false, true, outf);
3831 fclose (outf);
3834 /* Dump the egraph in textual form to multiple dump files, one per enode. */
3835 if (flag_dump_analyzer_exploded_nodes_3)
3837 auto_timevar tv (TV_ANALYZER_DUMP);
3839 unsigned i;
3840 exploded_node *enode;
3841 FOR_EACH_VEC_ELT (m_nodes, i, enode)
3843 char *filename
3844 = xasprintf ("%s.en-%i.txt", dump_base_name, i);
3845 FILE *outf = fopen (filename, "w");
3846 if (!outf)
3847 error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename);
3848 free (filename);
3850 fprintf (outf, "EN %i:\n", enode->m_index);
3851 enode->dump_succs_and_preds (outf);
3852 pretty_printer pp;
3853 enode->get_point ().print (&pp, format (true));
3854 fprintf (outf, "%s\n", pp_formatted_text (&pp));
3855 enode->get_state ().dump_to_file (m_ext_state, false, true, outf);
3857 fclose (outf);
3861 /* Emit a warning at any call to "__analyzer_dump_exploded_nodes",
3862 giving the number of processed exploded nodes for "before-stmt",
3863 and the IDs of processed, merger, and worklist enodes.
3865 We highlight the count of *processed* enodes since this is of most
3866 interest in DejaGnu tests for ensuring that state merger has
3867 happened.
3869 We don't show the count of merger and worklist enodes, as this is
3870 more of an implementation detail of the merging/worklist that we
3871 don't want to bake into our expected DejaGnu messages. */
3873 unsigned i;
3874 exploded_node *enode;
3875 hash_set<const gimple *> seen;
3876 FOR_EACH_VEC_ELT (m_nodes, i, enode)
3878 if (enode->get_point ().get_kind () != PK_BEFORE_STMT)
3879 continue;
3881 if (const gimple *stmt = enode->get_stmt ())
3882 if (const gcall *call = dyn_cast <const gcall *> (stmt))
3883 if (is_special_named_call_p (call, "__analyzer_dump_exploded_nodes",
3886 if (seen.contains (stmt))
3887 continue;
3889 auto_vec<exploded_node *> processed_enodes;
3890 auto_vec<exploded_node *> merger_enodes;
3891 auto_vec<exploded_node *> worklist_enodes;
3892 /* This is O(N^2). */
3893 unsigned j;
3894 exploded_node *other_enode;
3895 FOR_EACH_VEC_ELT (m_nodes, j, other_enode)
3897 if (other_enode->get_point ().get_kind () != PK_BEFORE_STMT)
3898 continue;
3899 if (other_enode->get_stmt () == stmt)
3900 switch (other_enode->get_status ())
3902 default:
3903 gcc_unreachable ();
3904 case exploded_node::STATUS_WORKLIST:
3905 worklist_enodes.safe_push (other_enode);
3906 break;
3907 case exploded_node::STATUS_PROCESSED:
3908 processed_enodes.safe_push (other_enode);
3909 break;
3910 case exploded_node::STATUS_MERGER:
3911 merger_enodes.safe_push (other_enode);
3912 break;
3916 pretty_printer pp;
3917 pp_character (&pp, '[');
3918 print_enode_indices (&pp, processed_enodes);
3919 if (merger_enodes.length () > 0)
3921 pp_string (&pp, "] merger(s): [");
3922 print_enode_indices (&pp, merger_enodes);
3924 if (worklist_enodes.length () > 0)
3926 pp_string (&pp, "] worklist: [");
3927 print_enode_indices (&pp, worklist_enodes);
3929 pp_character (&pp, ']');
3931 warning_n (stmt->location, 0, processed_enodes.length (),
3932 "%i processed enode: %s",
3933 "%i processed enodes: %s",
3934 processed_enodes.length (), pp_formatted_text (&pp));
3935 seen.add (stmt);
3937 /* If the argument is non-zero, then print all of the states
3938 of the various enodes. */
3939 tree t_arg = fold (gimple_call_arg (call, 0));
3940 if (TREE_CODE (t_arg) != INTEGER_CST)
3942 error_at (call->location,
3943 "integer constant required for arg 1");
3944 return;
3946 int i_arg = TREE_INT_CST_LOW (t_arg);
3947 if (i_arg)
3949 exploded_node *other_enode;
3950 FOR_EACH_VEC_ELT (processed_enodes, j, other_enode)
3952 fprintf (stderr, "%i of %i: EN %i:\n",
3953 j + 1, processed_enodes.length (),
3954 other_enode->m_index);
3955 other_enode->dump_succs_and_preds (stderr);
3956 /* Dump state. */
3957 other_enode->get_state ().dump (m_ext_state, false);
3964 DEBUG_FUNCTION exploded_node *
3965 exploded_graph::get_node_by_index (int idx) const
3967 exploded_node *enode = m_nodes[idx];
3968 gcc_assert (enode->m_index == idx);
3969 return enode;
3972 /* Ensure that there is an exploded_node for a top-level call to FNDECL. */
3974 void
3975 exploded_graph::on_escaped_function (tree fndecl)
3977 logger * const logger = get_logger ();
3978 LOG_FUNC_1 (logger, "%qE", fndecl);
3980 cgraph_node *cgnode = cgraph_node::get (fndecl);
3981 if (!cgnode)
3982 return;
3984 function *fun = cgnode->get_fun ();
3985 if (!fun)
3986 return;
3988 if (!gimple_has_body_p (fndecl))
3989 return;
3991 exploded_node *enode = add_function_entry (fun);
3992 if (logger)
3994 if (enode)
3995 logger->log ("created EN %i for %qE entrypoint",
3996 enode->m_index, fun->decl);
3997 else
3998 logger->log ("did not create enode for %qE entrypoint", fun->decl);
4002 /* A collection of classes for visualizing the callgraph in .dot form
4003 (as represented in the supergraph). */
4005 /* Forward decls. */
4006 class viz_callgraph_node;
4007 class viz_callgraph_edge;
4008 class viz_callgraph;
4009 class viz_callgraph_cluster;
4011 /* Traits for using "digraph.h" to visualize the callgraph. */
4013 struct viz_callgraph_traits
4015 typedef viz_callgraph_node node_t;
4016 typedef viz_callgraph_edge edge_t;
4017 typedef viz_callgraph graph_t;
4018 struct dump_args_t
4020 dump_args_t (const exploded_graph *eg) : m_eg (eg) {}
4021 const exploded_graph *m_eg;
4023 typedef viz_callgraph_cluster cluster_t;
4026 /* Subclass of dnode representing a function within the callgraph. */
4028 class viz_callgraph_node : public dnode<viz_callgraph_traits>
4030 friend class viz_callgraph;
4032 public:
4033 viz_callgraph_node (function *fun, int index)
4034 : m_fun (fun), m_index (index), m_num_supernodes (0), m_num_superedges (0)
4036 gcc_assert (fun);
4039 void dump_dot (graphviz_out *gv, const dump_args_t &args) const FINAL OVERRIDE
4041 pretty_printer *pp = gv->get_pp ();
4043 dump_dot_id (pp);
4044 pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=<",
4045 "lightgrey");
4046 pp_string (pp, "<TABLE BORDER=\"0\">");
4047 pp_write_text_to_stream (pp);
4049 gv->begin_trtd ();
4050 pp_printf (pp, "VCG: %i: %s", m_index, function_name (m_fun));
4051 gv->end_tdtr ();
4052 pp_newline (pp);
4054 gv->begin_trtd ();
4055 pp_printf (pp, "supernodes: %i\n", m_num_supernodes);
4056 gv->end_tdtr ();
4057 pp_newline (pp);
4059 gv->begin_trtd ();
4060 pp_printf (pp, "superedges: %i\n", m_num_superedges);
4061 gv->end_tdtr ();
4062 pp_newline (pp);
4064 if (args.m_eg)
4066 unsigned i;
4067 exploded_node *enode;
4068 unsigned num_enodes = 0;
4069 FOR_EACH_VEC_ELT (args.m_eg->m_nodes, i, enode)
4071 if (enode->get_point ().get_function () == m_fun)
4072 num_enodes++;
4074 gv->begin_trtd ();
4075 pp_printf (pp, "enodes: %i\n", num_enodes);
4076 gv->end_tdtr ();
4077 pp_newline (pp);
4079 // TODO: also show the per-callstring breakdown
4080 const exploded_graph::call_string_data_map_t *per_cs_data
4081 = args.m_eg->get_per_call_string_data ();
4082 for (exploded_graph::call_string_data_map_t::iterator iter
4083 = per_cs_data->begin ();
4084 iter != per_cs_data->end ();
4085 ++iter)
4087 const call_string *cs = (*iter).first;
4088 //per_call_string_data *data = (*iter).second;
4089 num_enodes = 0;
4090 FOR_EACH_VEC_ELT (args.m_eg->m_nodes, i, enode)
4092 if (enode->get_point ().get_function () == m_fun
4093 && enode->get_point ().get_call_string () == *cs)
4094 num_enodes++;
4096 if (num_enodes > 0)
4098 gv->begin_trtd ();
4099 cs->print (pp);
4100 pp_printf (pp, ": %i\n", num_enodes);
4101 pp_write_text_as_html_like_dot_to_stream (pp);
4102 gv->end_tdtr ();
4106 /* Show any summaries. */
4107 per_function_data *data = args.m_eg->get_per_function_data (m_fun);
4108 if (data)
4110 pp_newline (pp);
4111 gv->begin_trtd ();
4112 pp_printf (pp, "summaries: %i\n", data->m_summaries.length ());
4113 pp_write_text_as_html_like_dot_to_stream (pp);
4114 gv->end_tdtr ();
4118 pp_string (pp, "</TABLE>>];\n\n");
4119 pp_flush (pp);
4122 void dump_dot_id (pretty_printer *pp) const
4124 pp_printf (pp, "vcg_%i", m_index);
4127 private:
4128 function *m_fun;
4129 int m_index;
4130 int m_num_supernodes;
4131 int m_num_superedges;
4134 /* Subclass of dedge representing a callgraph edge. */
4136 class viz_callgraph_edge : public dedge<viz_callgraph_traits>
4138 public:
4139 viz_callgraph_edge (viz_callgraph_node *src, viz_callgraph_node *dest)
4140 : dedge<viz_callgraph_traits> (src, dest)
4143 void dump_dot (graphviz_out *gv, const dump_args_t &) const
4144 FINAL OVERRIDE
4146 pretty_printer *pp = gv->get_pp ();
4148 const char *style = "\"solid,bold\"";
4149 const char *color = "black";
4150 int weight = 10;
4151 const char *constraint = "true";
4153 m_src->dump_dot_id (pp);
4154 pp_string (pp, " -> ");
4155 m_dest->dump_dot_id (pp);
4156 pp_printf (pp,
4157 (" [style=%s, color=%s, weight=%d, constraint=%s,"
4158 " headlabel=\""),
4159 style, color, weight, constraint);
4160 pp_printf (pp, "\"];\n");
4164 /* Subclass of digraph representing the callgraph. */
4166 class viz_callgraph : public digraph<viz_callgraph_traits>
4168 public:
4169 viz_callgraph (const supergraph &sg);
4171 viz_callgraph_node *get_vcg_node_for_function (function *fun)
4173 return *m_map.get (fun);
4176 viz_callgraph_node *get_vcg_node_for_snode (supernode *snode)
4178 return get_vcg_node_for_function (snode->m_fun);
4181 private:
4182 hash_map<function *, viz_callgraph_node *> m_map;
4185 /* Placeholder subclass of cluster. */
4187 class viz_callgraph_cluster : public cluster<viz_callgraph_traits>
4191 /* viz_callgraph's ctor. */
4193 viz_callgraph::viz_callgraph (const supergraph &sg)
4195 cgraph_node *node;
4196 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
4198 function *fun = node->get_fun ();
4199 viz_callgraph_node *vcg_node
4200 = new viz_callgraph_node (fun, m_nodes.length ());
4201 m_map.put (fun, vcg_node);
4202 add_node (vcg_node);
4205 unsigned i;
4206 superedge *sedge;
4207 FOR_EACH_VEC_ELT (sg.m_edges, i, sedge)
4209 viz_callgraph_node *vcg_src = get_vcg_node_for_snode (sedge->m_src);
4210 if (vcg_src->m_fun)
4211 get_vcg_node_for_function (vcg_src->m_fun)->m_num_superedges++;
4212 if (sedge->dyn_cast_call_superedge ())
4214 viz_callgraph_node *vcg_dest = get_vcg_node_for_snode (sedge->m_dest);
4215 viz_callgraph_edge *vcg_edge
4216 = new viz_callgraph_edge (vcg_src, vcg_dest);
4217 add_edge (vcg_edge);
4221 supernode *snode;
4222 FOR_EACH_VEC_ELT (sg.m_nodes, i, snode)
4224 if (snode->m_fun)
4225 get_vcg_node_for_function (snode->m_fun)->m_num_supernodes++;
4229 /* Dump the callgraph to FILENAME. */
4231 static void
4232 dump_callgraph (const supergraph &sg, const char *filename,
4233 const exploded_graph *eg)
4235 FILE *outf = fopen (filename, "w");
4236 if (!outf)
4237 return;
4239 // TODO
4240 viz_callgraph vcg (sg);
4241 vcg.dump_dot (filename, NULL, viz_callgraph_traits::dump_args_t (eg));
4243 fclose (outf);
4246 /* Dump the callgraph to "<srcfile>.callgraph.dot". */
4248 static void
4249 dump_callgraph (const supergraph &sg, const exploded_graph *eg)
4251 auto_timevar tv (TV_ANALYZER_DUMP);
4252 char *filename = concat (dump_base_name, ".callgraph.dot", NULL);
4253 dump_callgraph (sg, filename, eg);
4254 free (filename);
4257 /* Subclass of dot_annotator for implementing
4258 DUMP_BASE_NAME.supergraph-eg.dot, a post-analysis dump of the supergraph.
4260 Annotate the supergraph nodes by printing the exploded nodes in concise
4261 form within them, next to their pertinent statements where appropriate,
4262 colorizing the exploded nodes based on sm-state.
4263 Also show saved diagnostics within the exploded nodes, giving information
4264 on whether they were feasible, and, if infeasible, where the problem
4265 was. */
4267 class exploded_graph_annotator : public dot_annotator
4269 public:
4270 exploded_graph_annotator (const exploded_graph &eg)
4271 : m_eg (eg)
4273 /* Avoid O(N^2) by prepopulating m_enodes_per_snodes. */
4274 unsigned i;
4275 supernode *snode;
4276 FOR_EACH_VEC_ELT (eg.get_supergraph ().m_nodes, i, snode)
4277 m_enodes_per_snodes.safe_push (new auto_vec <exploded_node *> ());
4278 exploded_node *enode;
4279 FOR_EACH_VEC_ELT (m_eg.m_nodes, i, enode)
4280 if (enode->get_supernode ())
4281 m_enodes_per_snodes[enode->get_supernode ()->m_index]->safe_push (enode);
4284 /* Show exploded nodes for BEFORE_SUPERNODE points before N. */
4285 bool add_node_annotations (graphviz_out *gv, const supernode &n,
4286 bool within_table)
4287 const FINAL OVERRIDE
4289 if (!within_table)
4290 return false;
4291 gv->begin_tr ();
4292 pretty_printer *pp = gv->get_pp ();
4294 gv->begin_td ();
4295 pp_string (pp, "BEFORE");
4296 pp_printf (pp, " (scc: %i)", m_eg.get_scc_id (n));
4297 gv->end_td ();
4299 unsigned i;
4300 exploded_node *enode;
4301 bool had_enode = false;
4302 FOR_EACH_VEC_ELT (*m_enodes_per_snodes[n.m_index], i, enode)
4304 gcc_assert (enode->get_supernode () == &n);
4305 const program_point &point = enode->get_point ();
4306 if (point.get_kind () != PK_BEFORE_SUPERNODE)
4307 continue;
4308 print_enode (gv, enode);
4309 had_enode = true;
4311 if (!had_enode)
4312 pp_string (pp, "<TD BGCOLOR=\"red\">UNREACHED</TD>");
4313 pp_flush (pp);
4314 gv->end_tr ();
4315 return true;
4318 /* Show exploded nodes for STMT. */
4319 void add_stmt_annotations (graphviz_out *gv, const gimple *stmt,
4320 bool within_row)
4321 const FINAL OVERRIDE
4323 if (!within_row)
4324 return;
4325 pretty_printer *pp = gv->get_pp ();
4327 const supernode *snode
4328 = m_eg.get_supergraph ().get_supernode_for_stmt (stmt);
4329 unsigned i;
4330 exploded_node *enode;
4331 bool had_td = false;
4332 FOR_EACH_VEC_ELT (*m_enodes_per_snodes[snode->m_index], i, enode)
4334 const program_point &point = enode->get_point ();
4335 if (point.get_kind () != PK_BEFORE_STMT)
4336 continue;
4337 if (point.get_stmt () != stmt)
4338 continue;
4339 print_enode (gv, enode);
4340 had_td = true;
4342 pp_flush (pp);
4343 if (!had_td)
4345 gv->begin_td ();
4346 gv->end_td ();
4350 /* Show exploded nodes for AFTER_SUPERNODE points after N. */
4351 bool add_after_node_annotations (graphviz_out *gv, const supernode &n)
4352 const FINAL OVERRIDE
4354 gv->begin_tr ();
4355 pretty_printer *pp = gv->get_pp ();
4357 gv->begin_td ();
4358 pp_string (pp, "AFTER");
4359 gv->end_td ();
4361 unsigned i;
4362 exploded_node *enode;
4363 FOR_EACH_VEC_ELT (*m_enodes_per_snodes[n.m_index], i, enode)
4365 gcc_assert (enode->get_supernode () == &n);
4366 const program_point &point = enode->get_point ();
4367 if (point.get_kind () != PK_AFTER_SUPERNODE)
4368 continue;
4369 print_enode (gv, enode);
4371 pp_flush (pp);
4372 gv->end_tr ();
4373 return true;
4376 private:
4377 /* Concisely print a TD element for ENODE, showing the index, status,
4378 and any saved_diagnostics at the enode. Colorize it to show sm-state.
4380 Ideally we'd dump ENODE's state here, hidden behind some kind of
4381 interactive disclosure method like a tooltip, so that the states
4382 can be explored without overwhelming the graph.
4383 However, I wasn't able to get graphviz/xdot to show tooltips on
4384 individual elements within a HTML-like label. */
4385 void print_enode (graphviz_out *gv, const exploded_node *enode) const
4387 pretty_printer *pp = gv->get_pp ();
4388 pp_printf (pp, "<TD BGCOLOR=\"%s\">",
4389 enode->get_dot_fillcolor ());
4390 pp_printf (pp, "<TABLE BORDER=\"0\">");
4391 gv->begin_trtd ();
4392 pp_printf (pp, "EN: %i", enode->m_index);
4393 switch (enode->get_status ())
4395 default:
4396 gcc_unreachable ();
4397 case exploded_node::STATUS_WORKLIST:
4398 pp_string (pp, "(W)");
4399 break;
4400 case exploded_node::STATUS_PROCESSED:
4401 break;
4402 case exploded_node::STATUS_MERGER:
4403 pp_string (pp, "(M)");
4404 break;
4405 case exploded_node::STATUS_BULK_MERGED:
4406 pp_string (pp, "(BM)");
4407 break;
4409 gv->end_tdtr ();
4410 /* Dump any saved_diagnostics at this enode. */
4412 const diagnostic_manager &dm = m_eg.get_diagnostic_manager ();
4413 for (unsigned i = 0; i < dm.get_num_diagnostics (); i++)
4415 const saved_diagnostic *sd = dm.get_saved_diagnostic (i);
4416 if (sd->m_enode == enode)
4417 print_saved_diagnostic (gv, sd);
4420 pp_printf (pp, "</TABLE>");
4421 pp_printf (pp, "</TD>");
4424 /* Print a TABLE element for SD, showing the kind, the length of the
4425 exploded_path, whether the path was feasible, and if infeasible,
4426 what the problem was. */
4427 void print_saved_diagnostic (graphviz_out *gv,
4428 const saved_diagnostic *sd) const
4430 pretty_printer *pp = gv->get_pp ();
4431 gv->begin_trtd ();
4432 pp_printf (pp, "<TABLE BORDER=\"0\">");
4433 gv->begin_tr ();
4434 pp_string (pp, "<TD BGCOLOR=\"green\">");
4435 pp_printf (pp, "DIAGNOSTIC: %s", sd->m_d->get_kind ());
4436 gv->end_tdtr ();
4437 gv->begin_trtd ();
4438 pp_printf (pp, "epath length: %i", sd->get_epath_length ());
4439 gv->end_tdtr ();
4440 switch (sd->get_status ())
4442 default:
4443 case saved_diagnostic::STATUS_NEW:
4444 gcc_unreachable ();
4445 break;
4446 case saved_diagnostic::STATUS_INFEASIBLE_PATH:
4448 gv->begin_trtd ();
4449 pp_printf (pp, "INFEASIBLE");
4450 gv->end_tdtr ();
4451 const feasibility_problem *p = sd->get_feasibility_problem ();
4452 gcc_assert (p);
4453 gv->begin_trtd ();
4454 pp_printf (pp, "at eedge %i: EN:%i -> EN:%i",
4455 p->m_eedge_idx,
4456 p->m_eedge.m_src->m_index,
4457 p->m_eedge.m_dest->m_index);
4458 pp_write_text_as_html_like_dot_to_stream (pp);
4459 gv->end_tdtr ();
4460 gv->begin_trtd ();
4461 p->m_eedge.m_sedge->dump (pp);
4462 pp_write_text_as_html_like_dot_to_stream (pp);
4463 gv->end_tdtr ();
4464 gv->begin_trtd ();
4465 pp_gimple_stmt_1 (pp, p->m_last_stmt, 0, (dump_flags_t)0);
4466 pp_write_text_as_html_like_dot_to_stream (pp);
4467 gv->end_tdtr ();
4468 /* Ideally we'd print p->m_model here; see the notes above about
4469 tooltips. */
4471 break;
4472 case saved_diagnostic::STATUS_FEASIBLE_PATH:
4473 gv->begin_trtd ();
4474 pp_printf (pp, "FEASIBLE");
4475 gv->end_tdtr ();
4476 break;
4478 pp_printf (pp, "</TABLE>");
4479 gv->end_tdtr ();
4482 const exploded_graph &m_eg;
4483 auto_delete_vec<auto_vec <exploded_node *> > m_enodes_per_snodes;
4486 /* Implement -fdump-analyzer-json. */
4488 static void
4489 dump_analyzer_json (const supergraph &sg,
4490 const exploded_graph &eg)
4492 auto_timevar tv (TV_ANALYZER_DUMP);
4493 char *filename = concat (dump_base_name, ".analyzer.json.gz", NULL);
4494 gzFile output = gzopen (filename, "w");
4495 if (!output)
4497 error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename);
4498 free (filename);
4499 return;
4502 json::object *toplev_obj = new json::object ();
4503 toplev_obj->set ("sgraph", sg.to_json ());
4504 toplev_obj->set ("egraph", eg.to_json ());
4506 pretty_printer pp;
4507 toplev_obj->print (&pp);
4508 pp_formatted_text (&pp);
4510 delete toplev_obj;
4512 if (gzputs (output, pp_formatted_text (&pp)) == EOF
4513 || gzclose (output))
4514 error_at (UNKNOWN_LOCATION, "error writing %qs", filename);
4516 free (filename);
4519 /* Run the analysis "engine". */
4521 void
4522 impl_run_checkers (logger *logger)
4524 LOG_SCOPE (logger);
4526 /* If using LTO, ensure that the cgraph nodes have function bodies. */
4527 cgraph_node *node;
4528 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
4529 node->get_untransformed_body ();
4531 engine eng;
4533 /* Create the supergraph. */
4534 supergraph sg (logger);
4536 state_purge_map *purge_map = NULL;
4538 if (flag_analyzer_state_purge)
4539 purge_map = new state_purge_map (sg, logger);
4541 if (flag_dump_analyzer_supergraph)
4543 /* Dump supergraph pre-analysis. */
4544 auto_timevar tv (TV_ANALYZER_DUMP);
4545 char *filename = concat (dump_base_name, ".supergraph.dot", NULL);
4546 supergraph::dump_args_t args ((enum supergraph_dot_flags)0, NULL);
4547 sg.dump_dot (filename, args);
4548 free (filename);
4551 if (flag_dump_analyzer_state_purge)
4553 auto_timevar tv (TV_ANALYZER_DUMP);
4554 state_purge_annotator a (purge_map);
4555 char *filename = concat (dump_base_name, ".state-purge.dot", NULL);
4556 supergraph::dump_args_t args ((enum supergraph_dot_flags)0, &a);
4557 sg.dump_dot (filename, args);
4558 free (filename);
4561 auto_delete_vec <state_machine> checkers;
4562 make_checkers (checkers, logger);
4564 if (logger)
4566 int i;
4567 state_machine *sm;
4568 FOR_EACH_VEC_ELT (checkers, i, sm)
4569 logger->log ("checkers[%i]: %s", i, sm->get_name ());
4572 /* Extrinsic state shared by nodes in the graph. */
4573 const extrinsic_state ext_state (checkers, &eng, logger);
4575 const analysis_plan plan (sg, logger);
4577 /* The exploded graph. */
4578 exploded_graph eg (sg, logger, ext_state, purge_map, plan,
4579 analyzer_verbosity);
4581 /* Add entrypoints to the graph for externally-callable functions. */
4582 eg.build_initial_worklist ();
4584 /* Now process the worklist, exploring the <point, state> graph. */
4585 eg.process_worklist ();
4587 if (flag_dump_analyzer_exploded_graph)
4589 auto_timevar tv (TV_ANALYZER_DUMP);
4590 char *filename
4591 = concat (dump_base_name, ".eg.dot", NULL);
4592 exploded_graph::dump_args_t args (eg);
4593 root_cluster c;
4594 eg.dump_dot (filename, &c, args);
4595 free (filename);
4598 /* Now emit any saved diagnostics. */
4599 eg.get_diagnostic_manager ().emit_saved_diagnostics (eg);
4601 eg.dump_exploded_nodes ();
4603 eg.log_stats ();
4605 if (flag_dump_analyzer_callgraph)
4606 dump_callgraph (sg, &eg);
4608 if (flag_dump_analyzer_supergraph)
4610 /* Dump post-analysis form of supergraph. */
4611 auto_timevar tv (TV_ANALYZER_DUMP);
4612 char *filename = concat (dump_base_name, ".supergraph-eg.dot", NULL);
4613 exploded_graph_annotator a (eg);
4614 supergraph::dump_args_t args ((enum supergraph_dot_flags)0, &a);
4615 sg.dump_dot (filename, args);
4616 free (filename);
4619 if (flag_dump_analyzer_json)
4620 dump_analyzer_json (sg, eg);
4622 delete purge_map;
4625 /* External entrypoint to the analysis "engine".
4626 Set up any dumps, then call impl_run_checkers. */
4628 void
4629 run_checkers ()
4631 /* Save input_location. */
4632 location_t saved_input_location = input_location;
4634 /* Handle -fdump-analyzer and -fdump-analyzer-stderr. */
4635 FILE *dump_fout = NULL;
4636 /* Track if we're responsible for closing dump_fout. */
4637 bool owns_dump_fout = false;
4638 if (flag_dump_analyzer_stderr)
4639 dump_fout = stderr;
4640 else if (flag_dump_analyzer)
4642 char *dump_filename = concat (dump_base_name, ".analyzer.txt", NULL);
4643 dump_fout = fopen (dump_filename, "w");
4644 free (dump_filename);
4645 if (dump_fout)
4646 owns_dump_fout = true;
4650 log_user the_logger (NULL);
4651 if (dump_fout)
4652 the_logger.set_logger (new logger (dump_fout, 0, 0,
4653 *global_dc->printer));
4654 LOG_SCOPE (the_logger.get_logger ());
4656 impl_run_checkers (the_logger.get_logger ());
4658 /* end of lifetime of the_logger (so that dump file is closed after the
4659 various dtors run). */
4662 if (owns_dump_fout)
4663 fclose (dump_fout);
4665 /* Restore input_location. Subsequent passes may assume that input_location
4666 is some arbitrary value *not* in the block tree, which might be violated
4667 if we didn't restore it. */
4668 input_location = saved_input_location;
4671 } // namespace ana
4673 #endif /* #if ENABLE_ANALYZER */