1 /* The analysis "engine".
2 Copyright (C) 2019-2020 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
4
5 This file is part of GCC.
6
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.
11
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.
16
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/>. */
20
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 "analyzer/analyzer.h"
41 #include "analyzer/analyzer-logging.h"
42 #include "analyzer/region-model.h"
43 #include "analyzer/constraint-manager.h"
44 #include "analyzer/sm.h"
45 #include "analyzer/pending-diagnostic.h"
46 #include "analyzer/diagnostic-manager.h"
47 #include "cfg.h"
48 #include "basic-block.h"
49 #include "gimple.h"
50 #include "gimple-iterator.h"
51 #include "gimple-pretty-print.h"
52 #include "cgraph.h"
53 #include "digraph.h"
54 #include "analyzer/supergraph.h"
55 #include "analyzer/call-string.h"
56 #include "analyzer/program-point.h"
57 #include "analyzer/program-state.h"
58 #include "analyzer/exploded-graph.h"
59 #include "analyzer/analysis-plan.h"
60 #include "analyzer/checker-path.h"
61 #include "analyzer/state-purge.h"
62 #include "analyzer/bar-chart.h"
63
64 /* For an overview, see gcc/doc/analyzer.texi. */
65
66 #if ENABLE_ANALYZER
67
68 namespace ana {
69
70 static int readability_comparator (const void *p1, const void *p2);
71
72 /* class impl_region_model_context : public region_model_context. */
73
74 impl_region_model_context::
impl_region_model_context(exploded_graph & eg,const exploded_node * enode_for_diag,const program_state * old_state,program_state * new_state,state_change * change,const gimple * stmt,stmt_finder * stmt_finder)75 impl_region_model_context (exploded_graph &eg,
76 const exploded_node *enode_for_diag,
77 const program_state *old_state,
78 program_state *new_state,
79 state_change *change,
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_change (change),
87 m_stmt (stmt),
88 m_stmt_finder (stmt_finder),
89 m_ext_state (eg.get_ext_state ())
90 {
91 }
92
93 impl_region_model_context::
impl_region_model_context(program_state * state,state_change * change,const extrinsic_state & ext_state,logger * logger)94 impl_region_model_context (program_state *state,
95 state_change *change,
96 const extrinsic_state &ext_state,
97 logger *logger)
98 : m_eg (NULL), m_logger (logger), m_enode_for_diag (NULL),
99 m_old_state (NULL),
100 m_new_state (state),
101 m_change (change),
102 m_stmt (NULL),
103 m_stmt_finder (NULL),
104 m_ext_state (ext_state)
105 {
106 }
107
108 void
warn(pending_diagnostic * d)109 impl_region_model_context::warn (pending_diagnostic *d)
110 {
111 LOG_FUNC (get_logger ());
112 if (m_eg)
113 m_eg->get_diagnostic_manager ().add_diagnostic
114 (m_enode_for_diag, m_enode_for_diag->get_supernode (),
115 m_stmt, m_stmt_finder, d);
116 }
117
118 void
remap_svalue_ids(const svalue_id_map & map)119 impl_region_model_context::remap_svalue_ids (const svalue_id_map &map)
120 {
121 m_new_state->remap_svalue_ids (map);
122 if (m_change)
123 m_change->remap_svalue_ids (map);
124 }
125
126 int
on_svalue_purge(svalue_id first_unused_sid,const svalue_id_map & map)127 impl_region_model_context::on_svalue_purge (svalue_id first_unused_sid,
128 const svalue_id_map &map)
129 {
130 int total = 0;
131 int sm_idx;
132 sm_state_map *smap;
133 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
134 {
135 const state_machine &sm = m_ext_state.get_sm (sm_idx);
136 total += smap->on_svalue_purge (sm, sm_idx, first_unused_sid,
137 map, this);
138 }
139 if (m_change)
140 total += m_change->on_svalue_purge (first_unused_sid);
141 return total;
142 }
143
144 void
on_unknown_change(svalue_id sid)145 impl_region_model_context::on_unknown_change (svalue_id sid)
146 {
147 int sm_idx;
148 sm_state_map *smap;
149 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
150 smap->on_unknown_change (sid);
151 }
152
153 /* class setjmp_svalue : public svalue. */
154
155 /* Compare the fields of this setjmp_svalue with OTHER, returning true
156 if they are equal.
157 For use by svalue::operator==. */
158
159 bool
compare_fields(const setjmp_svalue & other) const160 setjmp_svalue::compare_fields (const setjmp_svalue &other) const
161 {
162 return m_setjmp_record == other.m_setjmp_record;
163 }
164
165 /* Implementation of svalue::add_to_hash vfunc for setjmp_svalue. */
166
167 void
add_to_hash(inchash::hash & hstate) const168 setjmp_svalue::add_to_hash (inchash::hash &hstate) const
169 {
170 hstate.add_int (m_setjmp_record.m_enode->m_index);
171 }
172
173 /* Get the index of the stored exploded_node. */
174
175 int
get_enode_index() const176 setjmp_svalue::get_enode_index () const
177 {
178 return m_setjmp_record.m_enode->m_index;
179 }
180
181 /* Implementation of svalue::print_details vfunc for setjmp_svalue. */
182
183 void
print_details(const region_model & model ATTRIBUTE_UNUSED,svalue_id this_sid ATTRIBUTE_UNUSED,pretty_printer * pp) const184 setjmp_svalue::print_details (const region_model &model ATTRIBUTE_UNUSED,
185 svalue_id this_sid ATTRIBUTE_UNUSED,
186 pretty_printer *pp) const
187 {
188 pp_printf (pp, "setjmp: EN: %i", get_enode_index ());
189 }
190
191 /* Concrete implementation of sm_context, wiring it up to the rest of this
192 file. */
193
194 class impl_sm_context : public sm_context
195 {
196 public:
impl_sm_context(exploded_graph & eg,int sm_idx,const state_machine & sm,const exploded_node * enode_for_diag,const program_state * old_state,program_state * new_state,state_change * change,const sm_state_map * old_smap,sm_state_map * new_smap,stmt_finder * stmt_finder=NULL)197 impl_sm_context (exploded_graph &eg,
198 int sm_idx,
199 const state_machine &sm,
200 const exploded_node *enode_for_diag,
201 const program_state *old_state,
202 program_state *new_state,
203 state_change *change,
204 const sm_state_map *old_smap,
205 sm_state_map *new_smap,
206 stmt_finder *stmt_finder = NULL)
207 : sm_context (sm_idx, sm),
208 m_logger (eg.get_logger ()),
209 m_eg (eg), m_enode_for_diag (enode_for_diag),
210 m_old_state (old_state), m_new_state (new_state),
211 m_change (change),
212 m_old_smap (old_smap), m_new_smap (new_smap),
213 m_stmt_finder (stmt_finder)
214 {
215 }
216
get_logger() const217 logger *get_logger () const { return m_logger.get_logger (); }
218
get_fndecl_for_call(const gcall * call)219 tree get_fndecl_for_call (const gcall *call) FINAL OVERRIDE
220 {
221 impl_region_model_context old_ctxt
222 (m_eg, m_enode_for_diag, NULL, NULL/*m_enode->get_state ()*/,
223 m_change, call);
224 region_model *model = m_new_state->m_region_model;
225 return model->get_fndecl_for_call (call, &old_ctxt);
226 }
227
on_transition(const supernode * node ATTRIBUTE_UNUSED,const gimple * stmt ATTRIBUTE_UNUSED,tree var,state_machine::state_t from,state_machine::state_t to,tree origin)228 void on_transition (const supernode *node ATTRIBUTE_UNUSED,
229 const gimple *stmt ATTRIBUTE_UNUSED,
230 tree var,
231 state_machine::state_t from,
232 state_machine::state_t to,
233 tree origin) FINAL OVERRIDE
234 {
235 logger * const logger = get_logger ();
236 LOG_FUNC (logger);
237 impl_region_model_context old_ctxt
238 (m_eg, m_enode_for_diag, NULL, NULL/*m_enode->get_state ()*/,
239 m_change, stmt);
240 svalue_id var_old_sid
241 = m_old_state->m_region_model->get_rvalue (var, &old_ctxt);
242
243 impl_region_model_context new_ctxt (m_eg, m_enode_for_diag,
244 m_old_state, m_new_state,
245 m_change, NULL);
246 svalue_id var_new_sid
247 = m_new_state->m_region_model->get_rvalue (var, &new_ctxt);
248 svalue_id origin_new_sid
249 = m_new_state->m_region_model->get_rvalue (origin, &new_ctxt);
250
251 state_machine::state_t current = m_old_smap->get_state (var_old_sid);
252 if (current == from)
253 {
254 if (logger)
255 logger->log ("%s: state transition of %qE: %s -> %s",
256 m_sm.get_name (),
257 var,
258 m_sm.get_state_name (from),
259 m_sm.get_state_name (to));
260 m_new_smap->set_state (m_new_state->m_region_model, var_new_sid,
261 to, origin_new_sid);
262 if (m_change)
263 m_change->add_sm_change (m_sm_idx, var_new_sid, from, to);
264 }
265 }
266
warn_for_state(const supernode * snode,const gimple * stmt,tree var,state_machine::state_t state,pending_diagnostic * d)267 void warn_for_state (const supernode *snode, const gimple *stmt,
268 tree var, state_machine::state_t state,
269 pending_diagnostic *d) FINAL OVERRIDE
270 {
271 LOG_FUNC (get_logger ());
272 gcc_assert (d); // take ownership
273
274 impl_region_model_context old_ctxt
275 (m_eg, m_enode_for_diag, m_old_state, m_new_state, m_change, NULL);
276 state_machine::state_t current;
277 if (var)
278 {
279 svalue_id var_old_sid
280 = m_old_state->m_region_model->get_rvalue (var, &old_ctxt);
281 current = m_old_smap->get_state (var_old_sid);
282 }
283 else
284 current = m_old_smap->get_global_state ();
285
286 if (state == current)
287 {
288 m_eg.get_diagnostic_manager ().add_diagnostic
289 (&m_sm, m_enode_for_diag, snode, stmt, m_stmt_finder,
290 var, state, d);
291 }
292 else
293 delete d;
294 }
295
296 /* Hook for picking more readable trees for SSA names of temporaries,
297 so that rather than e.g.
298 "double-free of '<unknown>'"
299 we can print:
300 "double-free of 'inbuf.data'". */
301
get_readable_tree(tree expr)302 tree get_readable_tree (tree expr) FINAL OVERRIDE
303 {
304 /* Only for SSA_NAMEs of temporaries; otherwise, return EXPR, as it's
305 likely to be the least surprising tree to report. */
306 if (TREE_CODE (expr) != SSA_NAME)
307 return expr;
308 if (SSA_NAME_VAR (expr) != NULL)
309 return expr;
310
311 gcc_assert (m_new_state);
312 svalue_id sid = m_new_state->m_region_model->get_rvalue (expr, NULL);
313 /* Find trees for all regions storing the value. */
314 auto_vec<path_var> pvs;
315 m_new_state->m_region_model->get_path_vars_for_svalue (sid, &pvs);
316 if (pvs.length () < 1)
317 return expr;
318 /* Pick the "best" such tree. */
319 // TODO: should we also consider (and consolidate) equiv classes?
320 pvs.qsort (readability_comparator);
321 return pvs[0].m_tree;
322 }
323
get_global_state() const324 state_machine::state_t get_global_state () const FINAL OVERRIDE
325 {
326 return m_old_state->m_checker_states[m_sm_idx]->get_global_state ();
327 }
328
set_global_state(state_machine::state_t state)329 void set_global_state (state_machine::state_t state) FINAL OVERRIDE
330 {
331 m_new_state->m_checker_states[m_sm_idx]->set_global_state (state);
332 }
333
on_custom_transition(custom_transition * transition)334 void on_custom_transition (custom_transition *transition) FINAL OVERRIDE
335 {
336 transition->impl_transition (&m_eg,
337 const_cast<exploded_node *> (m_enode_for_diag),
338 m_sm_idx);
339 }
340
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 state_change *m_change;
347 const sm_state_map *m_old_smap;
348 sm_state_map *m_new_smap;
349 stmt_finder *m_stmt_finder;
350 };
351
352 /* Subclass of stmt_finder for finding the best stmt to report the leak at,
353 given the emission path. */
354
355 class leak_stmt_finder : public stmt_finder
356 {
357 public:
leak_stmt_finder(const exploded_graph & eg,tree var)358 leak_stmt_finder (const exploded_graph &eg, tree var)
359 : m_eg (eg), m_var (var) {}
360
clone() const361 stmt_finder *clone () const FINAL OVERRIDE
362 {
363 return new leak_stmt_finder (m_eg, m_var);
364 }
365
find_stmt(const exploded_path & epath)366 const gimple *find_stmt (const exploded_path &epath)
367 FINAL OVERRIDE
368 {
369 logger * const logger = m_eg.get_logger ();
370 LOG_FUNC (logger);
371
372 if (TREE_CODE (m_var) == SSA_NAME)
373 {
374 /* Locate the final write to this SSA name in the path. */
375 const gimple *def_stmt = SSA_NAME_DEF_STMT (m_var);
376
377 int idx_of_def_stmt;
378 bool found = epath.find_stmt_backwards (def_stmt, &idx_of_def_stmt);
379 if (!found)
380 goto not_found;
381
382 /* What was the next write to the underlying var
383 after the SSA name was set? (if any). */
384
385 for (unsigned idx = idx_of_def_stmt + 1;
386 idx < epath.m_edges.length ();
387 ++idx)
388 {
389 const exploded_edge *eedge = epath.m_edges[idx];
390 if (logger)
391 logger->log ("eedge[%i]: EN %i -> EN %i",
392 idx,
393 eedge->m_src->m_index,
394 eedge->m_dest->m_index);
395 const exploded_node *dst_node = eedge->m_dest;
396 const program_point &dst_point = dst_node->get_point ();
397 const gimple *stmt = dst_point.get_stmt ();
398 if (!stmt)
399 continue;
400 if (const gassign *assign = dyn_cast <const gassign *> (stmt))
401 {
402 tree lhs = gimple_assign_lhs (assign);
403 if (TREE_CODE (lhs) == SSA_NAME
404 && SSA_NAME_VAR (lhs) == SSA_NAME_VAR (m_var))
405 return assign;
406 }
407 }
408 }
409
410 not_found:
411
412 /* Look backwards for the first statement with a location. */
413 int i;
414 const exploded_edge *eedge;
415 FOR_EACH_VEC_ELT_REVERSE (epath.m_edges, i, eedge)
416 {
417 if (logger)
418 logger->log ("eedge[%i]: EN %i -> EN %i",
419 i,
420 eedge->m_src->m_index,
421 eedge->m_dest->m_index);
422 const exploded_node *dst_node = eedge->m_dest;
423 const program_point &dst_point = dst_node->get_point ();
424 const gimple *stmt = dst_point.get_stmt ();
425 if (stmt)
426 if (get_pure_location (stmt->location) != UNKNOWN_LOCATION)
427 return stmt;
428 }
429
430 gcc_unreachable ();
431 return NULL;
432 }
433
434 private:
435 const exploded_graph &m_eg;
436 tree m_var;
437 };
438
439 /* A measurement of how good EXPR is for presenting to the user, so
440 that e.g. we can say prefer printing
441 "leak of 'tmp.m_ptr'"
442 over:
443 "leak of '<unknown>'". */
444
445 static int
readability(const_tree expr)446 readability (const_tree expr)
447 {
448 gcc_assert (expr);
449 switch (TREE_CODE (expr))
450 {
451 case COMPONENT_REF:
452 case MEM_REF:
453 /* Impose a slight readability penalty relative to that of
454 operand 0. */
455 return readability (TREE_OPERAND (expr, 0)) - 1;
456
457 case SSA_NAME:
458 {
459 if (tree var = SSA_NAME_VAR (expr))
460 return readability (var);
461 /* Avoid printing '<unknown>' for SSA names for temporaries. */
462 return -1;
463 }
464 break;
465
466 case VAR_DECL:
467 /* Arbitrarily-chosen "high readability" value. */
468 return 256;
469
470 default:
471 return 0;
472 }
473
474 return 0;
475 }
476
477 /* A qsort comparator for trees to sort them into most user-readable to
478 least user-readable. */
479
480 static int
readability_comparator(const void * p1,const void * p2)481 readability_comparator (const void *p1, const void *p2)
482 {
483 path_var pv1 = *(path_var const *)p1;
484 path_var pv2 = *(path_var const *)p2;
485
486 /* TODO: should we consider stack depths? */
487 int r1 = readability (pv1.m_tree);
488 int r2 = readability (pv2.m_tree);
489
490 return r2 - r1;
491 }
492
493 /* Create an sm_context and use it to call SM's on_leak vfunc, so that
494 it can potentially complain about a leak of DST_SID (in a new region_model)
495 in the given STATE, where MAP can be used to map SID back to an "old"
496 region_model. */
497
498 void
on_state_leak(const state_machine & sm,int sm_idx,svalue_id dst_sid,svalue_id first_unused_sid,const svalue_id_map & map,state_machine::state_t state)499 impl_region_model_context::on_state_leak (const state_machine &sm,
500 int sm_idx,
501 svalue_id dst_sid,
502 svalue_id first_unused_sid,
503 const svalue_id_map &map,
504 state_machine::state_t state)
505 {
506 logger * const logger = get_logger ();
507 LOG_SCOPE (logger);
508 if (logger)
509 logger->log ("considering leak of sv%i", dst_sid.as_int ());
510
511 if (!m_eg)
512 return;
513
514 /* m_old_state also needs to be non-NULL so that the sm_ctxt can look
515 up the old state of the sid. */
516 gcc_assert (m_old_state);
517
518 /* Don't report on sid leaking if it's equal to one of the used sids.
519 For example, given:
520 some_non_trivial_expression = malloc (sizeof (struct foo));
521 we have:
522 _1 = malloc; (void *)
523 some_non_trivial_expression = _1; (struct foo *)
524 and at leak-detection time we may have:
525 sv5: {type: 'struct foo *', &r3} (used)
526 sv6: {type: 'void *', &r3} (unused)
527 where both point to the same region. We don't want to report a
528 leak of sv6, so we reject the report due to its equality with sv5. */
529 gcc_assert (m_new_state);
530 gcc_assert (!first_unused_sid.null_p ());
531 for (int i = 0; i < first_unused_sid.as_int (); i++)
532 {
533 svalue_id used_sid = svalue_id::from_int (i);
534
535 /* Use the "_without_cm" form of eval_condition, since
536 we're half-way through purging - we don't want to introduce new
537 equivalence classes into the constraint_manager for "sid" and
538 for each of the used_sids. */
539 const region_model &rm = *m_new_state->m_region_model;
540 tristate eq = rm.eval_condition_without_cm (dst_sid, EQ_EXPR, used_sid);
541 if (eq.is_true ())
542 {
543 if (logger)
544 logger->log ("rejecting leak of sv%i due to equality with sv%i",
545 dst_sid.as_int (), used_sid.as_int ());
546 return;
547 }
548 }
549
550 /* SID has leaked within the new state: no regions use it.
551 We need to convert it back to a tree, but since no regions use it, we
552 have to use MAP to convert it back to an svalue_id within the old state.
553 We can then look that svalue_id up to locate regions and thus tree(s)
554 that use it. */
555
556 svalue_id old_sid = map.get_src_for_dst (dst_sid);
557
558 auto_vec<path_var> leaked_pvs;
559 m_old_state->m_region_model->get_path_vars_for_svalue (old_sid, &leaked_pvs);
560
561 if (leaked_pvs.length () < 1)
562 return;
563
564 /* Find "best" leaked tree.
565 Sort the leaks into most human-readable first, through
566 to least user-readable. Given that we only emit one
567 leak per EC, this ought to ensure that we pick the most
568 user-readable description of each leaking EC.
569 This assumes that all vars in the EC have the same state. */
570 leaked_pvs.qsort (readability_comparator);
571
572 tree leaked_tree = leaked_pvs[0].m_tree;
573 if (logger)
574 logger->log ("best leaked_tree: %qE", leaked_tree);
575
576 leak_stmt_finder stmt_finder (*m_eg, leaked_tree);
577 impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
578 m_old_state, m_new_state,
579 m_change,
580 m_old_state->m_checker_states[sm_idx],
581 m_new_state->m_checker_states[sm_idx],
582 &stmt_finder);
583 gcc_assert (m_enode_for_diag);
584
585 /* Don't complain about leaks when returning from "main". */
586 if (m_enode_for_diag->get_supernode ()
587 && m_enode_for_diag->get_supernode ()->return_p ())
588 {
589 tree fndecl = m_enode_for_diag->get_function ()->decl;
590 if (0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "main"))
591 {
592 if (logger)
593 logger->log ("not reporting leak from main");
594 return;
595 }
596 }
597
598 pending_diagnostic *pd = sm.on_leak (leaked_tree);
599 if (pd)
600 m_eg->get_diagnostic_manager ().add_diagnostic
601 (&sm, m_enode_for_diag, m_enode_for_diag->get_supernode (),
602 m_stmt, &stmt_finder,
603 leaked_tree, state, pd);
604 }
605
606 /* Implementation of region_model_context::on_inherited_svalue vfunc
607 for impl_region_model_context.
608 Notify all checkers that CHILD_SID has been created from PARENT_SID,
609 so that those state machines that inherit state can propagate the state
610 from parent to child. */
611
612 void
on_inherited_svalue(svalue_id parent_sid,svalue_id child_sid)613 impl_region_model_context::on_inherited_svalue (svalue_id parent_sid,
614 svalue_id child_sid)
615 {
616 if (!m_new_state)
617 return;
618
619 int sm_idx;
620 sm_state_map *smap;
621 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
622 {
623 const state_machine &sm = m_ext_state.get_sm (sm_idx);
624 if (sm.inherited_state_p ())
625 smap->on_inherited_svalue (parent_sid, child_sid);
626 }
627 }
628
629 /* Implementation of region_model_context::on_cast vfunc
630 for impl_region_model_context.
631 Notify all checkers that DST_SID is a cast of SRC_SID, so that sm-state
632 can be propagated from src to dst. */
633
634 void
on_cast(svalue_id src_sid,svalue_id dst_sid)635 impl_region_model_context::on_cast (svalue_id src_sid,
636 svalue_id dst_sid)
637 {
638 if (!m_new_state)
639 return;
640
641 int sm_idx;
642 sm_state_map *smap;
643 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
644 smap->on_cast (src_sid, dst_sid);
645 }
646
647 /* Implementation of region_model_context::on_condition vfunc.
648 Notify all state machines about the condition, which could lead to
649 state transitions. */
650
651 void
on_condition(tree lhs,enum tree_code op,tree rhs)652 impl_region_model_context::on_condition (tree lhs, enum tree_code op, tree rhs)
653 {
654 int sm_idx;
655 sm_state_map *smap;
656 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
657 {
658 const state_machine &sm = m_ext_state.get_sm (sm_idx);
659 impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
660 m_old_state, m_new_state,
661 m_change,
662 m_old_state->m_checker_states[sm_idx],
663 m_new_state->m_checker_states[sm_idx]);
664 sm.on_condition (&sm_ctxt,
665 m_enode_for_diag->get_supernode (), m_stmt,
666 lhs, op, rhs);
667 }
668 }
669
670 /* Implementation of region_model_context::on_phi vfunc.
671 Notify all state machines about the phi, which could lead to
672 state transitions. */
673
674 void
on_phi(const gphi * phi,tree rhs)675 impl_region_model_context::on_phi (const gphi *phi, tree rhs)
676 {
677 int sm_idx;
678 sm_state_map *smap;
679 FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
680 {
681 const state_machine &sm = m_ext_state.get_sm (sm_idx);
682 impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
683 m_old_state, m_new_state,
684 m_change,
685 m_old_state->m_checker_states[sm_idx],
686 m_new_state->m_checker_states[sm_idx]);
687 sm.on_phi (&sm_ctxt, m_enode_for_diag->get_supernode (), phi, rhs);
688 }
689 }
690
691 /* Implementation of region_model_context::on_unexpected_tree_code vfunc.
692 Mark the new state as being invalid for further exploration.
693 TODO(stage1): introduce a warning for when this occurs. */
694
695 void
on_unexpected_tree_code(tree t,const dump_location_t & loc)696 impl_region_model_context::on_unexpected_tree_code (tree t,
697 const dump_location_t &loc)
698 {
699 logger * const logger = get_logger ();
700 if (logger)
701 logger->log ("unhandled tree code: %qs in %qs at %s:%i",
702 t ? get_tree_code_name (TREE_CODE (t)) : "(null)",
703 loc.get_impl_location ().m_function,
704 loc.get_impl_location ().m_file,
705 loc.get_impl_location ().m_line);
706 if (m_new_state)
707 m_new_state->m_valid = false;
708 }
709
710 /* struct point_and_state. */
711
712 /* Assert that this object is sane. */
713
714 void
validate(const extrinsic_state & ext_state) const715 point_and_state::validate (const extrinsic_state &ext_state) const
716 {
717 /* Skip this in a release build. */
718 #if !CHECKING_P
719 return;
720 #endif
721
722 m_point.validate ();
723
724 m_state.validate (ext_state);
725
726 /* Verify that the callstring's model of the stack corresponds to that
727 of the region_model. */
728 /* They should have the same depth. */
729 gcc_assert (m_point.get_stack_depth ()
730 == m_state.m_region_model->get_stack_depth ());
731 /* Check the functions in the callstring vs those in the frames
732 at each depth. */
733 for (int depth = 0; depth < m_point.get_stack_depth (); ++depth)
734 {
735 gcc_assert (m_point.get_function_at_depth (depth)
736 == m_state.m_region_model->get_function_at_depth (depth));
737 }
738 }
739
740 /* Subroutine of print_enode_indices: print a run of indices from START_IDX
741 to END_IDX to PP, using and updating *FIRST_RUN. */
742
743 static void
print_run(pretty_printer * pp,int start_idx,int end_idx,bool * first_run)744 print_run (pretty_printer *pp, int start_idx, int end_idx,
745 bool *first_run)
746 {
747 if (!(*first_run))
748 pp_string (pp, ", ");
749 *first_run = false;
750 if (start_idx == end_idx)
751 pp_printf (pp, "EN: %i", start_idx);
752 else
753 pp_printf (pp, "EN: %i-%i", start_idx, end_idx);
754 }
755
756 /* Print the indices within ENODES to PP, collecting them as
757 runs/singletons e.g. "EN: 4-7, EN: 20-23, EN: 42". */
758
759 static void
print_enode_indices(pretty_printer * pp,const auto_vec<exploded_node * > & enodes)760 print_enode_indices (pretty_printer *pp,
761 const auto_vec<exploded_node *> &enodes)
762 {
763 int cur_start_idx = -1;
764 int cur_finish_idx = -1;
765 bool first_run = true;
766 unsigned i;
767 exploded_node *enode;
768 FOR_EACH_VEC_ELT (enodes, i, enode)
769 {
770 if (cur_start_idx == -1)
771 {
772 gcc_assert (cur_finish_idx == -1);
773 cur_start_idx = cur_finish_idx = enode->m_index;
774 }
775 else
776 {
777 if (enode->m_index == cur_finish_idx + 1)
778 /* Continuation of a run. */
779 cur_finish_idx = enode->m_index;
780 else
781 {
782 /* Finish existing run, start a new one. */
783 gcc_assert (cur_start_idx >= 0);
784 gcc_assert (cur_finish_idx >= 0);
785 print_run (pp, cur_start_idx, cur_finish_idx,
786 &first_run);
787 cur_start_idx = cur_finish_idx = enode->m_index;
788 }
789 }
790 }
791 /* Finish any existing run. */
792 if (cur_start_idx >= 0)
793 {
794 gcc_assert (cur_finish_idx >= 0);
795 print_run (pp, cur_start_idx, cur_finish_idx,
796 &first_run);
797 }
798 }
799
800 /* class exploded_node : public dnode<eg_traits>. */
801
802 /* exploded_node's ctor. */
803
exploded_node(const point_and_state & ps,int index)804 exploded_node::exploded_node (const point_and_state &ps,
805 int index)
806 : m_ps (ps), m_status (STATUS_WORKLIST), m_index (index)
807 {
808 gcc_checking_assert (ps.get_state ().m_region_model->canonicalized_p ());
809 }
810
811 /* For use by dump_dot, get a value for the .dot "fillcolor" attribute.
812 Colorize by sm-state, to make it easier to see how sm-state propagates
813 through the exploded_graph. */
814
815 const char *
get_dot_fillcolor() const816 exploded_node::get_dot_fillcolor () const
817 {
818 const program_state &state = get_state ();
819
820 /* We want to be able to easily distinguish the no-sm-state case,
821 and to be able to distinguish cases where there's a single state
822 from each other.
823
824 Sum the sm_states, and use the result to choose from a table,
825 modulo table-size, special-casing the "no sm-state" case. */
826 int total_sm_state = 0;
827 int i;
828 sm_state_map *smap;
829 FOR_EACH_VEC_ELT (state.m_checker_states, i, smap)
830 {
831 for (sm_state_map::iterator_t iter = smap->begin ();
832 iter != smap->end ();
833 ++iter)
834 total_sm_state += (*iter).second.m_state;
835 total_sm_state += smap->get_global_state ();
836 }
837
838 if (total_sm_state > 0)
839 {
840 /* An arbitrarily-picked collection of light colors. */
841 const char * const colors[]
842 = {"azure", "coral", "cornsilk", "lightblue", "yellow"};
843 const int num_colors = sizeof (colors) / sizeof (colors[0]);
844 return colors[total_sm_state % num_colors];
845 }
846 else
847 /* No sm-state. */
848 return "lightgrey";
849 }
850
851 /* Implementation of dnode::dump_dot vfunc for exploded_node. */
852
853 void
dump_dot(graphviz_out * gv,const dump_args_t & args) const854 exploded_node::dump_dot (graphviz_out *gv, const dump_args_t &args) const
855 {
856 pretty_printer *pp = gv->get_pp ();
857
858 dump_dot_id (pp);
859 pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
860 get_dot_fillcolor ());
861 pp_write_text_to_stream (pp);
862
863 pp_printf (pp, "EN: %i", m_index);
864 if (m_status == STATUS_MERGER)
865 pp_string (pp, " (merger)");
866 pp_newline (pp);
867
868 format f (true);
869 m_ps.get_point ().print (pp, f);
870 pp_newline (pp);
871
872 const extrinsic_state &ext_state = args.m_eg.get_ext_state ();
873 const program_state &state = m_ps.get_state ();
874 state.dump_to_pp (ext_state, true, pp);
875 pp_newline (pp);
876
877 {
878 int i;
879 sm_state_map *smap;
880 FOR_EACH_VEC_ELT (state.m_checker_states, i, smap)
881 {
882 if (!smap->is_empty_p ())
883 {
884 pp_printf (pp, "%s: ", ext_state.get_name (i));
885 smap->print (ext_state.get_sm (i), state.m_region_model, pp);
886 pp_newline (pp);
887 }
888 }
889 }
890
891 /* Dump any saved_diagnostics at this enode. */
892 {
893 const diagnostic_manager &dm = args.m_eg.get_diagnostic_manager ();
894 for (unsigned i = 0; i < dm.get_num_diagnostics (); i++)
895 {
896 const saved_diagnostic *sd = dm.get_saved_diagnostic (i);
897 if (sd->m_enode == this)
898 {
899 pp_printf (pp, "DIAGNOSTIC: %s", sd->m_d->get_kind ());
900 pp_newline (pp);
901 }
902 }
903 }
904
905 pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
906
907 pp_string (pp, "\"];\n\n");
908 pp_flush (pp);
909 }
910
911 /* Dump this to PP in a form suitable for use as an id in .dot output. */
912
913 void
dump_dot_id(pretty_printer * pp) const914 exploded_node::dump_dot_id (pretty_printer *pp) const
915 {
916 pp_printf (pp, "exploded_node_%i", m_index);
917 }
918
919 /* Dump a multiline representation of this node to PP. */
920
921 void
dump_to_pp(pretty_printer * pp,const extrinsic_state & ext_state) const922 exploded_node::dump_to_pp (pretty_printer *pp,
923 const extrinsic_state &ext_state) const
924 {
925 pp_printf (pp, "EN: %i", m_index);
926 pp_newline (pp);
927
928 format f (true);
929 m_ps.get_point ().print (pp, f);
930 pp_newline (pp);
931
932 m_ps.get_state ().dump_to_pp (ext_state, false, pp);
933 pp_newline (pp);
934 }
935
936 /* Dump a multiline representation of this node to FILE. */
937
938 void
dump(FILE * fp,const extrinsic_state & ext_state) const939 exploded_node::dump (FILE *fp,
940 const extrinsic_state &ext_state) const
941 {
942 pretty_printer pp;
943 pp_format_decoder (&pp) = default_tree_printer;
944 pp_show_color (&pp) = pp_show_color (global_dc->printer);
945 pp.buffer->stream = fp;
946 dump_to_pp (&pp, ext_state);
947 pp_flush (&pp);
948 }
949
950 /* Dump a multiline representation of this node to stderr. */
951
952 DEBUG_FUNCTION void
dump(const extrinsic_state & ext_state) const953 exploded_node::dump (const extrinsic_state &ext_state) const
954 {
955 dump (stderr, ext_state);
956 }
957
958 } // namespace ana
959
960 /* Return true if FNDECL has a gimple body. */
961 // TODO: is there a pre-canned way to do this?
962
963 bool
fndecl_has_gimple_body_p(tree fndecl)964 fndecl_has_gimple_body_p (tree fndecl)
965 {
966 if (fndecl == NULL_TREE)
967 return false;
968
969 cgraph_node *n = cgraph_node::get (fndecl);
970 if (!n)
971 return false;
972
973 return n->has_gimple_body_p ();
974 }
975
976 namespace ana {
977
978 /* A pending_diagnostic subclass for implementing "__analyzer_dump_path". */
979
980 class dump_path_diagnostic
981 : public pending_diagnostic_subclass<dump_path_diagnostic>
982 {
983 public:
emit(rich_location * richloc)984 bool emit (rich_location *richloc) FINAL OVERRIDE
985 {
986 inform (richloc, "path");
987 return true;
988 }
989
get_kind() const990 const char *get_kind () const FINAL OVERRIDE { return "dump_path_diagnostic"; }
991
operator ==(const dump_path_diagnostic &) const992 bool operator== (const dump_path_diagnostic &) const
993 {
994 return true;
995 }
996 };
997
998 /* Modify STATE in place, applying the effects of the stmt at this node's
999 point. */
1000
1001 exploded_node::on_stmt_flags
on_stmt(exploded_graph & eg,const supernode * snode,const gimple * stmt,program_state * state,state_change * change) const1002 exploded_node::on_stmt (exploded_graph &eg,
1003 const supernode *snode,
1004 const gimple *stmt,
1005 program_state *state,
1006 state_change *change) const
1007 {
1008 /* Preserve the old state. It is used here for looking
1009 up old checker states, for determining state transitions, and
1010 also within impl_region_model_context and impl_sm_context for
1011 going from tree to svalue_id. */
1012 const program_state old_state (*state);
1013
1014 impl_region_model_context ctxt (eg, this,
1015 &old_state, state, change,
1016 stmt);
1017
1018 if (const gassign *assign = dyn_cast <const gassign *> (stmt))
1019 state->m_region_model->on_assignment (assign, &ctxt);
1020
1021 if (const greturn *return_ = dyn_cast <const greturn *> (stmt))
1022 state->m_region_model->on_return (return_, &ctxt);
1023
1024 /* Track whether we have a gcall to a function that's not recognized by
1025 anything, for which we don't have a function body, or for which we
1026 don't know the fndecl. */
1027 bool unknown_side_effects = false;
1028 if (const gcall *call = dyn_cast <const gcall *> (stmt))
1029 {
1030 /* Debugging/test support. */
1031 if (is_special_named_call_p (call, "__analyzer_dump", 0))
1032 {
1033 /* Handle the builtin "__analyzer_dump" by dumping state
1034 to stderr. */
1035 dump (eg.get_ext_state ());
1036 }
1037 else if (is_special_named_call_p (call, "__analyzer_dump_path", 0))
1038 {
1039 /* Handle the builtin "__analyzer_dump_path" by queuing a
1040 diagnostic at this exploded_node. */
1041 ctxt.warn (new dump_path_diagnostic ());
1042 }
1043 else if (is_special_named_call_p (call, "__analyzer_dump_region_model", 0))
1044 {
1045 /* Handle the builtin "__analyzer_dump_region_model" by dumping
1046 the region model's state to stderr. */
1047 state->m_region_model->dump (false);
1048 }
1049 else if (is_special_named_call_p (call, "__analyzer_eval", 1))
1050 {
1051 /* Handle the builtin "__analyzer_eval" by evaluating the input
1052 and dumping as a dummy warning, so that test cases can use
1053 dg-warning to validate the result (and so unexpected warnings will
1054 lead to DejaGnu failures). */
1055 tree t_arg = gimple_call_arg (call, 0);
1056 tristate t
1057 = state->m_region_model->eval_condition (t_arg,
1058 NE_EXPR,
1059 integer_zero_node,
1060 &ctxt);
1061 warning_at (call->location, 0, "%s", t.as_string ());
1062 }
1063 else if (is_special_named_call_p (call, "__analyzer_break", 0))
1064 {
1065 /* Handle the builtin "__analyzer_break" by triggering a
1066 breakpoint. */
1067 /* TODO: is there a good cross-platform way to do this? */
1068 raise (SIGINT);
1069 }
1070 else if (is_special_named_call_p (call, "__analyzer_dump_exploded_nodes",
1071 1))
1072 {
1073 /* This is handled elsewhere. */
1074 }
1075 else if (is_setjmp_call_p (call))
1076 state->m_region_model->on_setjmp (call, this, &ctxt);
1077 else if (is_longjmp_call_p (call))
1078 {
1079 on_longjmp (eg, call, state, &ctxt);
1080 return on_stmt_flags::terminate_path ();
1081 }
1082 else
1083 unknown_side_effects = state->m_region_model->on_call_pre (call, &ctxt);
1084 }
1085
1086 bool any_sm_changes = false;
1087 int sm_idx;
1088 sm_state_map *smap;
1089 FOR_EACH_VEC_ELT (old_state.m_checker_states, sm_idx, smap)
1090 {
1091 const state_machine &sm = eg.get_ext_state ().get_sm (sm_idx);
1092 const sm_state_map *old_smap
1093 = old_state.m_checker_states[sm_idx];
1094 sm_state_map *new_smap = state->m_checker_states[sm_idx];
1095 impl_sm_context sm_ctxt (eg, sm_idx, sm, this, &old_state, state,
1096 change,
1097 old_smap, new_smap);
1098 /* Allow the state_machine to handle the stmt. */
1099 if (sm.on_stmt (&sm_ctxt, snode, stmt))
1100 unknown_side_effects = false;
1101 else
1102 {
1103 /* For those stmts that were not handled by the state machine. */
1104 if (const gcall *call = dyn_cast <const gcall *> (stmt))
1105 {
1106 tree callee_fndecl
1107 = state->m_region_model->get_fndecl_for_call (call, &ctxt);
1108
1109 if (!fndecl_has_gimple_body_p (callee_fndecl))
1110 new_smap->purge_for_unknown_fncall (eg, sm, call, callee_fndecl,
1111 state->m_region_model,
1112 &ctxt);
1113 }
1114 }
1115 if (*old_smap != *new_smap)
1116 any_sm_changes = true;
1117 }
1118
1119 if (const gcall *call = dyn_cast <const gcall *> (stmt))
1120 state->m_region_model->on_call_post (call, unknown_side_effects, &ctxt);
1121
1122 return on_stmt_flags (any_sm_changes);
1123 }
1124
1125 /* Consider the effect of following superedge SUCC from this node.
1126
1127 Return true if it's feasible to follow the edge, or false
1128 if it's infeasible.
1129
1130 Examples: if it's the "true" branch within
1131 a CFG and we know the conditional is false, we know it's infeasible.
1132 If it's one of multiple interprocedual "return" edges, then only
1133 the edge back to the most recent callsite is feasible.
1134
1135 Update NEXT_STATE accordingly (e.g. to record that a condition was
1136 true or false, or that the NULL-ness of a pointer has been checked,
1137 pushing/popping stack frames, etc).
1138
1139 Update NEXT_POINT accordingly (updating the call string). */
1140
1141 bool
on_edge(exploded_graph & eg,const superedge * succ,program_point * next_point,program_state * next_state,state_change * change) const1142 exploded_node::on_edge (exploded_graph &eg,
1143 const superedge *succ,
1144 program_point *next_point,
1145 program_state *next_state,
1146 state_change *change) const
1147 {
1148 LOG_FUNC (eg.get_logger ());
1149
1150 if (!next_point->on_edge (eg, succ))
1151 return false;
1152
1153 if (!next_state->on_edge (eg, *this, succ, change))
1154 return false;
1155
1156 return true;
1157 }
1158
1159 /* Verify that the stack at LONGJMP_POINT is still valid, given a call
1160 to "setjmp" at SETJMP_POINT - the stack frame that "setjmp" was
1161 called in must still be valid.
1162
1163 Caveat: this merely checks the call_strings in the points; it doesn't
1164 detect the case where a frame returns and is then called again. */
1165
1166 static bool
valid_longjmp_stack_p(const program_point & longjmp_point,const program_point & setjmp_point)1167 valid_longjmp_stack_p (const program_point &longjmp_point,
1168 const program_point &setjmp_point)
1169 {
1170 const call_string &cs_at_longjmp = longjmp_point.get_call_string ();
1171 const call_string &cs_at_setjmp = setjmp_point.get_call_string ();
1172
1173 if (cs_at_longjmp.length () < cs_at_setjmp.length ())
1174 return false;
1175
1176 /* Check that the call strings match, up to the depth of the
1177 setjmp point. */
1178 for (unsigned depth = 0; depth < cs_at_setjmp.length (); depth++)
1179 if (cs_at_longjmp[depth] != cs_at_setjmp[depth])
1180 return false;
1181
1182 return true;
1183 }
1184
1185 /* A pending_diagnostic subclass for complaining about bad longjmps,
1186 where the enclosing function of the "setjmp" has returned (and thus
1187 the stack frame no longer exists). */
1188
1189 class stale_jmp_buf : public pending_diagnostic_subclass<dump_path_diagnostic>
1190 {
1191 public:
stale_jmp_buf(const gcall * setjmp_call,const gcall * longjmp_call)1192 stale_jmp_buf (const gcall *setjmp_call, const gcall *longjmp_call)
1193 : m_setjmp_call (setjmp_call), m_longjmp_call (longjmp_call)
1194 {}
1195
emit(rich_location * richloc)1196 bool emit (rich_location *richloc) FINAL OVERRIDE
1197 {
1198 return warning_at
1199 (richloc, OPT_Wanalyzer_stale_setjmp_buffer,
1200 "%qs called after enclosing function of %qs has returned",
1201 get_user_facing_name (m_longjmp_call),
1202 get_user_facing_name (m_setjmp_call));
1203 }
1204
get_kind() const1205 const char *get_kind () const FINAL OVERRIDE
1206 { return "stale_jmp_buf"; }
1207
operator ==(const stale_jmp_buf & other) const1208 bool operator== (const stale_jmp_buf &other) const
1209 {
1210 return (m_setjmp_call == other.m_setjmp_call
1211 && m_longjmp_call == other.m_longjmp_call);
1212 }
1213
1214 private:
1215 const gcall *m_setjmp_call;
1216 const gcall *m_longjmp_call;
1217 };
1218
1219 /* Handle LONGJMP_CALL, a call to longjmp or siglongjmp.
1220
1221 Attempt to locate where setjmp/sigsetjmp was called on the jmp_buf and build
1222 an exploded_node and exploded_edge to it representing a rewind to that frame,
1223 handling the various kinds of failure that can occur. */
1224
1225 void
on_longjmp(exploded_graph & eg,const gcall * longjmp_call,program_state * new_state,region_model_context * ctxt) const1226 exploded_node::on_longjmp (exploded_graph &eg,
1227 const gcall *longjmp_call,
1228 program_state *new_state,
1229 region_model_context *ctxt) const
1230 {
1231 tree buf_ptr = gimple_call_arg (longjmp_call, 0);
1232
1233 region_model *new_region_model = new_state->m_region_model;
1234 region_id buf_rid = new_region_model->deref_rvalue (buf_ptr, ctxt);
1235 region *buf = new_region_model->get_region (buf_rid);
1236 if (!buf)
1237 return;
1238
1239 svalue_id buf_content_sid
1240 = buf->get_value (*new_region_model, false, ctxt);
1241 svalue *buf_content_sval = new_region_model->get_svalue (buf_content_sid);
1242 if (!buf_content_sval)
1243 return;
1244 setjmp_svalue *setjmp_sval = buf_content_sval->dyn_cast_setjmp_svalue ();
1245 if (!setjmp_sval)
1246 return;
1247
1248 const setjmp_record tmp_setjmp_record = setjmp_sval->get_setjmp_record ();
1249
1250 /* Build a custom enode and eedge for rewinding from the longjmp/siglongjmp
1251 call back to the setjmp/sigsetjmp. */
1252 rewind_info_t rewind_info (tmp_setjmp_record, longjmp_call);
1253
1254 const gcall *setjmp_call = rewind_info.get_setjmp_call ();
1255 const program_point &setjmp_point = rewind_info.get_setjmp_point ();
1256
1257 const program_point &longjmp_point = get_point ();
1258
1259 /* Verify that the setjmp's call_stack hasn't been popped. */
1260 if (!valid_longjmp_stack_p (longjmp_point, setjmp_point))
1261 {
1262 ctxt->warn (new stale_jmp_buf (setjmp_call, longjmp_call));
1263 return;
1264 }
1265
1266 gcc_assert (longjmp_point.get_stack_depth ()
1267 >= setjmp_point.get_stack_depth ());
1268
1269 /* Update the state for use by the destination node. */
1270
1271 /* Stash the current number of diagnostics so that we can update
1272 any that this adds to show where the longjmp is rewinding to. */
1273
1274 diagnostic_manager *dm = &eg.get_diagnostic_manager ();
1275 unsigned prev_num_diagnostics = dm->get_num_diagnostics ();
1276
1277 new_region_model->on_longjmp (longjmp_call, setjmp_call,
1278 setjmp_point.get_stack_depth (), ctxt);
1279
1280 program_point next_point
1281 = program_point::after_supernode (setjmp_point.get_supernode (),
1282 setjmp_point.get_call_string ());
1283
1284 state_change change;
1285 exploded_node *next = eg.get_or_create_node (next_point, *new_state, &change);
1286
1287 /* Create custom exploded_edge for a longjmp. */
1288 if (next)
1289 {
1290 exploded_edge *eedge
1291 = eg.add_edge (const_cast<exploded_node *> (this), next, NULL,
1292 change,
1293 new rewind_info_t (tmp_setjmp_record, longjmp_call));
1294
1295 /* For any diagnostics that were queued here (such as leaks) we want
1296 the checker_path to show the rewinding events after the "final event"
1297 so that the user sees where the longjmp is rewinding to (otherwise the
1298 path is meaningless).
1299
1300 For example, we want to emit something like:
1301 | NN | {
1302 | NN | longjmp (env, 1);
1303 | | ~~~~~~~~~~~~~~~~
1304 | | |
1305 | | (10) 'ptr' leaks here; was allocated at (7)
1306 | | (11) rewinding from 'longjmp' in 'inner'...
1307 |
1308 <-------------+
1309 |
1310 'outer': event 12
1311 |
1312 | NN | i = setjmp(env);
1313 | | ^~~~~~
1314 | | |
1315 | | (12) ...to 'setjmp' in 'outer' (saved at (2))
1316
1317 where the "final" event above is event (10), but we want to append
1318 events (11) and (12) afterwards.
1319
1320 Do this by setting m_trailing_eedge on any diagnostics that were
1321 just saved. */
1322 unsigned num_diagnostics = dm->get_num_diagnostics ();
1323 for (unsigned i = prev_num_diagnostics; i < num_diagnostics; i++)
1324 {
1325 saved_diagnostic *sd = dm->get_saved_diagnostic (i);
1326 sd->m_trailing_eedge = eedge;
1327 }
1328 }
1329 }
1330
1331 /* Subroutine of exploded_graph::process_node for finding the successors
1332 of the supernode for a function exit basic block.
1333
1334 Ensure that pop_frame is called, potentially queuing diagnostics about
1335 leaks. */
1336
1337 void
detect_leaks(exploded_graph & eg) const1338 exploded_node::detect_leaks (exploded_graph &eg) const
1339 {
1340 LOG_FUNC_1 (eg.get_logger (), "EN: %i", m_index);
1341
1342 gcc_assert (get_point ().get_supernode ()->return_p ());
1343
1344 /* If we're not a "top-level" function, do nothing; pop_frame
1345 will be called when handling the return superedge. */
1346 if (get_point ().get_stack_depth () > 1)
1347 return;
1348
1349 /* We have a "top-level" function. */
1350 gcc_assert (get_point ().get_stack_depth () == 1);
1351
1352 const program_state &old_state = get_state ();
1353
1354 /* Work with a temporary copy of the state: pop the frame, and see
1355 what leaks (via purge_unused_svalues). */
1356 program_state new_state (old_state);
1357
1358 gcc_assert (new_state.m_region_model);
1359
1360 purge_stats stats;
1361 impl_region_model_context ctxt (eg, this,
1362 &old_state, &new_state,
1363 NULL,
1364 get_stmt ());
1365 new_state.m_region_model->pop_frame (region_id::null (),
1366 true, &stats, &ctxt);
1367 }
1368
1369 /* Dump the successors and predecessors of this enode to OUTF. */
1370
1371 void
dump_succs_and_preds(FILE * outf) const1372 exploded_node::dump_succs_and_preds (FILE *outf) const
1373 {
1374 unsigned i;
1375 exploded_edge *e;
1376 {
1377 auto_vec<exploded_node *> preds (m_preds.length ());
1378 FOR_EACH_VEC_ELT (m_preds, i, e)
1379 preds.quick_push (e->m_src);
1380 pretty_printer pp;
1381 print_enode_indices (&pp, preds);
1382 fprintf (outf, "preds: %s\n",
1383 pp_formatted_text (&pp));
1384 }
1385 {
1386 auto_vec<exploded_node *> succs (m_succs.length ());
1387 FOR_EACH_VEC_ELT (m_succs, i, e)
1388 succs.quick_push (e->m_dest);
1389 pretty_printer pp;
1390 print_enode_indices (&pp, succs);
1391 fprintf (outf, "succs: %s\n",
1392 pp_formatted_text (&pp));
1393 }
1394 }
1395
1396 /* class rewind_info_t : public exploded_edge::custom_info_t. */
1397
1398 /* Implementation of exploded_edge::custom_info_t::update_model vfunc
1399 for rewind_info_t.
1400
1401 Update state for the special-case of a rewind of a longjmp
1402 to a setjmp (which doesn't have a superedge, but does affect
1403 state). */
1404
1405 void
update_model(region_model * model,const exploded_edge & eedge)1406 rewind_info_t::update_model (region_model *model,
1407 const exploded_edge &eedge)
1408 {
1409 const program_point &longjmp_point = eedge.m_src->get_point ();
1410 const program_point &setjmp_point = eedge.m_dest->get_point ();
1411
1412 gcc_assert (longjmp_point.get_stack_depth ()
1413 >= setjmp_point.get_stack_depth ());
1414
1415 model->on_longjmp (get_longjmp_call (),
1416 get_setjmp_call (),
1417 setjmp_point.get_stack_depth (), NULL);
1418 }
1419
1420 /* Implementation of exploded_edge::custom_info_t::add_events_to_path vfunc
1421 for rewind_info_t. */
1422
1423 void
add_events_to_path(checker_path * emission_path,const exploded_edge & eedge)1424 rewind_info_t::add_events_to_path (checker_path *emission_path,
1425 const exploded_edge &eedge)
1426 {
1427 const exploded_node *src_node = eedge.m_src;
1428 const program_point &src_point = src_node->get_point ();
1429 const int src_stack_depth = src_point.get_stack_depth ();
1430 const exploded_node *dst_node = eedge.m_dest;
1431 const program_point &dst_point = dst_node->get_point ();
1432 const int dst_stack_depth = dst_point.get_stack_depth ();
1433
1434 emission_path->add_event
1435 (new rewind_from_longjmp_event
1436 (&eedge, get_longjmp_call ()->location,
1437 src_point.get_fndecl (),
1438 src_stack_depth, this));
1439 emission_path->add_event
1440 (new rewind_to_setjmp_event
1441 (&eedge, get_setjmp_call ()->location,
1442 dst_point.get_fndecl (),
1443 dst_stack_depth, this));
1444 }
1445
1446 /* class exploded_edge : public dedge<eg_traits>. */
1447
1448 /* exploded_edge's ctor. */
1449
exploded_edge(exploded_node * src,exploded_node * dest,const extrinsic_state & ext_state,const superedge * sedge,const state_change & change,custom_info_t * custom_info)1450 exploded_edge::exploded_edge (exploded_node *src, exploded_node *dest,
1451 const extrinsic_state &ext_state,
1452 const superedge *sedge,
1453 const state_change &change,
1454 custom_info_t *custom_info)
1455 : dedge<eg_traits> (src, dest), m_sedge (sedge), m_change (change),
1456 m_custom_info (custom_info)
1457 {
1458 change.validate (dest->get_state (), ext_state);
1459 }
1460
1461 /* exploded_edge's dtor. */
1462
~exploded_edge()1463 exploded_edge::~exploded_edge ()
1464 {
1465 delete m_custom_info;
1466 }
1467
1468 /* Implementation of dedge::dump_dot vfunc for exploded_edge.
1469 Use the label of the underlying superedge, if any. */
1470
1471 void
dump_dot(graphviz_out * gv,const dump_args_t & args) const1472 exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &args) const
1473 {
1474 pretty_printer *pp = gv->get_pp ();
1475
1476 const char *style = "\"solid,bold\"";
1477 const char *color = "black";
1478 int weight = 10;
1479 const char *constraint = "true";
1480
1481 if (m_sedge)
1482 switch (m_sedge->m_kind)
1483 {
1484 default:
1485 gcc_unreachable ();
1486 case SUPEREDGE_CFG_EDGE:
1487 break;
1488 case SUPEREDGE_CALL:
1489 color = "red";
1490 //constraint = "false";
1491 break;
1492 case SUPEREDGE_RETURN:
1493 color = "green";
1494 //constraint = "false";
1495 break;
1496 case SUPEREDGE_INTRAPROCEDURAL_CALL:
1497 style = "\"dotted\"";
1498 break;
1499 }
1500 if (m_custom_info)
1501 {
1502 color = "red";
1503 style = "\"dotted\"";
1504 }
1505
1506 m_src->dump_dot_id (pp);
1507 pp_string (pp, " -> ");
1508 m_dest->dump_dot_id (pp);
1509 pp_printf (pp,
1510 (" [style=%s, color=%s, weight=%d, constraint=%s,"
1511 " headlabel=\""),
1512 style, color, weight, constraint);
1513
1514 if (m_sedge)
1515 m_sedge->dump_label_to_pp (pp, false);
1516 else if (m_custom_info)
1517 m_custom_info->print (pp);
1518
1519 m_change.dump (pp, args.m_eg.get_ext_state ());
1520 //pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
1521
1522 pp_printf (pp, "\"];\n");
1523 }
1524
1525 /* struct stats. */
1526
1527 /* stats' ctor. */
1528
stats(int num_supernodes)1529 stats::stats (int num_supernodes)
1530 : m_node_reuse_count (0),
1531 m_node_reuse_after_merge_count (0),
1532 m_num_supernodes (num_supernodes)
1533 {
1534 for (int i = 0; i < NUM_POINT_KINDS; i++)
1535 m_num_nodes[i] = 0;
1536 }
1537
1538 /* Log these stats in multiline form to LOGGER. */
1539
1540 void
log(logger * logger) const1541 stats::log (logger *logger) const
1542 {
1543 gcc_assert (logger);
1544 for (int i = 0; i < NUM_POINT_KINDS; i++)
1545 if (m_num_nodes[i] > 0)
1546 logger->log ("m_num_nodes[%s]: %i",
1547 point_kind_to_string (static_cast <enum point_kind> (i)),
1548 m_num_nodes[i]);
1549 logger->log ("m_node_reuse_count: %i", m_node_reuse_count);
1550 logger->log ("m_node_reuse_after_merge_count: %i",
1551 m_node_reuse_after_merge_count);
1552 }
1553
1554 /* Dump these stats in multiline form to OUT. */
1555
1556 void
dump(FILE * out) const1557 stats::dump (FILE *out) const
1558 {
1559 for (int i = 0; i < NUM_POINT_KINDS; i++)
1560 if (m_num_nodes[i] > 0)
1561 fprintf (out, "m_num_nodes[%s]: %i\n",
1562 point_kind_to_string (static_cast <enum point_kind> (i)),
1563 m_num_nodes[i]);
1564 fprintf (out, "m_node_reuse_count: %i\n", m_node_reuse_count);
1565 fprintf (out, "m_node_reuse_after_merge_count: %i\n",
1566 m_node_reuse_after_merge_count);
1567
1568 if (m_num_supernodes > 0)
1569 fprintf (out, "PK_AFTER_SUPERNODE nodes per supernode: %.2f\n",
1570 (float)m_num_nodes[PK_AFTER_SUPERNODE] / (float)m_num_supernodes);
1571 }
1572
1573 /* Return the total number of enodes recorded within this object. */
1574
1575 int
get_total_enodes() const1576 stats::get_total_enodes () const
1577 {
1578 int result = 0;
1579 for (int i = 0; i < NUM_POINT_KINDS; i++)
1580 result += m_num_nodes[i];
1581 return result;
1582 }
1583
1584 /* strongly_connected_components's ctor. Tarjan's SCC algorithm. */
1585
1586 strongly_connected_components::
strongly_connected_components(const supergraph & sg,logger * logger)1587 strongly_connected_components (const supergraph &sg, logger *logger)
1588 : m_sg (sg), m_per_node (m_sg.num_nodes ())
1589 {
1590 LOG_SCOPE (logger);
1591 auto_timevar tv (TV_ANALYZER_SCC);
1592
1593 for (int i = 0; i < m_sg.num_nodes (); i++)
1594 m_per_node.quick_push (per_node_data ());
1595
1596 for (int i = 0; i < m_sg.num_nodes (); i++)
1597 if (m_per_node[i].m_index == -1)
1598 strong_connect (i);
1599
1600 if (0)
1601 dump ();
1602 }
1603
1604 /* Dump this object to stderr. */
1605
1606 DEBUG_FUNCTION void
dump() const1607 strongly_connected_components::dump () const
1608 {
1609 for (int i = 0; i < m_sg.num_nodes (); i++)
1610 {
1611 const per_node_data &v = m_per_node[i];
1612 fprintf (stderr, "SN %i: index: %i lowlink: %i on_stack: %i\n",
1613 i, v.m_index, v.m_lowlink, v.m_on_stack);
1614 }
1615 }
1616
1617 /* Subroutine of strongly_connected_components's ctor, part of Tarjan's
1618 SCC algorithm. */
1619
1620 void
strong_connect(unsigned index)1621 strongly_connected_components::strong_connect (unsigned index)
1622 {
1623 supernode *v_snode = m_sg.get_node_by_index (index);
1624
1625 /* Set the depth index for v to the smallest unused index. */
1626 per_node_data *v = &m_per_node[index];
1627 v->m_index = index;
1628 v->m_lowlink = index;
1629 m_stack.safe_push (index);
1630 v->m_on_stack = true;
1631 index++;
1632
1633 /* Consider successors of v. */
1634 unsigned i;
1635 superedge *sedge;
1636 FOR_EACH_VEC_ELT (v_snode->m_succs, i, sedge)
1637 {
1638 supernode *w_snode = sedge->m_dest;
1639 per_node_data *w = &m_per_node[w_snode->m_index];
1640 if (w->m_index == -1)
1641 {
1642 /* Successor w has not yet been visited; recurse on it. */
1643 strong_connect (w_snode->m_index);
1644 v->m_lowlink = MIN (v->m_lowlink, w->m_lowlink);
1645 }
1646 else if (w->m_on_stack)
1647 {
1648 /* Successor w is in stack S and hence in the current SCC
1649 If w is not on stack, then (v, w) is a cross-edge in the DFS
1650 tree and must be ignored. */
1651 v->m_lowlink = MIN (v->m_lowlink, w->m_index);
1652 }
1653 }
1654
1655 /* If v is a root node, pop the stack and generate an SCC. */
1656
1657 if (v->m_lowlink == v->m_index)
1658 {
1659 per_node_data *w;
1660 do {
1661 int idx = m_stack.pop ();
1662 w = &m_per_node[idx];
1663 w->m_on_stack = false;
1664 } while (w != v);
1665 }
1666 }
1667
1668 /* worklist's ctor. */
1669
worklist(const exploded_graph & eg,const analysis_plan & plan)1670 worklist::worklist (const exploded_graph &eg, const analysis_plan &plan)
1671 : m_scc (eg.get_supergraph (), eg.get_logger ()),
1672 m_plan (plan),
1673 m_queue (key_t (*this, NULL))
1674 {
1675 }
1676
1677 /* Return the number of nodes in the worklist. */
1678
1679 unsigned
length() const1680 worklist::length () const
1681 {
1682 return m_queue.nodes ();
1683 }
1684
1685 /* Return the next node in the worklist, removing it. */
1686
1687 exploded_node *
take_next()1688 worklist::take_next ()
1689 {
1690 return m_queue.extract_min ();
1691 }
1692
1693 /* Return the next node in the worklist without removing it. */
1694
1695 exploded_node *
peek_next()1696 worklist::peek_next ()
1697 {
1698 return m_queue.min ();
1699 }
1700
1701 /* Add ENODE to the worklist. */
1702
1703 void
add_node(exploded_node * enode)1704 worklist::add_node (exploded_node *enode)
1705 {
1706 gcc_assert (enode->get_status () == exploded_node::STATUS_WORKLIST);
1707 m_queue.insert (key_t (*this, enode), enode);
1708 }
1709
1710 /* Comparator for implementing worklist::key_t comparison operators.
1711 Return negative if KA is before KB
1712 Return positive if KA is after KB
1713 Return 0 if they are equal. */
1714
1715 int
cmp(const worklist::key_t & ka,const worklist::key_t & kb)1716 worklist::key_t::cmp (const worklist::key_t &ka, const worklist::key_t &kb)
1717 {
1718 const program_point &point_a = ka.m_enode->get_point ();
1719 const program_point &point_b = kb.m_enode->get_point ();
1720 const call_string &call_string_a = point_a.get_call_string ();
1721 const call_string &call_string_b = point_b.get_call_string ();
1722
1723 /* Order empty-callstring points with different functions based on the
1724 analysis_plan, so that we generate summaries before they are used. */
1725 if (flag_analyzer_call_summaries
1726 && call_string_a.empty_p ()
1727 && call_string_b.empty_p ()
1728 && point_a.get_function () != NULL
1729 && point_b.get_function () != NULL
1730 && point_a.get_function () != point_b.get_function ())
1731 {
1732 return ka.m_worklist.m_plan.cmp_function (point_a.get_function (),
1733 point_b.get_function ());
1734 }
1735
1736 /* First, order by SCC. */
1737 int scc_id_a = ka.get_scc_id (ka.m_enode);
1738 int scc_id_b = kb.get_scc_id (kb.m_enode);
1739 if (scc_id_a != scc_id_b)
1740 return scc_id_a - scc_id_b;
1741
1742 /* If in same SCC, order by supernode index (an arbitrary but stable
1743 ordering). */
1744 const supernode *snode_a = ka.m_enode->get_supernode ();
1745 const supernode *snode_b = kb.m_enode->get_supernode ();
1746 if (snode_a == NULL)
1747 {
1748 if (snode_b != NULL)
1749 /* One is NULL. */
1750 return -1;
1751 else
1752 /* Both are NULL. */
1753 return 0;
1754 }
1755 if (snode_b == NULL)
1756 /* One is NULL. */
1757 return 1;
1758 /* Neither are NULL. */
1759 gcc_assert (snode_a && snode_b);
1760 if (snode_a->m_index != snode_b->m_index)
1761 return snode_a->m_index - snode_b->m_index;
1762
1763 gcc_assert (snode_a == snode_b);
1764
1765 /* Order within supernode via program point. */
1766 int within_snode_cmp
1767 = function_point::cmp_within_supernode (point_a.get_function_point (),
1768 point_b.get_function_point ());
1769 if (within_snode_cmp)
1770 return within_snode_cmp;
1771
1772 /* The points might vary by callstring; try sorting by callstring. */
1773 int cs_cmp = call_string::cmp (call_string_a, call_string_b);
1774 if (cs_cmp)
1775 return cs_cmp;
1776
1777 /* Otherwise, we ought to have the same program_point. */
1778 gcc_assert (point_a == point_b);
1779
1780 const program_state &state_a = ka.m_enode->get_state ();
1781 const program_state &state_b = kb.m_enode->get_state ();
1782
1783 /* Sort by sm-state, so that identical sm-states are grouped
1784 together in the worklist.
1785 For now, sort by the hash value (might not be deterministic). */
1786 for (unsigned sm_idx = 0; sm_idx < state_a.m_checker_states.length ();
1787 ++sm_idx)
1788 {
1789 sm_state_map *smap_a = state_a.m_checker_states[sm_idx];
1790 sm_state_map *smap_b = state_b.m_checker_states[sm_idx];
1791
1792 hashval_t hash_a = smap_a->hash ();
1793 hashval_t hash_b = smap_b->hash ();
1794 if (hash_a < hash_b)
1795 return -1;
1796 else if (hash_a > hash_b)
1797 return 1;
1798 }
1799
1800 /* Otherwise, we have two enodes at the same program point but with
1801 different states. We don't have a good total ordering on states,
1802 so order them by enode index, so that we have at least have a
1803 stable sort. */
1804 return ka.m_enode->m_index - kb.m_enode->m_index;
1805 }
1806
1807 /* exploded_graph's ctor. */
1808
exploded_graph(const supergraph & sg,logger * logger,const extrinsic_state & ext_state,const state_purge_map * purge_map,const analysis_plan & plan,int verbosity)1809 exploded_graph::exploded_graph (const supergraph &sg, logger *logger,
1810 const extrinsic_state &ext_state,
1811 const state_purge_map *purge_map,
1812 const analysis_plan &plan,
1813 int verbosity)
1814 : m_sg (sg), m_logger (logger),
1815 m_worklist (*this, plan),
1816 m_ext_state (ext_state),
1817 m_purge_map (purge_map),
1818 m_plan (plan),
1819 m_diagnostic_manager (logger, verbosity),
1820 m_global_stats (m_sg.num_nodes ()),
1821 m_functionless_stats (m_sg.num_nodes ()),
1822 m_PK_AFTER_SUPERNODE_per_snode (m_sg.num_nodes ())
1823 {
1824 m_origin = get_or_create_node (program_point (function_point (NULL, NULL,
1825 0, PK_ORIGIN),
1826 call_string ()),
1827 program_state (ext_state), NULL);
1828 for (int i = 0; i < m_sg.num_nodes (); i++)
1829 m_PK_AFTER_SUPERNODE_per_snode.quick_push (i);
1830 }
1831
1832 /* exploded_graph's dtor. */
1833
~exploded_graph()1834 exploded_graph::~exploded_graph ()
1835 {
1836 for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
1837 iter != m_per_function_stats.end ();
1838 ++iter)
1839 delete (*iter).second;
1840
1841 for (point_map_t::iterator iter = m_per_point_data.begin ();
1842 iter != m_per_point_data.end ();
1843 ++iter)
1844 delete (*iter).second;
1845 }
1846
1847 /* Ensure that there is an exploded_node representing an external call to
1848 FUN, adding it to the worklist if creating it.
1849
1850 Add an edge from the origin exploded_node to the function entrypoint
1851 exploded_node.
1852
1853 Return the exploded_node for the entrypoint to the function. */
1854
1855 exploded_node *
add_function_entry(function * fun)1856 exploded_graph::add_function_entry (function *fun)
1857 {
1858 program_point point = program_point::from_function_entry (m_sg, fun);
1859 program_state state (m_ext_state);
1860 impl_region_model_context ctxt (&state, NULL, m_ext_state, get_logger ());
1861 state.m_region_model->push_frame (fun, NULL, &ctxt);
1862
1863 if (!state.m_valid)
1864 return NULL;
1865
1866 exploded_node *enode = get_or_create_node (point, state, NULL);
1867 /* We should never fail to add such a node. */
1868 gcc_assert (enode);
1869 state_change change;
1870 add_edge (m_origin, enode, NULL, change);
1871 return enode;
1872 }
1873
1874 /* Get or create an exploded_node for (POINT, STATE).
1875 If a new node is created, it is added to the worklist.
1876 If CHANGE is non-NULL, use it to suppress some purging of state,
1877 to make generation of state_change_event instances easier. */
1878
1879 exploded_node *
get_or_create_node(const program_point & point,const program_state & state,state_change * change)1880 exploded_graph::get_or_create_node (const program_point &point,
1881 const program_state &state,
1882 state_change *change)
1883 {
1884 logger * const logger = get_logger ();
1885 LOG_FUNC (logger);
1886 if (logger)
1887 {
1888 format f (false);
1889 pretty_printer *pp = logger->get_printer ();
1890 logger->start_log_line ();
1891 pp_string (pp, "point: ");
1892 point.print (pp, f);
1893 logger->end_log_line ();
1894 logger->start_log_line ();
1895 pp_string (pp, "state: ");
1896 state.dump_to_pp (m_ext_state, true, pp);
1897 logger->end_log_line ();
1898 }
1899
1900 /* Stop exploring paths for which we don't know how to effectively
1901 model the state. */
1902 if (!state.m_valid)
1903 {
1904 if (logger)
1905 logger->log ("invalid state; not creating node");
1906 return NULL;
1907 }
1908
1909 auto_cfun sentinel (point.get_function ());
1910
1911 state.validate (get_ext_state ());
1912
1913 //state.dump (get_ext_state ());
1914
1915 /* Prune state to try to improve the chances of a cache hit,
1916 avoiding generating redundant nodes. */
1917 program_state pruned_state = state.prune_for_point (*this, point, change);
1918
1919 pruned_state.validate (get_ext_state ());
1920
1921 //pruned_state.dump (get_ext_state ());
1922
1923 if (logger)
1924 {
1925 pretty_printer *pp = logger->get_printer ();
1926 logger->start_log_line ();
1927 pp_string (pp, "pruned_state: ");
1928 pruned_state.dump_to_pp (m_ext_state, true, pp);
1929 logger->end_log_line ();
1930 pruned_state.m_region_model->dump_to_pp (logger->get_printer (), true);
1931 }
1932
1933 stats *per_fn_stats = get_or_create_function_stats (point.get_function ());
1934
1935 stats *per_cs_stats
1936 = &get_or_create_per_call_string_data (point.get_call_string ())->m_stats;
1937
1938 point_and_state ps (point, pruned_state);
1939 ps.validate (m_ext_state);
1940 if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
1941 {
1942 /* An exploded_node for PS already exists. */
1943 if (logger)
1944 logger->log ("reused EN: %i", (*slot)->m_index);
1945 m_global_stats.m_node_reuse_count++;
1946 per_fn_stats->m_node_reuse_count++;
1947 per_cs_stats->m_node_reuse_count++;
1948 return *slot;
1949 }
1950
1951 per_program_point_data *per_point_data
1952 = get_or_create_per_program_point_data (point);
1953
1954 /* Consider merging state with another enode at this program_point. */
1955 if (flag_analyzer_state_merge)
1956 {
1957 exploded_node *existing_enode;
1958 unsigned i;
1959 FOR_EACH_VEC_ELT (per_point_data->m_enodes, i, existing_enode)
1960 {
1961 if (logger)
1962 logger->log ("considering merging with existing EN: %i for point",
1963 existing_enode->m_index);
1964 gcc_assert (existing_enode->get_point () == point);
1965 const program_state &existing_state = existing_enode->get_state ();
1966
1967 /* This merges successfully within the loop. */
1968
1969 program_state merged_state (m_ext_state);
1970 if (pruned_state.can_merge_with_p (existing_state, m_ext_state,
1971 &merged_state))
1972 {
1973 if (logger)
1974 logger->log ("merging new state with that of EN: %i",
1975 existing_enode->m_index);
1976
1977 /* Try again for a cache hit.
1978 Whether we get one or not, merged_state's value_ids have no
1979 relationship to those of the input state, and thus to those
1980 of CHANGE, so we must purge any svalue_ids from *CHANGE. */
1981 ps.set_state (merged_state);
1982 if (change)
1983 change->on_svalue_purge (svalue_id::from_int (0));
1984
1985 if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
1986 {
1987 /* An exploded_node for PS already exists. */
1988 if (logger)
1989 logger->log ("reused EN: %i", (*slot)->m_index);
1990 m_global_stats.m_node_reuse_after_merge_count++;
1991 per_fn_stats->m_node_reuse_after_merge_count++;
1992 per_cs_stats->m_node_reuse_after_merge_count++;
1993 return *slot;
1994 }
1995 }
1996 else
1997 if (logger)
1998 logger->log ("not merging new state with that of EN: %i",
1999 existing_enode->m_index);
2000 }
2001 }
2002
2003 /* Impose a limit on the number of enodes per program point, and
2004 simply stop if we exceed it. */
2005 if ((int)per_point_data->m_enodes.length ()
2006 > param_analyzer_max_enodes_per_program_point)
2007 {
2008 if (logger)
2009 logger->log ("not creating enode; too many at program point");
2010 warning_at (point.get_location (), OPT_Wanalyzer_too_complex,
2011 "terminating analysis for this program point");
2012 per_point_data->m_excess_enodes++;
2013 return NULL;
2014 }
2015
2016 ps.validate (m_ext_state);
2017
2018 /* An exploded_node for "ps" doesn't already exist; create one. */
2019 exploded_node *node = new exploded_node (ps, m_nodes.length ());
2020 add_node (node);
2021 m_point_and_state_to_node.put (node->get_ps_key (), node);
2022
2023 /* Update per-program_point data. */
2024 per_point_data->m_enodes.safe_push (node);
2025
2026 const enum point_kind node_pk = node->get_point ().get_kind ();
2027 m_global_stats.m_num_nodes[node_pk]++;
2028 per_fn_stats->m_num_nodes[node_pk]++;
2029 per_cs_stats->m_num_nodes[node_pk]++;
2030
2031 if (node_pk == PK_AFTER_SUPERNODE)
2032 m_PK_AFTER_SUPERNODE_per_snode[point.get_supernode ()->m_index]++;
2033
2034 if (logger)
2035 {
2036 format f (false);
2037 pretty_printer *pp = logger->get_printer ();
2038 logger->log ("created EN: %i", node->m_index);
2039 logger->start_log_line ();
2040 pp_string (pp, "point: ");
2041 point.print (pp, f);
2042 logger->end_log_line ();
2043 logger->start_log_line ();
2044 pp_string (pp, "pruned_state: ");
2045 pruned_state.dump_to_pp (m_ext_state, true, pp);
2046 logger->end_log_line ();
2047 }
2048
2049 /* Add the new node to the worlist. */
2050 m_worklist.add_node (node);
2051 return node;
2052 }
2053
2054 /* Add an exploded_edge from SRC to DEST, recording its association
2055 with SEDGE (which may be NULL), and, if non-NULL, taking ownership
2056 of REWIND_INFO.
2057 Return the newly-created eedge. */
2058
2059 exploded_edge *
add_edge(exploded_node * src,exploded_node * dest,const superedge * sedge,const state_change & change,exploded_edge::custom_info_t * custom_info)2060 exploded_graph::add_edge (exploded_node *src, exploded_node *dest,
2061 const superedge *sedge,
2062 const state_change &change,
2063 exploded_edge::custom_info_t *custom_info)
2064 {
2065 exploded_edge *e = new exploded_edge (src, dest, m_ext_state,
2066 sedge, change, custom_info);
2067 digraph<eg_traits>::add_edge (e);
2068 return e;
2069 }
2070
2071 /* Ensure that this graph has per-program_point-data for POINT;
2072 borrow a pointer to it. */
2073
2074 per_program_point_data *
2075 exploded_graph::
get_or_create_per_program_point_data(const program_point & point)2076 get_or_create_per_program_point_data (const program_point &point)
2077 {
2078 if (per_program_point_data **slot = m_per_point_data.get (&point))
2079 return *slot;
2080
2081 per_program_point_data *per_point_data = new per_program_point_data (point);
2082 m_per_point_data.put (&per_point_data->m_key, per_point_data);
2083 return per_point_data;
2084 }
2085
2086 /* Ensure that this graph has per-call_string-data for CS;
2087 borrow a pointer to it. */
2088
2089 per_call_string_data *
get_or_create_per_call_string_data(const call_string & cs)2090 exploded_graph::get_or_create_per_call_string_data (const call_string &cs)
2091 {
2092 if (per_call_string_data **slot = m_per_call_string_data.get (&cs))
2093 return *slot;
2094
2095 per_call_string_data *data = new per_call_string_data (cs, m_sg.num_nodes ());
2096 m_per_call_string_data.put (&data->m_key,
2097 data);
2098 return data;
2099 }
2100
2101 /* Ensure that this graph has per-function-data for FUN;
2102 borrow a pointer to it. */
2103
2104 per_function_data *
get_or_create_per_function_data(function * fun)2105 exploded_graph::get_or_create_per_function_data (function *fun)
2106 {
2107 if (per_function_data **slot = m_per_function_data.get (fun))
2108 return *slot;
2109
2110 per_function_data *data = new per_function_data ();
2111 m_per_function_data.put (fun, data);
2112 return data;
2113 }
2114
2115 /* Get this graph's per-function-data for FUN if there is any,
2116 otherwise NULL. */
2117
2118 per_function_data *
get_per_function_data(function * fun) const2119 exploded_graph::get_per_function_data (function *fun) const
2120 {
2121 if (per_function_data **slot
2122 = const_cast <per_function_data_t &> (m_per_function_data).get (fun))
2123 return *slot;
2124
2125 return NULL;
2126 }
2127
2128 /* Return true if NODE and FUN should be traversed directly, rather than
2129 called via other functions. */
2130
2131 static bool
toplevel_function_p(cgraph_node * node,function * fun,logger * logger)2132 toplevel_function_p (cgraph_node *node, function *fun, logger *logger)
2133 {
2134 /* TODO: better logic here
2135 e.g. only if more than one caller, and significantly complicated.
2136 Perhaps some whole-callgraph analysis to decide if it's worth summarizing
2137 an edge, and if so, we need summaries. */
2138 if (flag_analyzer_call_summaries)
2139 {
2140 int num_call_sites = 0;
2141 for (cgraph_edge *edge = node->callers; edge; edge = edge->next_caller)
2142 ++num_call_sites;
2143
2144 /* For now, if there's more than one in-edge, and we want call
2145 summaries, do it at the top level so that there's a chance
2146 we'll have a summary when we need one. */
2147 if (num_call_sites > 1)
2148 {
2149 if (logger)
2150 logger->log ("traversing %qE (%i call sites)",
2151 fun->decl, num_call_sites);
2152 return true;
2153 }
2154 }
2155
2156 if (!TREE_PUBLIC (fun->decl))
2157 {
2158 if (logger)
2159 logger->log ("not traversing %qE (static)", fun->decl);
2160 return false;
2161 }
2162
2163 if (logger)
2164 logger->log ("traversing %qE (all checks passed)", fun->decl);
2165
2166 return true;
2167 }
2168
2169 /* Add initial nodes to EG, with entrypoints for externally-callable
2170 functions. */
2171
2172 void
build_initial_worklist()2173 exploded_graph::build_initial_worklist ()
2174 {
2175 logger * const logger = get_logger ();
2176 LOG_SCOPE (logger);
2177
2178 cgraph_node *node;
2179 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
2180 {
2181 function *fun = node->get_fun ();
2182 if (!toplevel_function_p (node, fun, logger))
2183 continue;
2184 exploded_node *enode = add_function_entry (fun);
2185 if (logger)
2186 {
2187 if (enode)
2188 logger->log ("created EN %i for %qE entrypoint",
2189 enode->m_index, fun->decl);
2190 else
2191 logger->log ("did not create enode for %qE entrypoint", fun->decl);
2192 }
2193 }
2194 }
2195
2196 /* The main loop of the analysis.
2197 Take freshly-created exploded_nodes from the worklist, calling
2198 process_node on them to explore the <point, state> graph.
2199 Add edges to their successors, potentially creating new successors
2200 (which are also added to the worklist). */
2201
2202 void
process_worklist()2203 exploded_graph::process_worklist ()
2204 {
2205 logger * const logger = get_logger ();
2206 LOG_SCOPE (logger);
2207 auto_timevar tv (TV_ANALYZER_WORKLIST);
2208
2209 while (m_worklist.length () > 0)
2210 {
2211 exploded_node *node = m_worklist.take_next ();
2212 gcc_assert (node->get_status () == exploded_node::STATUS_WORKLIST);
2213 gcc_assert (node->m_succs.length () == 0
2214 || node == m_origin);
2215
2216 if (logger)
2217 logger->log ("next to process: EN: %i", node->m_index);
2218
2219 /* Avoid exponential explosions of nodes by attempting to merge
2220 nodes that are at the same program point and which have
2221 sufficiently similar state. */
2222 if (flag_analyzer_state_merge && node != m_origin)
2223 if (exploded_node *node_2 = m_worklist.peek_next ())
2224 {
2225 gcc_assert (node_2->get_status ()
2226 == exploded_node::STATUS_WORKLIST);
2227 gcc_assert (node->m_succs.length () == 0);
2228 gcc_assert (node_2->m_succs.length () == 0);
2229
2230 gcc_assert (node != node_2);
2231
2232 if (logger)
2233 logger->log ("peek worklist: EN: %i", node_2->m_index);
2234
2235 if (node->get_point () == node_2->get_point ())
2236 {
2237 if (logger)
2238 {
2239 format f (false);
2240 pretty_printer *pp = logger->get_printer ();
2241 logger->start_log_line ();
2242 logger->log_partial
2243 ("got potential merge EN: %i and EN: %i at ",
2244 node->m_index, node_2->m_index);
2245 node->get_point ().print (pp, f);
2246 logger->end_log_line ();
2247 }
2248
2249 const program_state &state = node->get_state ();
2250 const program_state &state_2 = node_2->get_state ();
2251
2252 /* They shouldn't be equal, or we wouldn't have two
2253 separate nodes. */
2254 gcc_assert (state != state_2);
2255
2256 program_state merged_state (m_ext_state);
2257 state_change change;
2258 if (state.can_merge_with_p (state_2, m_ext_state,
2259 &merged_state))
2260 {
2261 if (logger)
2262 logger->log ("merging EN: %i and EN: %i",
2263 node->m_index, node_2->m_index);
2264
2265 if (merged_state == state)
2266 {
2267 /* Then merge node_2 into node by adding an edge. */
2268 add_edge (node_2, node, NULL, change);
2269
2270 /* Remove node_2 from the worklist. */
2271 m_worklist.take_next ();
2272 node_2->set_status (exploded_node::STATUS_MERGER);
2273
2274 /* Continue processing "node" below. */
2275 }
2276 else if (merged_state == state_2)
2277 {
2278 /* Then merge node into node_2, and leave node_2
2279 in the worklist, to be processed on the next
2280 iteration. */
2281 add_edge (node, node_2, NULL, change);
2282 node->set_status (exploded_node::STATUS_MERGER);
2283 continue;
2284 }
2285 else
2286 {
2287 /* We have a merged state that differs from
2288 both state and state_2. */
2289
2290 /* Remove node_2 from the worklist. */
2291 m_worklist.take_next ();
2292
2293 /* Create (or get) an exploded node for the merged
2294 states, adding to the worklist. */
2295 exploded_node *merged_enode
2296 = get_or_create_node (node->get_point (),
2297 merged_state, &change);
2298 if (merged_enode == NULL)
2299 continue;
2300
2301 if (logger)
2302 logger->log ("merged EN: %i and EN: %i into EN: %i",
2303 node->m_index, node_2->m_index,
2304 merged_enode->m_index);
2305
2306 /* "node" and "node_2" have both now been removed
2307 from the worklist; we should not process them.
2308
2309 "merged_enode" may be a new node; if so it will be
2310 processed in a subsequent iteration.
2311 Alternatively, "merged_enode" could be an existing
2312 node; one way the latter can
2313 happen is if we end up merging a succession of
2314 similar nodes into one. */
2315
2316 /* If merged_node is one of the two we were merging,
2317 add it back to the worklist to ensure it gets
2318 processed.
2319
2320 Add edges from the merged nodes to it (but not a
2321 self-edge). */
2322 if (merged_enode == node)
2323 m_worklist.add_node (merged_enode);
2324 else
2325 {
2326 add_edge (node, merged_enode, NULL, change);
2327 node->set_status (exploded_node::STATUS_MERGER);
2328 }
2329
2330 if (merged_enode == node_2)
2331 m_worklist.add_node (merged_enode);
2332 else
2333 {
2334 add_edge (node_2, merged_enode, NULL, change);
2335 node_2->set_status (exploded_node::STATUS_MERGER);
2336 }
2337
2338 continue;
2339 }
2340 }
2341
2342 /* TODO: should we attempt more than two nodes,
2343 or just do pairs of nodes? (and hope that we get
2344 a cascade of mergers). */
2345 }
2346 }
2347
2348 process_node (node);
2349
2350 /* Impose a hard limit on the number of exploded nodes, to ensure
2351 that the analysis terminates in the face of pathological state
2352 explosion (or bugs).
2353
2354 Specifically, the limit is on the number of PK_AFTER_SUPERNODE
2355 exploded nodes, looking at supernode exit events.
2356
2357 We use exit rather than entry since there can be multiple
2358 entry ENs, one per phi; the number of PK_AFTER_SUPERNODE ought
2359 to be equivalent to the number of supernodes multiplied by the
2360 number of states. */
2361 const int limit = m_sg.num_nodes () * param_analyzer_bb_explosion_factor;
2362 if (m_global_stats.m_num_nodes[PK_AFTER_SUPERNODE] > limit)
2363 {
2364 if (logger)
2365 logger->log ("bailing out; too many nodes");
2366 warning_at (node->get_point ().get_location (),
2367 OPT_Wanalyzer_too_complex,
2368 "analysis bailed out early"
2369 " (%i 'after-snode' enodes; %i enodes)",
2370 m_global_stats.m_num_nodes[PK_AFTER_SUPERNODE],
2371 m_nodes.length ());
2372 return;
2373 }
2374 }
2375 }
2376
2377 /* Return true if STMT must appear at the start of its exploded node, and
2378 thus we can't consolidate its effects within a run of other statements,
2379 where PREV_STMT was the previous statement. */
2380
2381 static bool
stmt_requires_new_enode_p(const gimple * stmt,const gimple * prev_stmt)2382 stmt_requires_new_enode_p (const gimple *stmt,
2383 const gimple *prev_stmt)
2384 {
2385 /* Stop consolidating at calls to
2386 "__analyzer_dump_exploded_nodes", so they always appear at the
2387 start of an exploded_node. */
2388 if (const gcall *call = dyn_cast <const gcall *> (stmt))
2389 if (is_special_named_call_p (call, "__analyzer_dump_exploded_nodes",
2390 1))
2391 return true;
2392
2393 /* If we had a PREV_STMT with an unknown location, and this stmt
2394 has a known location, then if a state change happens here, it
2395 could be consolidated into PREV_STMT, giving us an event with
2396 no location. Ensure that STMT gets its own exploded_node to
2397 avoid this. */
2398 if (get_pure_location (prev_stmt->location) == UNKNOWN_LOCATION
2399 && get_pure_location (stmt->location) != UNKNOWN_LOCATION)
2400 return true;
2401
2402 return false;
2403 }
2404
2405 /* The core of exploded_graph::process_worklist (the main analysis loop),
2406 handling one node in the worklist.
2407
2408 Get successor <point, state> pairs for NODE, calling get_or_create on
2409 them, and adding an exploded_edge to each successors.
2410
2411 Freshly-created nodes will be added to the worklist. */
2412
2413 void
process_node(exploded_node * node)2414 exploded_graph::process_node (exploded_node *node)
2415 {
2416 logger * const logger = get_logger ();
2417 LOG_FUNC_1 (logger, "EN: %i", node->m_index);
2418
2419 node->set_status (exploded_node::STATUS_PROCESSED);
2420
2421 const program_point &point = node->get_point ();
2422
2423 /* Update cfun and input_location in case of an ICE: make it easier to
2424 track down which source construct we're failing to handle. */
2425 auto_cfun sentinel (node->get_function ());
2426 const gimple *stmt = point.get_stmt ();
2427 if (stmt)
2428 input_location = stmt->location;
2429
2430 const program_state &state = node->get_state ();
2431 if (logger)
2432 {
2433 pretty_printer *pp = logger->get_printer ();
2434 logger->start_log_line ();
2435 pp_string (pp, "point: ");
2436 point.print (pp, format (false));
2437 pp_string (pp, ", state: ");
2438 state.dump_to_pp (m_ext_state, true, pp);
2439 logger->end_log_line ();
2440 }
2441
2442 switch (point.get_kind ())
2443 {
2444 default:
2445 gcc_unreachable ();
2446 case PK_ORIGIN:
2447 /* This node exists to simplify finding the shortest path
2448 to an exploded_node. */
2449 break;
2450
2451 case PK_BEFORE_SUPERNODE:
2452 {
2453 program_state next_state (state);
2454 state_change change;
2455
2456 if (point.get_from_edge ())
2457 {
2458 impl_region_model_context ctxt (*this, node,
2459 &state, &next_state, &change,
2460 NULL);
2461 const cfg_superedge *last_cfg_superedge
2462 = point.get_from_edge ()->dyn_cast_cfg_superedge ();
2463 if (last_cfg_superedge)
2464 next_state.m_region_model->update_for_phis
2465 (node->get_supernode (),
2466 last_cfg_superedge,
2467 &ctxt);
2468 }
2469
2470 if (point.get_supernode ()->m_stmts.length () > 0)
2471 {
2472 program_point next_point
2473 = program_point::before_stmt (point.get_supernode (), 0,
2474 point.get_call_string ());
2475 exploded_node *next
2476 = get_or_create_node (next_point, next_state, &change);
2477 if (next)
2478 add_edge (node, next, NULL, change);
2479 }
2480 else
2481 {
2482 program_point next_point
2483 = program_point::after_supernode (point.get_supernode (),
2484 point.get_call_string ());
2485 exploded_node *next = get_or_create_node (next_point, next_state,
2486 &change);
2487 if (next)
2488 add_edge (node, next, NULL, change);
2489 }
2490 }
2491 break;
2492 case PK_BEFORE_STMT:
2493 {
2494 /* Determine the effect of a run of one or more statements
2495 within one supernode, generating an edge to the program_point
2496 after the last statement that's processed.
2497
2498 Stop iterating statements and thus consolidating into one enode
2499 when:
2500 - reaching the end of the statements in the supernode
2501 - if an sm-state-change occurs (so that it gets its own
2502 exploded_node)
2503 - if "-fanalyzer-fine-grained" is active
2504 - encountering certain statements must appear at the start of
2505 their enode (for which stmt_requires_new_enode_p returns true)
2506
2507 Update next_state in-place, to get the result of the one
2508 or more stmts that are processed. */
2509 program_state next_state (state);
2510 state_change change;
2511 const supernode *snode = point.get_supernode ();
2512 unsigned stmt_idx;
2513 const gimple *prev_stmt = NULL;
2514 for (stmt_idx = point.get_stmt_idx ();
2515 stmt_idx < snode->m_stmts.length ();
2516 stmt_idx++)
2517 {
2518 const gimple *stmt = snode->m_stmts[stmt_idx];
2519
2520 if (stmt_idx > point.get_stmt_idx ())
2521 if (stmt_requires_new_enode_p (stmt, prev_stmt))
2522 {
2523 stmt_idx--;
2524 break;
2525 }
2526 prev_stmt = stmt;
2527
2528 /* Process the stmt. */
2529 exploded_node::on_stmt_flags flags
2530 = node->on_stmt (*this, snode, stmt, &next_state, &change);
2531
2532 /* If flags.m_terminate_path, stop analyzing; any nodes/edges
2533 will have been added by on_stmt (e.g. for handling longjmp). */
2534 if (flags.m_terminate_path)
2535 return;
2536
2537 if (flags.m_sm_changes || flag_analyzer_fine_grained)
2538 break;
2539 }
2540 unsigned next_idx = stmt_idx + 1;
2541 program_point next_point
2542 = (next_idx < point.get_supernode ()->m_stmts.length ()
2543 ? program_point::before_stmt (point.get_supernode (), next_idx,
2544 point.get_call_string ())
2545 : program_point::after_supernode (point.get_supernode (),
2546 point.get_call_string ()));
2547 exploded_node *next = get_or_create_node (next_point,
2548 next_state, &change);
2549 if (next)
2550 add_edge (node, next, NULL, change);
2551 }
2552 break;
2553 case PK_AFTER_SUPERNODE:
2554 {
2555 /* If this is an EXIT BB, detect leaks, and potentially
2556 create a function summary. */
2557 if (point.get_supernode ()->return_p ())
2558 {
2559 node->detect_leaks (*this);
2560 if (flag_analyzer_call_summaries
2561 && point.get_call_string ().empty_p ())
2562 {
2563 /* TODO: create function summary
2564 There can be more than one; each corresponds to a different
2565 final enode in the function. */
2566 if (logger)
2567 {
2568 pretty_printer *pp = logger->get_printer ();
2569 logger->start_log_line ();
2570 logger->log_partial
2571 ("would create function summary for %qE; state: ",
2572 point.get_fndecl ());
2573 state.dump_to_pp (m_ext_state, true, pp);
2574 logger->end_log_line ();
2575 }
2576 per_function_data *per_fn_data
2577 = get_or_create_per_function_data (point.get_function ());
2578 per_fn_data->add_call_summary (node);
2579 }
2580 }
2581 /* Traverse into successors of the supernode. */
2582 int i;
2583 superedge *succ;
2584 FOR_EACH_VEC_ELT (point.get_supernode ()->m_succs, i, succ)
2585 {
2586 if (logger)
2587 logger->log ("considering SN: %i -> SN: %i",
2588 succ->m_src->m_index, succ->m_dest->m_index);
2589
2590 state_change change;
2591
2592 program_point next_point
2593 = program_point::before_supernode (succ->m_dest, succ,
2594 point.get_call_string ());
2595 program_state next_state (state);
2596
2597 if (!node->on_edge (*this, succ, &next_point, &next_state, &change))
2598 {
2599 if (logger)
2600 logger->log ("skipping impossible edge to SN: %i",
2601 succ->m_dest->m_index);
2602 continue;
2603 }
2604
2605 exploded_node *next = get_or_create_node (next_point, next_state,
2606 &change);
2607 if (next)
2608 add_edge (node, next, succ, change);
2609 }
2610 }
2611 break;
2612 }
2613 }
2614
2615 /* Ensure that this graph has a stats instance for FN, return it.
2616 FN can be NULL, in which case a stats instances is returned covering
2617 "functionless" parts of the graph (the origin node). */
2618
2619 stats *
get_or_create_function_stats(function * fn)2620 exploded_graph::get_or_create_function_stats (function *fn)
2621 {
2622 if (!fn)
2623 return &m_functionless_stats;
2624
2625 if (stats **slot = m_per_function_stats.get (fn))
2626 return *slot;
2627 else
2628 {
2629 int num_supernodes = fn ? n_basic_blocks_for_fn (fn) : 0;
2630 /* not quite the num supernodes, but nearly. */
2631 stats *new_stats = new stats (num_supernodes);
2632 m_per_function_stats.put (fn, new_stats);
2633 return new_stats;
2634 }
2635 }
2636
2637 /* Print bar charts to PP showing:
2638 - the number of enodes per function, and
2639 - for each function:
2640 - the number of enodes per supernode/BB
2641 - the number of excess enodes per supernode/BB beyond the
2642 per-program-point limit, if there were any. */
2643
2644 void
print_bar_charts(pretty_printer * pp) const2645 exploded_graph::print_bar_charts (pretty_printer *pp) const
2646 {
2647 cgraph_node *cgnode;
2648
2649 pp_string (pp, "enodes per function:");
2650 pp_newline (pp);
2651 bar_chart enodes_per_function;
2652 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode)
2653 {
2654 function *fn = cgnode->get_fun ();
2655 const stats * const *s_ptr
2656 = const_cast <function_stat_map_t &> (m_per_function_stats).get (fn);
2657 enodes_per_function.add_item (function_name (fn),
2658 s_ptr ? (*s_ptr)->get_total_enodes () : 0);
2659 }
2660 enodes_per_function.print (pp);
2661
2662 /* Accumulate number of enodes per supernode. */
2663 auto_vec<unsigned> enodes_per_supernode (m_sg.num_nodes ());
2664 for (int i = 0; i < m_sg.num_nodes (); i++)
2665 enodes_per_supernode.quick_push (0);
2666 int i;
2667 exploded_node *enode;
2668 FOR_EACH_VEC_ELT (m_nodes, i, enode)
2669 {
2670 const supernode *iter_snode = enode->get_supernode ();
2671 if (!iter_snode)
2672 continue;
2673 enodes_per_supernode[iter_snode->m_index]++;
2674 }
2675
2676 /* Accumulate excess enodes per supernode. */
2677 auto_vec<unsigned> excess_enodes_per_supernode (m_sg.num_nodes ());
2678 for (int i = 0; i < m_sg.num_nodes (); i++)
2679 excess_enodes_per_supernode.quick_push (0);
2680 for (point_map_t::iterator iter = m_per_point_data.begin ();
2681 iter != m_per_point_data.end (); ++iter)
2682 {
2683 const program_point *point = (*iter).first;
2684 const supernode *iter_snode = point->get_supernode ();
2685 if (!iter_snode)
2686 continue;
2687 const per_program_point_data *point_data = (*iter).second;
2688 excess_enodes_per_supernode[iter_snode->m_index]
2689 += point_data->m_excess_enodes;
2690 }
2691
2692 /* Show per-function bar_charts of enodes per supernode/BB. */
2693 pp_string (pp, "per-function enodes per supernode/BB:");
2694 pp_newline (pp);
2695 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode)
2696 {
2697 function *fn = cgnode->get_fun ();
2698 pp_printf (pp, "function: %qs", function_name (fn));
2699 pp_newline (pp);
2700
2701 bar_chart enodes_per_snode;
2702 bar_chart excess_enodes_per_snode;
2703 bool have_excess_enodes = false;
2704 for (int i = 0; i < m_sg.num_nodes (); i++)
2705 {
2706 const supernode *iter_snode = m_sg.get_node_by_index (i);
2707 if (iter_snode->get_function () != fn)
2708 continue;
2709 pretty_printer tmp_pp;
2710 pp_printf (&tmp_pp, "sn %i (bb %i)",
2711 iter_snode->m_index, iter_snode->m_bb->index);
2712 enodes_per_snode.add_item (pp_formatted_text (&tmp_pp),
2713 enodes_per_supernode[iter_snode->m_index]);
2714 const int num_excess
2715 = excess_enodes_per_supernode[iter_snode->m_index];
2716 excess_enodes_per_snode.add_item (pp_formatted_text (&tmp_pp),
2717 num_excess);
2718 if (num_excess)
2719 have_excess_enodes = true;
2720 }
2721 enodes_per_snode.print (pp);
2722 if (have_excess_enodes)
2723 {
2724 pp_printf (pp, "EXCESS ENODES:");
2725 pp_newline (pp);
2726 excess_enodes_per_snode.print (pp);
2727 }
2728 }
2729 }
2730
2731 /* Write all stats information to this graph's logger, if any. */
2732
2733 void
log_stats() const2734 exploded_graph::log_stats () const
2735 {
2736 logger * const logger = get_logger ();
2737 if (!logger)
2738 return;
2739
2740 LOG_SCOPE (logger);
2741
2742 logger->log ("m_sg.num_nodes (): %i", m_sg.num_nodes ());
2743 logger->log ("m_nodes.length (): %i", m_nodes.length ());
2744 logger->log ("m_edges.length (): %i", m_edges.length ());
2745 logger->log ("remaining enodes in worklist: %i", m_worklist.length ());
2746
2747 logger->log ("global stats:");
2748 m_global_stats.log (logger);
2749
2750 for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
2751 iter != m_per_function_stats.end ();
2752 ++iter)
2753 {
2754 function *fn = (*iter).first;
2755 log_scope s (logger, function_name (fn));
2756 (*iter).second->log (logger);
2757 }
2758
2759 print_bar_charts (logger->get_printer ());
2760 }
2761
2762 /* Dump all stats information to OUT. */
2763
2764 void
dump_stats(FILE * out) const2765 exploded_graph::dump_stats (FILE *out) const
2766 {
2767 fprintf (out, "m_sg.num_nodes (): %i\n", m_sg.num_nodes ());
2768 fprintf (out, "m_nodes.length (): %i\n", m_nodes.length ());
2769 fprintf (out, "m_edges.length (): %i\n", m_edges.length ());
2770 fprintf (out, "remaining enodes in worklist: %i", m_worklist.length ());
2771
2772 fprintf (out, "global stats:\n");
2773 m_global_stats.dump (out);
2774
2775 for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
2776 iter != m_per_function_stats.end ();
2777 ++iter)
2778 {
2779 function *fn = (*iter).first;
2780 fprintf (out, "function: %s\n", function_name (fn));
2781 (*iter).second->dump (out);
2782 }
2783
2784 fprintf (out, "PK_AFTER_SUPERNODE per supernode:\n");
2785 for (unsigned i = 0; i < m_PK_AFTER_SUPERNODE_per_snode.length (); i++)
2786 fprintf (out, " SN %i: %3i\n", i, m_PK_AFTER_SUPERNODE_per_snode[i]);
2787 }
2788
2789 void
dump_states_for_supernode(FILE * out,const supernode * snode) const2790 exploded_graph::dump_states_for_supernode (FILE *out,
2791 const supernode *snode) const
2792 {
2793 fprintf (out, "PK_AFTER_SUPERNODE nodes for SN: %i\n", snode->m_index);
2794 int i;
2795 exploded_node *enode;
2796 int state_idx = 0;
2797 FOR_EACH_VEC_ELT (m_nodes, i, enode)
2798 {
2799 const supernode *iter_snode = enode->get_supernode ();
2800 if (enode->get_point ().get_kind () == PK_AFTER_SUPERNODE
2801 && iter_snode == snode)
2802 {
2803 pretty_printer pp;
2804 pp_format_decoder (&pp) = default_tree_printer;
2805 enode->get_state ().dump_to_pp (m_ext_state, true, &pp);
2806 fprintf (out, "state %i: EN: %i\n %s\n",
2807 state_idx++, enode->m_index,
2808 pp_formatted_text (&pp));
2809 }
2810 }
2811 fprintf (out, "#exploded_node for PK_AFTER_SUPERNODE for SN: %i = %i\n",
2812 snode->m_index, state_idx);
2813 }
2814
2815 /* Look for the last use of SEARCH_STMT within this path.
2816 If found write the edge's index to *OUT_IDX and return true, otherwise
2817 return false. */
2818
2819 bool
find_stmt_backwards(const gimple * search_stmt,int * out_idx) const2820 exploded_path::find_stmt_backwards (const gimple *search_stmt,
2821 int *out_idx) const
2822 {
2823 int i;
2824 const exploded_edge *eedge;
2825 FOR_EACH_VEC_ELT_REVERSE (m_edges, i, eedge)
2826 {
2827 const exploded_node *dst_node = eedge->m_dest;
2828 const program_point &dst_point = dst_node->get_point ();
2829 const gimple *stmt = dst_point.get_stmt ();
2830 if (stmt == search_stmt)
2831 {
2832 *out_idx = i;
2833 return true;
2834 }
2835 }
2836 return false;
2837 }
2838
2839 /* Get the final exploded_node in this path, which must be non-empty. */
2840
2841 exploded_node *
get_final_enode() const2842 exploded_path::get_final_enode () const
2843 {
2844 gcc_assert (m_edges.length () > 0);
2845 return m_edges[m_edges.length () - 1]->m_dest;
2846 }
2847
2848 /* Check state along this path, returning true if it is feasible.
2849 If OUT is non-NULL, and the path is infeasible, write a new
2850 feasibility_problem to *OUT. */
2851
2852 bool
feasible_p(logger * logger,feasibility_problem ** out) const2853 exploded_path::feasible_p (logger *logger, feasibility_problem **out) const
2854 {
2855 LOG_SCOPE (logger);
2856
2857 /* Traverse the path, updating this model. */
2858 region_model model;
2859 for (unsigned i = 0; i < m_edges.length (); i++)
2860 {
2861 const exploded_edge *eedge = m_edges[i];
2862 if (logger)
2863 logger->log ("considering edge %i: EN:%i -> EN:%i",
2864 i,
2865 eedge->m_src->m_index,
2866 eedge->m_dest->m_index);
2867 const exploded_node &src_enode = *eedge->m_src;
2868 const program_point &src_point = src_enode.get_point ();
2869 if (logger)
2870 {
2871 logger->start_log_line ();
2872 src_point.print (logger->get_printer (), format (false));
2873 logger->end_log_line ();
2874 }
2875
2876 if (const gimple *stmt = src_point.get_stmt ())
2877 {
2878 /* Update cfun and input_location in case of ICE: make it easier to
2879 track down which source construct we're failing to handle. */
2880 auto_cfun sentinel (src_point.get_function ());
2881 input_location = stmt->location;
2882
2883 if (const gassign *assign = dyn_cast <const gassign *> (stmt))
2884 model.on_assignment (assign, NULL);
2885 else if (const greturn *return_ = dyn_cast <const greturn *> (stmt))
2886 model.on_return (return_, NULL);
2887 }
2888
2889 const superedge *sedge = eedge->m_sedge;
2890 if (sedge)
2891 {
2892 if (logger)
2893 logger->log (" sedge: SN:%i -> SN:%i %s",
2894 sedge->m_src->m_index,
2895 sedge->m_dest->m_index,
2896 sedge->get_description (false));
2897
2898 const gimple *last_stmt = src_point.get_supernode ()->get_last_stmt ();
2899 if (!model.maybe_update_for_edge (*sedge, last_stmt, NULL))
2900 {
2901 if (logger)
2902 {
2903 logger->log ("rejecting due to region model");
2904 model.dump_to_pp (logger->get_printer (), false);
2905 }
2906 if (out)
2907 *out = new feasibility_problem (i, model, *eedge, last_stmt);
2908 return false;
2909 }
2910 }
2911 else
2912 {
2913 /* Special-case the initial eedge from the origin node to the
2914 initial function by pushing a frame for it. */
2915 if (i == 0)
2916 {
2917 gcc_assert (eedge->m_src->m_index == 0);
2918 gcc_assert (src_point.get_kind () == PK_ORIGIN);
2919 gcc_assert (eedge->m_dest->get_point ().get_kind ()
2920 == PK_BEFORE_SUPERNODE);
2921 function *fun = eedge->m_dest->get_function ();
2922 gcc_assert (fun);
2923 model.push_frame (fun, NULL, NULL);
2924 if (logger)
2925 logger->log (" pushing frame for %qD", fun->decl);
2926 }
2927 else if (eedge->m_custom_info)
2928 eedge->m_custom_info->update_model (&model, *eedge);
2929 }
2930
2931 /* Handle phi nodes on an edge leaving a PK_BEFORE_SUPERNODE (to
2932 a PK_BEFORE_STMT, or a PK_AFTER_SUPERNODE if no stmts).
2933 This will typically not be associated with a superedge. */
2934 if (src_point.get_from_edge ())
2935 {
2936 const cfg_superedge *last_cfg_superedge
2937 = src_point.get_from_edge ()->dyn_cast_cfg_superedge ();
2938 if (last_cfg_superedge)
2939 {
2940 if (logger)
2941 logger->log (" update for phis");
2942 model.update_for_phis (src_enode.get_supernode (),
2943 last_cfg_superedge,
2944 NULL);
2945 }
2946 }
2947
2948 if (logger)
2949 {
2950 logger->log ("state after edge %i: EN:%i -> EN:%i",
2951 i,
2952 eedge->m_src->m_index,
2953 eedge->m_dest->m_index);
2954 logger->start_log_line ();
2955 model.dump_to_pp (logger->get_printer (), true);
2956 logger->end_log_line ();
2957 }
2958 }
2959
2960 return true;
2961 }
2962
2963 /* Dump this path in multiline form to PP. */
2964
2965 void
dump_to_pp(pretty_printer * pp) const2966 exploded_path::dump_to_pp (pretty_printer *pp) const
2967 {
2968 for (unsigned i = 0; i < m_edges.length (); i++)
2969 {
2970 const exploded_edge *eedge = m_edges[i];
2971 pp_printf (pp, "m_edges[%i]: EN %i -> EN %i",
2972 i,
2973 eedge->m_src->m_index,
2974 eedge->m_dest->m_index);
2975 pp_newline (pp);
2976 }
2977 }
2978
2979 /* Dump this path in multiline form to FP. */
2980
2981 void
dump(FILE * fp) const2982 exploded_path::dump (FILE *fp) const
2983 {
2984 pretty_printer pp;
2985 pp_format_decoder (&pp) = default_tree_printer;
2986 pp_show_color (&pp) = pp_show_color (global_dc->printer);
2987 pp.buffer->stream = fp;
2988 dump_to_pp (&pp);
2989 pp_flush (&pp);
2990 }
2991
2992 /* Dump this path in multiline form to stderr. */
2993
2994 DEBUG_FUNCTION void
dump() const2995 exploded_path::dump () const
2996 {
2997 dump (stderr);
2998 }
2999
3000 /* A family of cluster subclasses for use when generating .dot output for
3001 exploded graphs (-fdump-analyzer-exploded-graph), for grouping the
3002 enodes into hierarchical boxes.
3003
3004 All functionless enodes appear in the top-level graph.
3005 Every (function, call_string) pair gets its own cluster. Within that
3006 cluster, each supernode gets its own cluster.
3007
3008 Hence all enodes relating to a particular function with a particular
3009 callstring will be in a cluster together; all enodes for the same
3010 function but with a different callstring will be in a different
3011 cluster. */
3012
3013 /* Base class of cluster for clustering exploded_node instances in .dot
3014 output, based on various subclass-specific criteria. */
3015
3016 class exploded_cluster : public cluster<eg_traits>
3017 {
3018 };
3019
3020 /* Cluster containing all exploded_node instances for one supernode. */
3021
3022 class supernode_cluster : public exploded_cluster
3023 {
3024 public:
supernode_cluster(const supernode * supernode)3025 supernode_cluster (const supernode *supernode) : m_supernode (supernode) {}
3026
3027 // TODO: dtor?
3028
dump_dot(graphviz_out * gv,const dump_args_t & args) const3029 void dump_dot (graphviz_out *gv, const dump_args_t &args) const FINAL OVERRIDE
3030 {
3031 gv->println ("subgraph \"cluster_supernode_%p\" {",
3032 (const void *)this);
3033 gv->indent ();
3034 gv->println ("style=\"dashed\";");
3035 gv->println ("label=\"SN: %i (bb: %i)\";",
3036 m_supernode->m_index, m_supernode->m_bb->index);
3037
3038 int i;
3039 exploded_node *enode;
3040 FOR_EACH_VEC_ELT (m_enodes, i, enode)
3041 enode->dump_dot (gv, args);
3042
3043 /* Terminate subgraph. */
3044 gv->outdent ();
3045 gv->println ("}");
3046 }
3047
add_node(exploded_node * en)3048 void add_node (exploded_node *en) FINAL OVERRIDE
3049 {
3050 m_enodes.safe_push (en);
3051 }
3052
3053 private:
3054 const supernode *m_supernode;
3055 auto_vec <exploded_node *> m_enodes;
3056 };
3057
3058 /* Cluster containing all supernode_cluster instances for one
3059 (function, call_string) pair. */
3060
3061 class function_call_string_cluster : public exploded_cluster
3062 {
3063 public:
function_call_string_cluster(function * fun,call_string cs)3064 function_call_string_cluster (function *fun, call_string cs)
3065 : m_fun (fun), m_cs (cs) {}
3066
~function_call_string_cluster()3067 ~function_call_string_cluster ()
3068 {
3069 for (map_t::iterator iter = m_map.begin ();
3070 iter != m_map.end ();
3071 ++iter)
3072 delete (*iter).second;
3073 }
3074
dump_dot(graphviz_out * gv,const dump_args_t & args) const3075 void dump_dot (graphviz_out *gv, const dump_args_t &args) const FINAL OVERRIDE
3076 {
3077 const char *funcname = function_name (m_fun);
3078
3079 gv->println ("subgraph \"cluster_function_%p\" {", (const void *)this);
3080 gv->indent ();
3081 gv->write_indent ();
3082 gv->print ("label=\"call string: ");
3083 m_cs.print (gv->get_pp ());
3084 gv->print (" function: %s \";", funcname);
3085 gv->print ("\n");
3086
3087 for (map_t::iterator iter = m_map.begin ();
3088 iter != m_map.end ();
3089 ++iter)
3090 (*iter).second->dump_dot (gv, args);
3091
3092 /* Terminate subgraph. */
3093 gv->outdent ();
3094 gv->println ("}");
3095 }
3096
add_node(exploded_node * en)3097 void add_node (exploded_node *en) FINAL OVERRIDE
3098 {
3099 const supernode *supernode = en->get_supernode ();
3100 gcc_assert (supernode);
3101 supernode_cluster **slot = m_map.get (supernode);
3102 if (slot)
3103 (*slot)->add_node (en);
3104 else
3105 {
3106 supernode_cluster *child = new supernode_cluster (supernode);
3107 m_map.put (supernode, child);
3108 child->add_node (en);
3109 }
3110 }
3111
3112 private:
3113 function *m_fun;
3114 call_string m_cs;
3115 typedef ordered_hash_map<const supernode *, supernode_cluster *> map_t;
3116 map_t m_map;
3117 };
3118
3119 /* Keys for root_cluster. */
3120
3121 struct function_call_string
3122 {
function_call_stringana::function_call_string3123 function_call_string (function *fun, call_string cs)
3124 : m_fun (fun), m_cs (cs)
3125 {
3126 gcc_assert (fun);
3127 }
3128
3129 function *m_fun;
3130 call_string m_cs;
3131 };
3132
3133 } // namespace ana
3134
3135 template <> struct default_hash_traits<function_call_string>
3136 : public pod_hash_traits<function_call_string>
3137 {
3138 static const bool empty_zero_p = false;
3139 };
3140
3141 template <>
3142 inline hashval_t
hash(value_type v)3143 pod_hash_traits<function_call_string>::hash (value_type v)
3144 {
3145 return pointer_hash <function>::hash (v.m_fun) ^ v.m_cs.hash ();
3146 }
3147
3148 template <>
3149 inline bool
equal(const value_type & existing,const value_type & candidate)3150 pod_hash_traits<function_call_string>::equal (const value_type &existing,
3151 const value_type &candidate)
3152 {
3153 return existing.m_fun == candidate.m_fun && existing.m_cs == candidate.m_cs;
3154 }
3155 template <>
3156 inline void
mark_deleted(value_type & v)3157 pod_hash_traits<function_call_string>::mark_deleted (value_type &v)
3158 {
3159 v.m_fun = reinterpret_cast<function *> (1);
3160 }
3161 template <>
3162 inline void
mark_empty(value_type & v)3163 pod_hash_traits<function_call_string>::mark_empty (value_type &v)
3164 {
3165 v.m_fun = NULL;
3166 }
3167 template <>
3168 inline bool
is_deleted(value_type v)3169 pod_hash_traits<function_call_string>::is_deleted (value_type v)
3170 {
3171 return v.m_fun == reinterpret_cast<function *> (1);
3172 }
3173 template <>
3174 inline bool
is_empty(value_type v)3175 pod_hash_traits<function_call_string>::is_empty (value_type v)
3176 {
3177 return v.m_fun == NULL;
3178 }
3179
3180 namespace ana {
3181
3182 /* Top-level cluster for generating .dot output for exploded graphs,
3183 handling the functionless nodes, and grouping the remaining nodes by
3184 callstring. */
3185
3186 class root_cluster : public exploded_cluster
3187 {
3188 public:
~root_cluster()3189 ~root_cluster ()
3190 {
3191 for (map_t::iterator iter = m_map.begin ();
3192 iter != m_map.end ();
3193 ++iter)
3194 delete (*iter).second;
3195 }
3196
dump_dot(graphviz_out * gv,const dump_args_t & args) const3197 void dump_dot (graphviz_out *gv, const dump_args_t &args) const FINAL OVERRIDE
3198 {
3199 int i;
3200 exploded_node *enode;
3201 FOR_EACH_VEC_ELT (m_functionless_enodes, i, enode)
3202 enode->dump_dot (gv, args);
3203
3204 for (map_t::iterator iter = m_map.begin ();
3205 iter != m_map.end ();
3206 ++iter)
3207 (*iter).second->dump_dot (gv, args);
3208 }
3209
add_node(exploded_node * en)3210 void add_node (exploded_node *en) FINAL OVERRIDE
3211 {
3212 function *fun = en->get_function ();
3213 if (!fun)
3214 {
3215 m_functionless_enodes.safe_push (en);
3216 return;
3217 }
3218
3219 const call_string &cs = en->get_point ().get_call_string ();
3220 function_call_string key (fun, cs);
3221 function_call_string_cluster **slot = m_map.get (key);
3222 if (slot)
3223 (*slot)->add_node (en);
3224 else
3225 {
3226 function_call_string_cluster *child
3227 = new function_call_string_cluster (fun, cs);
3228 m_map.put (key, child);
3229 child->add_node (en);
3230 }
3231 }
3232
3233 private:
3234 /* This can't be an ordered_hash_map, as we can't store vec<call_string>,
3235 since it's not a POD; vec<>::quick_push has:
3236 *slot = obj;
3237 and the slot isn't initialized, so the assignment op dies when cleaning up
3238 un-inited *slot (within the truncate call). */
3239 typedef hash_map<function_call_string, function_call_string_cluster *> map_t;
3240 map_t m_map;
3241
3242 /* This should just be the origin exploded_node. */
3243 auto_vec <exploded_node *> m_functionless_enodes;
3244 };
3245
3246 /* Subclass of range_label for use within
3247 exploded_graph::dump_exploded_nodes for implementing
3248 -fdump-analyzer-exploded-nodes: a label for a specific
3249 exploded_node. */
3250
3251 class enode_label : public range_label
3252 {
3253 public:
enode_label(const extrinsic_state & ext_state,exploded_node * enode)3254 enode_label (const extrinsic_state &ext_state,
3255 exploded_node *enode)
3256 : m_ext_state (ext_state), m_enode (enode) {}
3257
get_text(unsigned) const3258 label_text get_text (unsigned) const FINAL OVERRIDE
3259 {
3260 pretty_printer pp;
3261 pp_format_decoder (&pp) = default_tree_printer;
3262 m_enode->get_state ().dump_to_pp (m_ext_state, true, &pp);
3263 return make_label_text (false, "EN: %i: %s",
3264 m_enode->m_index, pp_formatted_text (&pp));
3265 }
3266
3267 private:
3268 const extrinsic_state &m_ext_state;
3269 exploded_node *m_enode;
3270 };
3271
3272 /* Postprocessing support for dumping the exploded nodes.
3273 Handle -fdump-analyzer-exploded-nodes,
3274 -fdump-analyzer-exploded-nodes-2, and the
3275 "__analyzer_dump_exploded_nodes" builtin. */
3276
3277 void
dump_exploded_nodes() const3278 exploded_graph::dump_exploded_nodes () const
3279 {
3280 // TODO
3281 /* Locate calls to __analyzer_dump_exploded_nodes. */
3282 // Print how many egs there are for them?
3283 /* Better: log them as we go, and record the exploded nodes
3284 in question. */
3285
3286 /* Show every enode. */
3287
3288 /* Gather them by stmt, so that we can more clearly see the
3289 "hotspots" requiring numerous exploded nodes. */
3290
3291 /* Alternatively, simply throw them all into one big rich_location
3292 and see if the label-printing will sort it out...
3293 This requires them all to be in the same source file. */
3294
3295 if (flag_dump_analyzer_exploded_nodes)
3296 {
3297 auto_timevar tv (TV_ANALYZER_DUMP);
3298 gcc_rich_location richloc (UNKNOWN_LOCATION);
3299 unsigned i;
3300 exploded_node *enode;
3301 FOR_EACH_VEC_ELT (m_nodes, i, enode)
3302 {
3303 if (const gimple *stmt = enode->get_stmt ())
3304 {
3305 if (get_pure_location (richloc.get_loc ()) == UNKNOWN_LOCATION)
3306 richloc.set_range (0, stmt->location, SHOW_RANGE_WITH_CARET);
3307 else
3308 richloc.add_range (stmt->location,
3309 SHOW_RANGE_WITHOUT_CARET,
3310 new enode_label (m_ext_state, enode));
3311 }
3312 }
3313 warning_at (&richloc, 0, "%i exploded nodes", m_nodes.length ());
3314
3315 /* Repeat the warning without all the labels, so that message is visible
3316 (the other one may well have scrolled past the terminal limit). */
3317 warning_at (richloc.get_loc (), 0,
3318 "%i exploded nodes", m_nodes.length ());
3319
3320 if (m_worklist.length () > 0)
3321 warning_at (richloc.get_loc (), 0,
3322 "worklist still contains %i nodes", m_worklist.length ());
3323 }
3324
3325 /* Dump the egraph in textual form to a dump file. */
3326 if (flag_dump_analyzer_exploded_nodes_2)
3327 {
3328 auto_timevar tv (TV_ANALYZER_DUMP);
3329 char *filename
3330 = concat (dump_base_name, ".eg.txt", NULL);
3331 FILE *outf = fopen (filename, "w");
3332 if (!outf)
3333 error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename);
3334 free (filename);
3335
3336 fprintf (outf, "exploded graph for %s\n", dump_base_name);
3337 fprintf (outf, " nodes: %i\n", m_nodes.length ());
3338 fprintf (outf, " edges: %i\n", m_edges.length ());
3339
3340 unsigned i;
3341 exploded_node *enode;
3342 FOR_EACH_VEC_ELT (m_nodes, i, enode)
3343 {
3344 fprintf (outf, "\nEN %i:\n", enode->m_index);
3345 enode->dump_succs_and_preds (outf);
3346 pretty_printer pp;
3347 enode->get_point ().print (&pp, format (true));
3348 fprintf (outf, "%s\n", pp_formatted_text (&pp));
3349 enode->get_state ().dump_to_file (m_ext_state, false, outf);
3350 }
3351
3352 fclose (outf);
3353 }
3354
3355 /* Dump the egraph in textual form to multiple dump files, one per enode. */
3356 if (flag_dump_analyzer_exploded_nodes_3)
3357 {
3358 auto_timevar tv (TV_ANALYZER_DUMP);
3359
3360 unsigned i;
3361 exploded_node *enode;
3362 FOR_EACH_VEC_ELT (m_nodes, i, enode)
3363 {
3364 char *filename
3365 = xasprintf ("%s.en-%i.txt", dump_base_name, i);
3366 FILE *outf = fopen (filename, "w");
3367 if (!outf)
3368 error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename);
3369 free (filename);
3370
3371 fprintf (outf, "EN %i:\n", enode->m_index);
3372 enode->dump_succs_and_preds (outf);
3373 pretty_printer pp;
3374 enode->get_point ().print (&pp, format (true));
3375 fprintf (outf, "%s\n", pp_formatted_text (&pp));
3376 enode->get_state ().dump_to_file (m_ext_state, false, outf);
3377
3378 fclose (outf);
3379 }
3380 }
3381
3382 /* Emit a warning at any call to "__analyzer_dump_exploded_nodes",
3383 giving the number of processed exploded nodes for "before-stmt",
3384 and the IDs of processed, merger, and worklist enodes.
3385
3386 We highlight the count of *processed* enodes since this is of most
3387 interest in DejaGnu tests for ensuring that state merger has
3388 happened.
3389
3390 We don't show the count of merger and worklist enodes, as this is
3391 more of an implementation detail of the merging/worklist that we
3392 don't want to bake into our expected DejaGnu messages. */
3393
3394 unsigned i;
3395 exploded_node *enode;
3396 hash_set<const gimple *> seen;
3397 FOR_EACH_VEC_ELT (m_nodes, i, enode)
3398 {
3399 if (enode->get_point ().get_kind () != PK_BEFORE_STMT)
3400 continue;
3401
3402 if (const gimple *stmt = enode->get_stmt ())
3403 if (const gcall *call = dyn_cast <const gcall *> (stmt))
3404 if (is_special_named_call_p (call, "__analyzer_dump_exploded_nodes",
3405 1))
3406 {
3407 if (seen.contains (stmt))
3408 continue;
3409
3410 auto_vec<exploded_node *> processed_enodes;
3411 auto_vec<exploded_node *> merger_enodes;
3412 auto_vec<exploded_node *> worklist_enodes;
3413 /* This is O(N^2). */
3414 unsigned j;
3415 exploded_node *other_enode;
3416 FOR_EACH_VEC_ELT (m_nodes, j, other_enode)
3417 {
3418 if (other_enode->get_point ().get_kind () != PK_BEFORE_STMT)
3419 continue;
3420 if (other_enode->get_stmt () == stmt)
3421 switch (other_enode->get_status ())
3422 {
3423 default:
3424 gcc_unreachable ();
3425 case exploded_node::STATUS_WORKLIST:
3426 worklist_enodes.safe_push (other_enode);
3427 break;
3428 case exploded_node::STATUS_PROCESSED:
3429 processed_enodes.safe_push (other_enode);
3430 break;
3431 case exploded_node::STATUS_MERGER:
3432 merger_enodes.safe_push (other_enode);
3433 break;
3434 }
3435 }
3436
3437 pretty_printer pp;
3438 pp_character (&pp, '[');
3439 print_enode_indices (&pp, processed_enodes);
3440 if (merger_enodes.length () > 0)
3441 {
3442 pp_string (&pp, "] merger(s): [");
3443 print_enode_indices (&pp, merger_enodes);
3444 }
3445 if (worklist_enodes.length () > 0)
3446 {
3447 pp_string (&pp, "] worklist: [");
3448 print_enode_indices (&pp, worklist_enodes);
3449 }
3450 pp_character (&pp, ']');
3451
3452 warning_n (stmt->location, 0, processed_enodes.length (),
3453 "%i processed enode: %s",
3454 "%i processed enodes: %s",
3455 processed_enodes.length (), pp_formatted_text (&pp));
3456 seen.add (stmt);
3457
3458 /* If the argument is non-zero, then print all of the states
3459 of the various enodes. */
3460 tree t_arg = fold (gimple_call_arg (call, 0));
3461 if (TREE_CODE (t_arg) != INTEGER_CST)
3462 {
3463 error_at (call->location,
3464 "integer constant required for arg 1");
3465 return;
3466 }
3467 int i_arg = TREE_INT_CST_LOW (t_arg);
3468 if (i_arg)
3469 {
3470 exploded_node *other_enode;
3471 FOR_EACH_VEC_ELT (processed_enodes, j, other_enode)
3472 {
3473 fprintf (stderr, "%i of %i: EN %i:\n",
3474 j + 1, processed_enodes.length (),
3475 other_enode->m_index);
3476 other_enode->dump_succs_and_preds (stderr);
3477 /* Dump state. */
3478 other_enode->get_state ().dump (m_ext_state, false);
3479 }
3480 }
3481 }
3482 }
3483 }
3484
3485 /* A collection of classes for visualizing the callgraph in .dot form
3486 (as represented in the supergraph). */
3487
3488 /* Forward decls. */
3489 class viz_callgraph_node;
3490 class viz_callgraph_edge;
3491 class viz_callgraph;
3492 class viz_callgraph_cluster;
3493
3494 /* Traits for using "digraph.h" to visualize the callgraph. */
3495
3496 struct viz_callgraph_traits
3497 {
3498 typedef viz_callgraph_node node_t;
3499 typedef viz_callgraph_edge edge_t;
3500 typedef viz_callgraph graph_t;
3501 struct dump_args_t
3502 {
dump_args_tana::viz_callgraph_traits::dump_args_t3503 dump_args_t (const exploded_graph *eg) : m_eg (eg) {}
3504 const exploded_graph *m_eg;
3505 };
3506 typedef viz_callgraph_cluster cluster_t;
3507 };
3508
3509 /* Subclass of dnode representing a function within the callgraph. */
3510
3511 class viz_callgraph_node : public dnode<viz_callgraph_traits>
3512 {
3513 friend class viz_callgraph;
3514
3515 public:
viz_callgraph_node(function * fun,int index)3516 viz_callgraph_node (function *fun, int index)
3517 : m_fun (fun), m_index (index), m_num_supernodes (0), m_num_superedges (0)
3518 {
3519 gcc_assert (fun);
3520 }
3521
dump_dot(graphviz_out * gv,const dump_args_t & args) const3522 void dump_dot (graphviz_out *gv, const dump_args_t &args) const FINAL OVERRIDE
3523 {
3524 pretty_printer *pp = gv->get_pp ();
3525
3526 dump_dot_id (pp);
3527 pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=<",
3528 "lightgrey");
3529 pp_string (pp, "<TABLE BORDER=\"0\">");
3530 pp_write_text_to_stream (pp);
3531
3532 gv->begin_trtd ();
3533 pp_printf (pp, "VCG: %i: %s", m_index, function_name (m_fun));
3534 gv->end_tdtr ();
3535 pp_newline (pp);
3536
3537 gv->begin_trtd ();
3538 pp_printf (pp, "supernodes: %i\n", m_num_supernodes);
3539 gv->end_tdtr ();
3540 pp_newline (pp);
3541
3542 gv->begin_trtd ();
3543 pp_printf (pp, "superedges: %i\n", m_num_superedges);
3544 gv->end_tdtr ();
3545 pp_newline (pp);
3546
3547 if (args.m_eg)
3548 {
3549 unsigned i;
3550 exploded_node *enode;
3551 unsigned num_enodes = 0;
3552 FOR_EACH_VEC_ELT (args.m_eg->m_nodes, i, enode)
3553 {
3554 if (enode->get_point ().get_function () == m_fun)
3555 num_enodes++;
3556 }
3557 gv->begin_trtd ();
3558 pp_printf (pp, "enodes: %i\n", num_enodes);
3559 gv->end_tdtr ();
3560 pp_newline (pp);
3561
3562 // TODO: also show the per-callstring breakdown
3563 const exploded_graph::call_string_data_map_t *per_cs_data
3564 = args.m_eg->get_per_call_string_data ();
3565 for (exploded_graph::call_string_data_map_t::iterator iter
3566 = per_cs_data->begin ();
3567 iter != per_cs_data->end ();
3568 ++iter)
3569 {
3570 const call_string *cs = (*iter).first;
3571 //per_call_string_data *data = (*iter).second;
3572 num_enodes = 0;
3573 FOR_EACH_VEC_ELT (args.m_eg->m_nodes, i, enode)
3574 {
3575 if (enode->get_point ().get_function () == m_fun
3576 && enode->get_point ().get_call_string () == *cs)
3577 num_enodes++;
3578 }
3579 if (num_enodes > 0)
3580 {
3581 gv->begin_trtd ();
3582 cs->print (pp);
3583 pp_printf (pp, ": %i\n", num_enodes);
3584 pp_write_text_as_html_like_dot_to_stream (pp);
3585 gv->end_tdtr ();
3586 }
3587 }
3588
3589 /* Show any summaries. */
3590 per_function_data *data = args.m_eg->get_per_function_data (m_fun);
3591 if (data)
3592 {
3593 pp_newline (pp);
3594 gv->begin_trtd ();
3595 pp_printf (pp, "summaries: %i\n", data->m_summaries.length ());
3596 pp_write_text_as_html_like_dot_to_stream (pp);
3597 gv->end_tdtr ();
3598 }
3599 }
3600
3601 pp_string (pp, "</TABLE>>];\n\n");
3602 pp_flush (pp);
3603 }
3604
dump_dot_id(pretty_printer * pp) const3605 void dump_dot_id (pretty_printer *pp) const
3606 {
3607 pp_printf (pp, "vcg_%i", m_index);
3608 }
3609
3610 private:
3611 function *m_fun;
3612 int m_index;
3613 int m_num_supernodes;
3614 int m_num_superedges;
3615 };
3616
3617 /* Subclass of dedge representing a callgraph edge. */
3618
3619 class viz_callgraph_edge : public dedge<viz_callgraph_traits>
3620 {
3621 public:
viz_callgraph_edge(viz_callgraph_node * src,viz_callgraph_node * dest)3622 viz_callgraph_edge (viz_callgraph_node *src, viz_callgraph_node *dest)
3623 : dedge<viz_callgraph_traits> (src, dest)
3624 {}
3625
dump_dot(graphviz_out * gv,const dump_args_t &) const3626 void dump_dot (graphviz_out *gv, const dump_args_t &) const
3627 FINAL OVERRIDE
3628 {
3629 pretty_printer *pp = gv->get_pp ();
3630
3631 const char *style = "\"solid,bold\"";
3632 const char *color = "black";
3633 int weight = 10;
3634 const char *constraint = "true";
3635
3636 m_src->dump_dot_id (pp);
3637 pp_string (pp, " -> ");
3638 m_dest->dump_dot_id (pp);
3639 pp_printf (pp,
3640 (" [style=%s, color=%s, weight=%d, constraint=%s,"
3641 " headlabel=\""),
3642 style, color, weight, constraint);
3643 pp_printf (pp, "\"];\n");
3644 }
3645 };
3646
3647 /* Subclass of digraph representing the callgraph. */
3648
3649 class viz_callgraph : public digraph<viz_callgraph_traits>
3650 {
3651 public:
3652 viz_callgraph (const supergraph &sg);
3653
get_vcg_node_for_function(function * fun)3654 viz_callgraph_node *get_vcg_node_for_function (function *fun)
3655 {
3656 return *m_map.get (fun);
3657 }
3658
get_vcg_node_for_snode(supernode * snode)3659 viz_callgraph_node *get_vcg_node_for_snode (supernode *snode)
3660 {
3661 return get_vcg_node_for_function (snode->m_fun);
3662 }
3663
3664 private:
3665 hash_map<function *, viz_callgraph_node *> m_map;
3666 };
3667
3668 /* Placeholder subclass of cluster. */
3669
3670 class viz_callgraph_cluster : public cluster<viz_callgraph_traits>
3671 {
3672 };
3673
3674 /* viz_callgraph's ctor. */
3675
viz_callgraph(const supergraph & sg)3676 viz_callgraph::viz_callgraph (const supergraph &sg)
3677 {
3678 cgraph_node *node;
3679 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
3680 {
3681 function *fun = node->get_fun ();
3682 viz_callgraph_node *vcg_node
3683 = new viz_callgraph_node (fun, m_nodes.length ());
3684 m_map.put (fun, vcg_node);
3685 add_node (vcg_node);
3686 }
3687
3688 unsigned i;
3689 superedge *sedge;
3690 FOR_EACH_VEC_ELT (sg.m_edges, i, sedge)
3691 {
3692 viz_callgraph_node *vcg_src = get_vcg_node_for_snode (sedge->m_src);
3693 if (vcg_src->m_fun)
3694 get_vcg_node_for_function (vcg_src->m_fun)->m_num_superedges++;
3695 if (sedge->dyn_cast_call_superedge ())
3696 {
3697 viz_callgraph_node *vcg_dest = get_vcg_node_for_snode (sedge->m_dest);
3698 viz_callgraph_edge *vcg_edge
3699 = new viz_callgraph_edge (vcg_src, vcg_dest);
3700 add_edge (vcg_edge);
3701 }
3702 }
3703
3704 supernode *snode;
3705 FOR_EACH_VEC_ELT (sg.m_nodes, i, snode)
3706 {
3707 if (snode->m_fun)
3708 get_vcg_node_for_function (snode->m_fun)->m_num_supernodes++;
3709 }
3710 }
3711
3712 /* Dump the callgraph to FILENAME. */
3713
3714 static void
dump_callgraph(const supergraph & sg,const char * filename,const exploded_graph * eg)3715 dump_callgraph (const supergraph &sg, const char *filename,
3716 const exploded_graph *eg)
3717 {
3718 FILE *outf = fopen (filename, "w");
3719 if (!outf)
3720 return;
3721
3722 // TODO
3723 viz_callgraph vcg (sg);
3724 vcg.dump_dot (filename, NULL, viz_callgraph_traits::dump_args_t (eg));
3725
3726 fclose (outf);
3727 }
3728
3729 /* Dump the callgraph to "<srcfile>.callgraph.dot". */
3730
3731 static void
dump_callgraph(const supergraph & sg,const exploded_graph * eg)3732 dump_callgraph (const supergraph &sg, const exploded_graph *eg)
3733 {
3734 auto_timevar tv (TV_ANALYZER_DUMP);
3735 char *filename = concat (dump_base_name, ".callgraph.dot", NULL);
3736 dump_callgraph (sg, filename, eg);
3737 free (filename);
3738 }
3739
3740 /* Subclass of dot_annotator for implementing
3741 DUMP_BASE_NAME.supergraph-eg.dot, a post-analysis dump of the supergraph.
3742
3743 Annotate the supergraph nodes by printing the exploded nodes in concise
3744 form within them, next to their pertinent statements where appropriate,
3745 colorizing the exploded nodes based on sm-state.
3746 Also show saved diagnostics within the exploded nodes, giving information
3747 on whether they were feasible, and, if infeasible, where the problem
3748 was. */
3749
3750 class exploded_graph_annotator : public dot_annotator
3751 {
3752 public:
exploded_graph_annotator(const exploded_graph & eg)3753 exploded_graph_annotator (const exploded_graph &eg)
3754 : m_eg (eg)
3755 {
3756 /* Avoid O(N^2) by prepopulating m_enodes_per_snodes. */
3757 unsigned i;
3758 supernode *snode;
3759 FOR_EACH_VEC_ELT (eg.get_supergraph ().m_nodes, i, snode)
3760 m_enodes_per_snodes.safe_push (new auto_vec <exploded_node *> ());
3761 exploded_node *enode;
3762 FOR_EACH_VEC_ELT (m_eg.m_nodes, i, enode)
3763 if (enode->get_supernode ())
3764 m_enodes_per_snodes[enode->get_supernode ()->m_index]->safe_push (enode);
3765 }
3766
3767 /* Show exploded nodes for BEFORE_SUPERNODE points before N. */
add_node_annotations(graphviz_out * gv,const supernode & n,bool within_table) const3768 bool add_node_annotations (graphviz_out *gv, const supernode &n,
3769 bool within_table)
3770 const FINAL OVERRIDE
3771 {
3772 if (!within_table)
3773 return false;
3774 gv->begin_tr ();
3775 pretty_printer *pp = gv->get_pp ();
3776
3777 gv->begin_td ();
3778 pp_string (pp, "BEFORE");
3779 gv->end_td ();
3780
3781 unsigned i;
3782 exploded_node *enode;
3783 bool had_enode = false;
3784 FOR_EACH_VEC_ELT (*m_enodes_per_snodes[n.m_index], i, enode)
3785 {
3786 gcc_assert (enode->get_supernode () == &n);
3787 const program_point &point = enode->get_point ();
3788 if (point.get_kind () != PK_BEFORE_SUPERNODE)
3789 continue;
3790 print_enode (gv, enode);
3791 had_enode = true;
3792 }
3793 if (!had_enode)
3794 pp_string (pp, "<TD BGCOLOR=\"red\">UNREACHED</TD>");
3795 pp_flush (pp);
3796 gv->end_tr ();
3797 return true;
3798 }
3799
3800 /* Show exploded nodes for STMT. */
add_stmt_annotations(graphviz_out * gv,const gimple * stmt,bool within_row) const3801 void add_stmt_annotations (graphviz_out *gv, const gimple *stmt,
3802 bool within_row)
3803 const FINAL OVERRIDE
3804 {
3805 if (!within_row)
3806 return;
3807 pretty_printer *pp = gv->get_pp ();
3808
3809 const supernode *snode
3810 = m_eg.get_supergraph ().get_supernode_for_stmt (stmt);
3811 unsigned i;
3812 exploded_node *enode;
3813 bool had_td = false;
3814 FOR_EACH_VEC_ELT (*m_enodes_per_snodes[snode->m_index], i, enode)
3815 {
3816 const program_point &point = enode->get_point ();
3817 if (point.get_kind () != PK_BEFORE_STMT)
3818 continue;
3819 if (point.get_stmt () != stmt)
3820 continue;
3821 print_enode (gv, enode);
3822 had_td = true;
3823 }
3824 pp_flush (pp);
3825 if (!had_td)
3826 {
3827 gv->begin_td ();
3828 gv->end_td ();
3829 }
3830 }
3831
3832 /* Show exploded nodes for AFTER_SUPERNODE points after N. */
add_after_node_annotations(graphviz_out * gv,const supernode & n) const3833 bool add_after_node_annotations (graphviz_out *gv, const supernode &n)
3834 const FINAL OVERRIDE
3835 {
3836 gv->begin_tr ();
3837 pretty_printer *pp = gv->get_pp ();
3838
3839 gv->begin_td ();
3840 pp_string (pp, "AFTER");
3841 gv->end_td ();
3842
3843 unsigned i;
3844 exploded_node *enode;
3845 FOR_EACH_VEC_ELT (*m_enodes_per_snodes[n.m_index], i, enode)
3846 {
3847 gcc_assert (enode->get_supernode () == &n);
3848 const program_point &point = enode->get_point ();
3849 if (point.get_kind () != PK_AFTER_SUPERNODE)
3850 continue;
3851 print_enode (gv, enode);
3852 }
3853 pp_flush (pp);
3854 gv->end_tr ();
3855 return true;
3856 }
3857
3858 private:
3859 /* Concisely print a TD element for ENODE, showing the index, status,
3860 and any saved_diagnostics at the enode. Colorize it to show sm-state.
3861
3862 Ideally we'd dump ENODE's state here, hidden behind some kind of
3863 interactive disclosure method like a tooltip, so that the states
3864 can be explored without overwhelming the graph.
3865 However, I wasn't able to get graphviz/xdot to show tooltips on
3866 individual elements within a HTML-like label. */
print_enode(graphviz_out * gv,const exploded_node * enode) const3867 void print_enode (graphviz_out *gv, const exploded_node *enode) const
3868 {
3869 pretty_printer *pp = gv->get_pp ();
3870 pp_printf (pp, "<TD BGCOLOR=\"%s\">",
3871 enode->get_dot_fillcolor ());
3872 pp_printf (pp, "<TABLE BORDER=\"0\">");
3873 gv->begin_trtd ();
3874 pp_printf (pp, "EN: %i", enode->m_index);
3875 switch (enode->get_status ())
3876 {
3877 default:
3878 gcc_unreachable ();
3879 case exploded_node::STATUS_WORKLIST:
3880 pp_string (pp, "(W)");
3881 break;
3882 case exploded_node::STATUS_PROCESSED:
3883 break;
3884 case exploded_node::STATUS_MERGER:
3885 pp_string (pp, "(M)");
3886 break;
3887 }
3888 gv->end_tdtr ();
3889 /* Dump any saved_diagnostics at this enode. */
3890 {
3891 const diagnostic_manager &dm = m_eg.get_diagnostic_manager ();
3892 for (unsigned i = 0; i < dm.get_num_diagnostics (); i++)
3893 {
3894 const saved_diagnostic *sd = dm.get_saved_diagnostic (i);
3895 if (sd->m_enode == enode)
3896 print_saved_diagnostic (gv, sd);
3897 }
3898 }
3899 pp_printf (pp, "</TABLE>");
3900 pp_printf (pp, "</TD>");
3901 }
3902
3903 /* Print a TABLE element for SD, showing the kind, the length of the
3904 exploded_path, whether the path was feasible, and if infeasible,
3905 what the problem was. */
print_saved_diagnostic(graphviz_out * gv,const saved_diagnostic * sd) const3906 void print_saved_diagnostic (graphviz_out *gv,
3907 const saved_diagnostic *sd) const
3908 {
3909 pretty_printer *pp = gv->get_pp ();
3910 gv->begin_trtd ();
3911 pp_printf (pp, "<TABLE BORDER=\"0\">");
3912 gv->begin_tr ();
3913 pp_string (pp, "<TD BGCOLOR=\"green\">");
3914 pp_printf (pp, "DIAGNOSTIC: %s", sd->m_d->get_kind ());
3915 gv->end_tdtr ();
3916 gv->begin_trtd ();
3917 pp_printf (pp, "epath length: %i", sd->get_epath_length ());
3918 gv->end_tdtr ();
3919 switch (sd->get_status ())
3920 {
3921 default:
3922 case saved_diagnostic::STATUS_NEW:
3923 gcc_unreachable ();
3924 break;
3925 case saved_diagnostic::STATUS_INFEASIBLE_PATH:
3926 {
3927 gv->begin_trtd ();
3928 pp_printf (pp, "INFEASIBLE");
3929 gv->end_tdtr ();
3930 const feasibility_problem *p = sd->get_feasibility_problem ();
3931 gcc_assert (p);
3932 gv->begin_trtd ();
3933 pp_printf (pp, "at eedge %i: EN:%i -> EN:%i",
3934 p->m_eedge_idx,
3935 p->m_eedge.m_src->m_index,
3936 p->m_eedge.m_dest->m_index);
3937 pp_write_text_as_html_like_dot_to_stream (pp);
3938 gv->end_tdtr ();
3939 gv->begin_trtd ();
3940 p->m_eedge.m_sedge->dump (pp);
3941 pp_write_text_as_html_like_dot_to_stream (pp);
3942 gv->end_tdtr ();
3943 gv->begin_trtd ();
3944 pp_gimple_stmt_1 (pp, p->m_last_stmt, 0, (dump_flags_t)0);
3945 pp_write_text_as_html_like_dot_to_stream (pp);
3946 gv->end_tdtr ();
3947 /* Ideally we'd print p->m_model here; see the notes above about
3948 tooltips. */
3949 }
3950 break;
3951 case saved_diagnostic::STATUS_FEASIBLE_PATH:
3952 gv->begin_trtd ();
3953 pp_printf (pp, "FEASIBLE");
3954 gv->end_tdtr ();
3955 break;
3956 }
3957 pp_printf (pp, "</TABLE>");
3958 gv->end_tdtr ();
3959 }
3960
3961 const exploded_graph &m_eg;
3962 auto_delete_vec<auto_vec <exploded_node *> > m_enodes_per_snodes;
3963 };
3964
3965 /* Run the analysis "engine". */
3966
3967 void
impl_run_checkers(logger * logger)3968 impl_run_checkers (logger *logger)
3969 {
3970 LOG_SCOPE (logger);
3971
3972 /* If using LTO, ensure that the cgraph nodes have function bodies. */
3973 cgraph_node *node;
3974 FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
3975 node->get_untransformed_body ();
3976
3977 /* Create the supergraph. */
3978 supergraph sg (logger);
3979
3980 state_purge_map *purge_map = NULL;
3981
3982 if (flag_analyzer_state_purge)
3983 purge_map = new state_purge_map (sg, logger);
3984
3985 if (flag_dump_analyzer_supergraph)
3986 {
3987 /* Dump supergraph pre-analysis. */
3988 auto_timevar tv (TV_ANALYZER_DUMP);
3989 char *filename = concat (dump_base_name, ".supergraph.dot", NULL);
3990 supergraph::dump_args_t args ((enum supergraph_dot_flags)0, NULL);
3991 sg.dump_dot (filename, args);
3992 free (filename);
3993 }
3994
3995 if (flag_dump_analyzer_state_purge)
3996 {
3997 auto_timevar tv (TV_ANALYZER_DUMP);
3998 state_purge_annotator a (purge_map);
3999 char *filename = concat (dump_base_name, ".state-purge.dot", NULL);
4000 supergraph::dump_args_t args ((enum supergraph_dot_flags)0, &a);
4001 sg.dump_dot (filename, args);
4002 free (filename);
4003 }
4004
4005 auto_delete_vec <state_machine> checkers;
4006 make_checkers (checkers, logger);
4007
4008 if (logger)
4009 {
4010 int i;
4011 state_machine *sm;
4012 FOR_EACH_VEC_ELT (checkers, i, sm)
4013 logger->log ("checkers[%i]: %s", i, sm->get_name ());
4014 }
4015
4016 /* Extrinsic state shared by nodes in the graph. */
4017 const extrinsic_state ext_state (checkers);
4018
4019 const analysis_plan plan (sg, logger);
4020
4021 /* The exploded graph. */
4022 exploded_graph eg (sg, logger, ext_state, purge_map, plan,
4023 analyzer_verbosity);
4024
4025 /* Add entrypoints to the graph for externally-callable functions. */
4026 eg.build_initial_worklist ();
4027
4028 /* Now process the worklist, exploring the <point, state> graph. */
4029 eg.process_worklist ();
4030
4031 if (flag_dump_analyzer_exploded_graph)
4032 {
4033 auto_timevar tv (TV_ANALYZER_DUMP);
4034 char *filename
4035 = concat (dump_base_name, ".eg.dot", NULL);
4036 exploded_graph::dump_args_t args (eg);
4037 root_cluster c;
4038 eg.dump_dot (filename, &c, args);
4039 free (filename);
4040 }
4041
4042 /* Now emit any saved diagnostics. */
4043 eg.get_diagnostic_manager ().emit_saved_diagnostics (eg);
4044
4045 eg.dump_exploded_nodes ();
4046
4047 eg.log_stats ();
4048
4049 if (flag_dump_analyzer_callgraph)
4050 dump_callgraph (sg, &eg);
4051
4052 if (flag_dump_analyzer_supergraph)
4053 {
4054 /* Dump post-analysis form of supergraph. */
4055 auto_timevar tv (TV_ANALYZER_DUMP);
4056 char *filename = concat (dump_base_name, ".supergraph-eg.dot", NULL);
4057 exploded_graph_annotator a (eg);
4058 supergraph::dump_args_t args ((enum supergraph_dot_flags)0, &a);
4059 sg.dump_dot (filename, args);
4060 free (filename);
4061 }
4062
4063 delete purge_map;
4064 }
4065
4066 /* External entrypoint to the analysis "engine".
4067 Set up any dumps, then call impl_run_checkers. */
4068
4069 void
run_checkers()4070 run_checkers ()
4071 {
4072 /* Save input_location. */
4073 location_t saved_input_location = input_location;
4074
4075 /* Handle -fdump-analyzer and -fdump-analyzer-stderr. */
4076 FILE *dump_fout = NULL;
4077 /* Track if we're responsible for closing dump_fout. */
4078 bool owns_dump_fout = false;
4079 if (flag_dump_analyzer_stderr)
4080 dump_fout = stderr;
4081 else if (flag_dump_analyzer)
4082 {
4083 char *dump_filename = concat (dump_base_name, ".analyzer.txt", NULL);
4084 dump_fout = fopen (dump_filename, "w");
4085 free (dump_filename);
4086 if (dump_fout)
4087 owns_dump_fout = true;
4088 }
4089
4090 {
4091 log_user the_logger (NULL);
4092 if (dump_fout)
4093 the_logger.set_logger (new logger (dump_fout, 0, 0,
4094 *global_dc->printer));
4095 LOG_SCOPE (the_logger.get_logger ());
4096
4097 impl_run_checkers (the_logger.get_logger ());
4098
4099 /* end of lifetime of the_logger (so that dump file is closed after the
4100 various dtors run). */
4101 }
4102
4103 if (owns_dump_fout)
4104 fclose (dump_fout);
4105
4106 /* Restore input_location. Subsequent passes may assume that input_location
4107 is some arbitrary value *not* in the block tree, which might be violated
4108 if we didn't restore it. */
4109 input_location = saved_input_location;
4110 }
4111
4112 } // namespace ana
4113
4114 #endif /* #if ENABLE_ANALYZER */
4115