Branch data Line data Source code
1 : : /* The analysis "engine".
2 : : Copyright (C) 2019-2025 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 "analyzer/common.h"
22 : :
23 : : #include <zlib.h>
24 : :
25 : : #include "cfg.h"
26 : : #include "gcc-rich-location.h"
27 : : #include "gimple-iterator.h"
28 : : #include "gimple-pretty-print.h"
29 : : #include "cgraph.h"
30 : : #include "fold-const.h"
31 : : #include "digraph.h"
32 : : #include "plugin.h"
33 : : #include "target.h"
34 : : #include "stringpool.h"
35 : : #include "attribs.h"
36 : : #include "tree-dfa.h"
37 : :
38 : : #include "text-art/dump.h"
39 : :
40 : : #include "analyzer/analyzer-logging.h"
41 : : #include "analyzer/call-string.h"
42 : : #include "analyzer/program-point.h"
43 : : #include "analyzer/store.h"
44 : : #include "analyzer/region-model.h"
45 : : #include "analyzer/constraint-manager.h"
46 : : #include "analyzer/sm.h"
47 : : #include "analyzer/pending-diagnostic.h"
48 : : #include "analyzer/diagnostic-manager.h"
49 : : #include "analyzer/supergraph.h"
50 : : #include "analyzer/program-state.h"
51 : : #include "analyzer/exploded-graph.h"
52 : : #include "analyzer/analysis-plan.h"
53 : : #include "analyzer/checker-path.h"
54 : : #include "analyzer/state-purge.h"
55 : : #include "analyzer/bar-chart.h"
56 : : #include "analyzer/call-info.h"
57 : : #include "analyzer/known-function-manager.h"
58 : : #include "analyzer/call-summary.h"
59 : :
60 : : /* For an overview, see gcc/doc/analyzer.texi. */
61 : :
62 : : #if ENABLE_ANALYZER
63 : :
64 : : namespace ana {
65 : :
66 : : /* class impl_region_model_context : public region_model_context. */
67 : :
68 : 1452702 : impl_region_model_context::
69 : : impl_region_model_context (exploded_graph &eg,
70 : : exploded_node *enode_for_diag,
71 : : const program_state *old_state,
72 : : program_state *new_state,
73 : : uncertainty_t *uncertainty,
74 : : path_context *path_ctxt,
75 : : const gimple *stmt,
76 : : stmt_finder *stmt_finder,
77 : 1452702 : bool *out_could_have_done_work)
78 : 1452702 : : m_eg (&eg), m_logger (eg.get_logger ()),
79 : 1452702 : m_enode_for_diag (enode_for_diag),
80 : 1452702 : m_old_state (old_state),
81 : 1452702 : m_new_state (new_state),
82 : 1452702 : m_stmt (stmt),
83 : 1452702 : m_stmt_finder (stmt_finder),
84 : 1452702 : m_ext_state (eg.get_ext_state ()),
85 : 1452702 : m_uncertainty (uncertainty),
86 : 1452702 : m_path_ctxt (path_ctxt),
87 : 1452702 : m_out_could_have_done_work (out_could_have_done_work)
88 : : {
89 : 1452702 : }
90 : :
91 : 4 : impl_region_model_context::
92 : : impl_region_model_context (program_state *state,
93 : : const extrinsic_state &ext_state,
94 : : uncertainty_t *uncertainty,
95 : 4 : logger *logger)
96 : 4 : : m_eg (nullptr), m_logger (logger), m_enode_for_diag (nullptr),
97 : 4 : m_old_state (nullptr),
98 : 4 : m_new_state (state),
99 : 4 : m_stmt (nullptr),
100 : 4 : m_stmt_finder (nullptr),
101 : 4 : m_ext_state (ext_state),
102 : 4 : m_uncertainty (uncertainty),
103 : 4 : m_path_ctxt (nullptr),
104 : 4 : m_out_could_have_done_work (nullptr)
105 : : {
106 : 4 : }
107 : :
108 : : bool
109 : 3527 : impl_region_model_context::warn (std::unique_ptr<pending_diagnostic> d,
110 : : const stmt_finder *custom_finder)
111 : : {
112 : 3527 : LOG_FUNC (get_logger ());
113 : 3527 : auto curr_stmt_finder = custom_finder ? custom_finder : m_stmt_finder;
114 : 3527 : if (m_stmt == nullptr && curr_stmt_finder == nullptr)
115 : : {
116 : 305 : if (get_logger ())
117 : 0 : get_logger ()->log ("rejecting diagnostic: no stmt");
118 : 305 : return false;
119 : : }
120 : 3222 : if (m_eg)
121 : : {
122 : 3222 : bool terminate_path = d->terminate_path_p ();
123 : 3222 : pending_location ploc (m_enode_for_diag,
124 : 3222 : m_enode_for_diag->get_supernode (),
125 : : m_stmt,
126 : 3222 : curr_stmt_finder);
127 : 3222 : if (m_eg->get_diagnostic_manager ().add_diagnostic (ploc,
128 : : std::move (d)))
129 : : {
130 : 3181 : if (m_path_ctxt
131 : 2264 : && terminate_path
132 : 879 : && flag_analyzer_suppress_followups)
133 : 628 : m_path_ctxt->terminate_path ();
134 : 3181 : return true;
135 : : }
136 : : }
137 : : return false;
138 : 3527 : }
139 : :
140 : : void
141 : 224 : impl_region_model_context::add_note (std::unique_ptr<pending_note> pn)
142 : : {
143 : 224 : LOG_FUNC (get_logger ());
144 : 224 : if (m_eg)
145 : 224 : m_eg->get_diagnostic_manager ().add_note (std::move (pn));
146 : 224 : }
147 : :
148 : : void
149 : 188 : impl_region_model_context::add_event (std::unique_ptr<checker_event> event)
150 : : {
151 : 188 : LOG_FUNC (get_logger ());
152 : 188 : if (m_eg)
153 : 188 : m_eg->get_diagnostic_manager ().add_event (std::move (event));
154 : 188 : }
155 : :
156 : : void
157 : 75897 : impl_region_model_context::on_svalue_leak (const svalue *sval)
158 : :
159 : : {
160 : 758591 : for (sm_state_map *smap : m_new_state->m_checker_states)
161 : 530900 : smap->on_svalue_leak (sval, this);
162 : 75897 : }
163 : :
164 : : void
165 : 594762 : impl_region_model_context::
166 : : on_liveness_change (const svalue_set &live_svalues,
167 : : const region_model *model)
168 : : {
169 : 5943406 : for (sm_state_map *smap : m_new_state->m_checker_states)
170 : 4159120 : smap->on_liveness_change (live_svalues, model, m_ext_state, this);
171 : 594762 : }
172 : :
173 : : void
174 : 63713 : impl_region_model_context::on_unknown_change (const svalue *sval,
175 : : bool is_mutable)
176 : : {
177 : 63713 : if (!sval->can_have_associated_state_p ())
178 : : return;
179 : 500035 : for (sm_state_map *smap : m_new_state->m_checker_states)
180 : 349999 : smap->on_unknown_change (sval, is_mutable, m_ext_state);
181 : : }
182 : :
183 : : void
184 : 380 : impl_region_model_context::on_escaped_function (tree fndecl)
185 : : {
186 : 380 : m_eg->on_escaped_function (fndecl);
187 : 380 : }
188 : :
189 : : uncertainty_t *
190 : 4963661 : impl_region_model_context::get_uncertainty ()
191 : : {
192 : 4963661 : return m_uncertainty;
193 : : }
194 : :
195 : : /* Purge state involving SVAL. The region_model has already been purged,
196 : : so we only need to purge other state in the program_state:
197 : : the sm-state. */
198 : :
199 : : void
200 : 22432 : impl_region_model_context::purge_state_involving (const svalue *sval)
201 : : {
202 : 22432 : int i;
203 : 22432 : sm_state_map *smap;
204 : 179456 : FOR_EACH_VEC_ELT (m_new_state->m_checker_states, i, smap)
205 : 157024 : smap->purge_state_involving (sval, m_ext_state);
206 : 22432 : }
207 : :
208 : : void
209 : 10629 : impl_region_model_context::bifurcate (std::unique_ptr<custom_edge_info> info)
210 : : {
211 : 10629 : if (m_path_ctxt)
212 : 10629 : m_path_ctxt->bifurcate (std::move (info));
213 : 10629 : }
214 : :
215 : : void
216 : 1643 : impl_region_model_context::terminate_path ()
217 : : {
218 : 1643 : if (m_path_ctxt)
219 : 1643 : return m_path_ctxt->terminate_path ();
220 : : }
221 : :
222 : : /* struct setjmp_record. */
223 : :
224 : : int
225 : 0 : setjmp_record::cmp (const setjmp_record &rec1, const setjmp_record &rec2)
226 : : {
227 : 0 : if (int cmp_enode = rec1.m_enode->m_index - rec2.m_enode->m_index)
228 : : return cmp_enode;
229 : 0 : gcc_assert (&rec1 == &rec2);
230 : : return 0;
231 : : }
232 : :
233 : : /* class setjmp_svalue : public svalue. */
234 : :
235 : : /* Implementation of svalue::accept vfunc for setjmp_svalue. */
236 : :
237 : : void
238 : 1556 : setjmp_svalue::accept (visitor *v) const
239 : : {
240 : 1556 : v->visit_setjmp_svalue (this);
241 : 1556 : }
242 : :
243 : : /* Implementation of svalue::dump_to_pp vfunc for setjmp_svalue. */
244 : :
245 : : void
246 : 0 : setjmp_svalue::dump_to_pp (pretty_printer *pp, bool simple) const
247 : : {
248 : 0 : if (simple)
249 : 0 : pp_printf (pp, "SETJMP(EN: %i)", get_enode_index ());
250 : : else
251 : 0 : pp_printf (pp, "setjmp_svalue(EN%i)", get_enode_index ());
252 : 0 : }
253 : :
254 : : /* Implementation of svalue::print_dump_widget_label vfunc for
255 : : setjmp_svalue. */
256 : :
257 : : void
258 : 0 : setjmp_svalue::print_dump_widget_label (pretty_printer *pp) const
259 : : {
260 : 0 : pp_printf (pp, "setjmp_svalue(EN: %i)", get_enode_index ());
261 : 0 : }
262 : :
263 : : /* Implementation of svalue::add_dump_widget_children vfunc for
264 : : setjmp_svalue. */
265 : :
266 : : void
267 : 0 : setjmp_svalue::
268 : : add_dump_widget_children (text_art::tree_widget &,
269 : : const text_art::dump_widget_info &) const
270 : : {
271 : : /* No children. */
272 : 0 : }
273 : :
274 : : /* Get the index of the stored exploded_node. */
275 : :
276 : : int
277 : 0 : setjmp_svalue::get_enode_index () const
278 : : {
279 : 0 : return m_setjmp_record.m_enode->m_index;
280 : : }
281 : :
282 : : /* Concrete implementation of sm_context, wiring it up to the rest of this
283 : : file. */
284 : :
285 : 2143013 : class impl_sm_context : public sm_context
286 : : {
287 : : public:
288 : 2143791 : impl_sm_context (exploded_graph &eg,
289 : : int sm_idx,
290 : : const state_machine &sm,
291 : : exploded_node *enode_for_diag,
292 : : const program_state *old_state,
293 : : program_state *new_state,
294 : : const sm_state_map *old_smap,
295 : : sm_state_map *new_smap,
296 : : path_context *path_ctxt,
297 : : const stmt_finder *stmt_finder = nullptr,
298 : : bool unknown_side_effects = false)
299 : 2143791 : : sm_context (sm_idx, sm),
300 : 2143791 : m_logger (eg.get_logger ()),
301 : 2143791 : m_eg (eg), m_enode_for_diag (enode_for_diag),
302 : 2143791 : m_old_state (old_state), m_new_state (new_state),
303 : 2143791 : m_old_smap (old_smap), m_new_smap (new_smap),
304 : 2143791 : m_path_ctxt (path_ctxt),
305 : 2143791 : m_stmt_finder (stmt_finder),
306 : 778 : m_unknown_side_effects (unknown_side_effects)
307 : : {
308 : : }
309 : :
310 : 221062 : logger *get_logger () const { return m_logger.get_logger (); }
311 : :
312 : 421576 : tree get_fndecl_for_call (const gcall &call) final override
313 : : {
314 : 421576 : impl_region_model_context old_ctxt
315 : : (m_eg, m_enode_for_diag, nullptr, nullptr, nullptr/*m_enode->get_state ()*/,
316 : 421576 : nullptr, &call);
317 : 421576 : region_model *model = m_new_state->m_region_model;
318 : 421576 : return model->get_fndecl_for_call (call, &old_ctxt);
319 : 421576 : }
320 : :
321 : 78160 : state_machine::state_t get_state (const gimple *stmt ATTRIBUTE_UNUSED,
322 : : tree var) final override
323 : : {
324 : 78160 : logger * const logger = get_logger ();
325 : 78160 : LOG_FUNC (logger);
326 : : /* Use nullptr ctxt on this get_rvalue call to avoid triggering
327 : : uninitialized value warnings. */
328 : 78160 : const svalue *var_old_sval
329 : 78160 : = m_old_state->m_region_model->get_rvalue (var, nullptr);
330 : :
331 : 78160 : state_machine::state_t current
332 : 78160 : = m_old_smap->get_state (var_old_sval, m_eg.get_ext_state ());
333 : 156320 : return current;
334 : 78160 : }
335 : 95419 : state_machine::state_t get_state (const gimple *stmt ATTRIBUTE_UNUSED,
336 : : const svalue *sval) final override
337 : : {
338 : 95419 : logger * const logger = get_logger ();
339 : 95419 : LOG_FUNC (logger);
340 : 95419 : state_machine::state_t current
341 : 95419 : = m_old_smap->get_state (sval, m_eg.get_ext_state ());
342 : 190838 : return current;
343 : 95419 : }
344 : :
345 : :
346 : 37311 : void set_next_state (const gimple *,
347 : : tree var,
348 : : state_machine::state_t to,
349 : : tree origin) final override
350 : : {
351 : 37311 : logger * const logger = get_logger ();
352 : 37311 : LOG_FUNC (logger);
353 : 37311 : const svalue *var_new_sval
354 : 37311 : = m_new_state->m_region_model->get_rvalue (var, nullptr);
355 : 37311 : const svalue *origin_new_sval
356 : 37311 : = m_new_state->m_region_model->get_rvalue (origin, nullptr);
357 : :
358 : : /* We use the new sval here to avoid issues with uninitialized values. */
359 : 37311 : state_machine::state_t current
360 : 37311 : = m_old_smap->get_state (var_new_sval, m_eg.get_ext_state ());
361 : 37311 : if (logger)
362 : 23 : logger->log ("%s: state transition of %qE: %s -> %s",
363 : 23 : m_sm.get_name (),
364 : : var,
365 : : current->get_name (),
366 : : to->get_name ());
367 : 37311 : m_new_smap->set_state (m_new_state->m_region_model, var_new_sval,
368 : 37311 : to, origin_new_sval, m_eg.get_ext_state ());
369 : 37311 : }
370 : :
371 : 4280 : void set_next_state (const gimple *stmt,
372 : : const svalue *sval,
373 : : state_machine::state_t to,
374 : : tree origin) final override
375 : : {
376 : 4280 : logger * const logger = get_logger ();
377 : 4280 : LOG_FUNC (logger);
378 : 4280 : impl_region_model_context old_ctxt
379 : : (m_eg, m_enode_for_diag, nullptr, nullptr, nullptr/*m_enode->get_state ()*/,
380 : 4280 : nullptr, stmt);
381 : :
382 : 4280 : const svalue *origin_new_sval
383 : 4280 : = m_new_state->m_region_model->get_rvalue (origin, nullptr);
384 : :
385 : 4280 : state_machine::state_t current
386 : 4280 : = m_old_smap->get_state (sval, m_eg.get_ext_state ());
387 : 4280 : if (logger)
388 : : {
389 : 0 : logger->start_log_line ();
390 : 0 : logger->log_partial ("%s: state transition of ",
391 : 0 : m_sm.get_name ());
392 : 0 : sval->dump_to_pp (logger->get_printer (), true);
393 : 0 : logger->log_partial (": %s -> %s",
394 : : current->get_name (),
395 : : to->get_name ());
396 : 0 : logger->end_log_line ();
397 : : }
398 : 4280 : m_new_smap->set_state (m_new_state->m_region_model, sval,
399 : 4280 : to, origin_new_sval, m_eg.get_ext_state ());
400 : 4280 : }
401 : :
402 : 5779 : void warn (const supernode *snode, const gimple *stmt,
403 : : tree var,
404 : : std::unique_ptr<pending_diagnostic> d) final override
405 : : {
406 : 5779 : LOG_FUNC (get_logger ());
407 : 5779 : gcc_assert (d);
408 : 5779 : const svalue *var_old_sval
409 : 5779 : = m_old_state->m_region_model->get_rvalue (var, nullptr);
410 : 5779 : state_machine::state_t current
411 : : = (var
412 : 5779 : ? m_old_smap->get_state (var_old_sval, m_eg.get_ext_state ())
413 : 86 : : m_old_smap->get_global_state ());
414 : 5779 : bool terminate_path = d->terminate_path_p ();
415 : 5779 : pending_location ploc (m_enode_for_diag, snode, stmt, m_stmt_finder);
416 : 5779 : m_eg.get_diagnostic_manager ().add_diagnostic
417 : 5779 : (&m_sm, ploc,
418 : : var, var_old_sval, current, std::move (d));
419 : 5779 : if (m_path_ctxt
420 : 5772 : && terminate_path
421 : 301 : && flag_analyzer_suppress_followups)
422 : 217 : m_path_ctxt->terminate_path ();
423 : 5779 : }
424 : :
425 : 113 : void warn (const supernode *snode, const gimple *stmt,
426 : : const svalue *sval,
427 : : std::unique_ptr<pending_diagnostic> d) final override
428 : : {
429 : 113 : LOG_FUNC (get_logger ());
430 : 113 : gcc_assert (d);
431 : 113 : state_machine::state_t current
432 : : = (sval
433 : 113 : ? m_old_smap->get_state (sval, m_eg.get_ext_state ())
434 : 0 : : m_old_smap->get_global_state ());
435 : 113 : bool terminate_path = d->terminate_path_p ();
436 : 113 : pending_location ploc (m_enode_for_diag, snode, stmt, m_stmt_finder);
437 : 113 : m_eg.get_diagnostic_manager ().add_diagnostic
438 : 113 : (&m_sm, ploc,
439 : : NULL_TREE, sval, current, std::move (d));
440 : 113 : if (m_path_ctxt
441 : 13 : && terminate_path
442 : 0 : && flag_analyzer_suppress_followups)
443 : 0 : m_path_ctxt->terminate_path ();
444 : 113 : }
445 : :
446 : : /* Hook for picking more readable trees for SSA names of temporaries,
447 : : so that rather than e.g.
448 : : "double-free of '<unknown>'"
449 : : we can print:
450 : : "double-free of 'inbuf.data'". */
451 : :
452 : 6251 : tree get_diagnostic_tree (tree expr) final override
453 : : {
454 : : /* Only for SSA_NAMEs of temporaries; otherwise, return EXPR, as it's
455 : : likely to be the least surprising tree to report. */
456 : 6251 : if (TREE_CODE (expr) != SSA_NAME)
457 : : return expr;
458 : 6153 : if (SSA_NAME_VAR (expr) != NULL)
459 : : return expr;
460 : :
461 : 420 : gcc_assert (m_new_state);
462 : 420 : const svalue *sval = m_new_state->m_region_model->get_rvalue (expr, nullptr);
463 : : /* Find trees for all regions storing the value. */
464 : 420 : if (tree t = m_new_state->m_region_model->get_representative_tree (sval))
465 : : return t;
466 : : else
467 : : return expr;
468 : : }
469 : :
470 : 132 : tree get_diagnostic_tree (const svalue *sval) final override
471 : : {
472 : 132 : return m_new_state->m_region_model->get_representative_tree (sval);
473 : : }
474 : :
475 : 246926 : state_machine::state_t get_global_state () const final override
476 : : {
477 : 246926 : return m_old_state->m_checker_states[m_sm_idx]->get_global_state ();
478 : : }
479 : :
480 : 21459 : void set_global_state (state_machine::state_t state) final override
481 : : {
482 : 21459 : m_new_state->m_checker_states[m_sm_idx]->set_global_state (state);
483 : 21459 : }
484 : :
485 : 9008 : void clear_all_per_svalue_state () final override
486 : : {
487 : 9008 : m_new_state->m_checker_states[m_sm_idx]->clear_all_per_svalue_state ();
488 : 9008 : }
489 : :
490 : 10 : void on_custom_transition (custom_transition *transition) final override
491 : : {
492 : 10 : transition->impl_transition (&m_eg,
493 : : const_cast<exploded_node *> (m_enode_for_diag),
494 : : m_sm_idx);
495 : 10 : }
496 : :
497 : 225982 : tree is_zero_assignment (const gimple *stmt) final override
498 : : {
499 : 225982 : const gassign *assign_stmt = dyn_cast <const gassign *> (stmt);
500 : 126291 : if (!assign_stmt)
501 : : return NULL_TREE;
502 : 126291 : impl_region_model_context old_ctxt
503 : 126291 : (m_eg, m_enode_for_diag, m_old_state, m_new_state, nullptr, nullptr, stmt);
504 : 252582 : if (const svalue *sval
505 : 126291 : = m_new_state->m_region_model->get_gassign_result (assign_stmt,
506 : : &old_ctxt))
507 : 121361 : if (tree cst = sval->maybe_get_constant ())
508 : 31407 : if (::zerop(cst))
509 : 12620 : return gimple_assign_lhs (assign_stmt);
510 : : return NULL_TREE;
511 : 126291 : }
512 : :
513 : 24 : path_context *get_path_context () const final override
514 : : {
515 : 24 : return m_path_ctxt;
516 : : }
517 : :
518 : 56141 : bool unknown_side_effects_p () const final override
519 : : {
520 : 56141 : return m_unknown_side_effects;
521 : : }
522 : :
523 : 266211 : const program_state *get_old_program_state () const final override
524 : : {
525 : 266211 : return m_old_state;
526 : : }
527 : :
528 : 1733 : const program_state *get_new_program_state () const final override
529 : : {
530 : 1733 : return m_new_state;
531 : : }
532 : :
533 : : log_user m_logger;
534 : : exploded_graph &m_eg;
535 : : exploded_node *m_enode_for_diag;
536 : : const program_state *m_old_state;
537 : : program_state *m_new_state;
538 : : const sm_state_map *m_old_smap;
539 : : sm_state_map *m_new_smap;
540 : : path_context *m_path_ctxt;
541 : : const stmt_finder *m_stmt_finder;
542 : :
543 : : /* Are we handling an external function with unknown side effects? */
544 : : bool m_unknown_side_effects;
545 : : };
546 : :
547 : : bool
548 : 687259 : impl_region_model_context::
549 : : get_state_map_by_name (const char *name,
550 : : sm_state_map **out_smap,
551 : : const state_machine **out_sm,
552 : : unsigned *out_sm_idx,
553 : : std::unique_ptr<sm_context> *out_sm_context)
554 : : {
555 : 687259 : if (!m_new_state)
556 : : return false;
557 : :
558 : 682187 : unsigned sm_idx;
559 : 682187 : if (!m_ext_state.get_sm_idx_by_name (name, &sm_idx))
560 : : return false;
561 : :
562 : 681683 : const state_machine *sm = &m_ext_state.get_sm (sm_idx);
563 : 681683 : sm_state_map *new_smap = m_new_state->m_checker_states[sm_idx];
564 : :
565 : 681683 : *out_smap = new_smap;
566 : 681683 : *out_sm = sm;
567 : 681683 : if (out_sm_idx)
568 : 680887 : *out_sm_idx = sm_idx;
569 : 681683 : if (out_sm_context)
570 : : {
571 : 778 : const sm_state_map *old_smap = m_old_state->m_checker_states[sm_idx];
572 : 778 : *out_sm_context
573 : 778 : = std::make_unique<impl_sm_context> (*m_eg,
574 : : sm_idx,
575 : : *sm,
576 : 778 : m_enode_for_diag,
577 : 778 : m_old_state,
578 : 778 : m_new_state,
579 : : old_smap,
580 : : new_smap,
581 : 778 : m_path_ctxt,
582 : 778 : m_stmt_finder,
583 : 1556 : false);
584 : : }
585 : : return true;
586 : : }
587 : :
588 : : /* Subclass of stmt_finder for finding the best stmt to report the leak at,
589 : : given the emission path. */
590 : :
591 : 1884 : class leak_stmt_finder : public stmt_finder
592 : : {
593 : : public:
594 : 3253 : leak_stmt_finder (const exploded_graph &eg, tree var)
595 : 1369 : : m_eg (eg), m_var (var) {}
596 : :
597 : 1369 : std::unique_ptr<stmt_finder> clone () const final override
598 : : {
599 : 1369 : return std::make_unique<leak_stmt_finder> (m_eg, m_var);
600 : : }
601 : :
602 : 842 : const gimple *find_stmt (const exploded_path &epath)
603 : : final override
604 : : {
605 : 842 : logger * const logger = m_eg.get_logger ();
606 : 842 : LOG_FUNC (logger);
607 : :
608 : 842 : if (m_var && TREE_CODE (m_var) == SSA_NAME)
609 : : {
610 : : /* Locate the final write to this SSA name in the path. */
611 : 796 : const gimple *def_stmt = SSA_NAME_DEF_STMT (m_var);
612 : :
613 : 796 : int idx_of_def_stmt;
614 : 796 : bool found = epath.find_stmt_backwards (def_stmt, &idx_of_def_stmt);
615 : 796 : if (!found)
616 : 62 : goto not_found;
617 : :
618 : : /* What was the next write to the underlying var
619 : : after the SSA name was set? (if any). */
620 : :
621 : 734 : for (unsigned idx = idx_of_def_stmt + 1;
622 : 16104 : idx < epath.m_edges.length ();
623 : : ++idx)
624 : : {
625 : 15386 : const exploded_edge *eedge = epath.m_edges[idx];
626 : 15386 : if (logger)
627 : 0 : logger->log ("eedge[%i]: EN %i -> EN %i",
628 : : idx,
629 : 0 : eedge->m_src->m_index,
630 : 0 : eedge->m_dest->m_index);
631 : 15386 : const exploded_node *dst_node = eedge->m_dest;
632 : 15386 : const program_point &dst_point = dst_node->get_point ();
633 : 15386 : const gimple *stmt = dst_point.get_stmt ();
634 : 15386 : if (!stmt)
635 : 5544 : continue;
636 : 17520 : if (const gassign *assign = dyn_cast <const gassign *> (stmt))
637 : : {
638 : 2150 : tree lhs = gimple_assign_lhs (assign);
639 : 2150 : if (TREE_CODE (lhs) == SSA_NAME
640 : 3016 : && SSA_NAME_VAR (lhs) == SSA_NAME_VAR (m_var))
641 : 16 : return assign;
642 : : }
643 : : }
644 : : }
645 : :
646 : 46 : not_found:
647 : :
648 : : /* Look backwards for the first statement with a location. */
649 : 826 : int i;
650 : 826 : const exploded_edge *eedge;
651 : 4274 : FOR_EACH_VEC_ELT_REVERSE (epath.m_edges, i, eedge)
652 : : {
653 : 3448 : if (logger)
654 : 0 : logger->log ("eedge[%i]: EN %i -> EN %i",
655 : : i,
656 : 0 : eedge->m_src->m_index,
657 : 0 : eedge->m_dest->m_index);
658 : 3448 : const exploded_node *dst_node = eedge->m_dest;
659 : 3448 : const program_point &dst_point = dst_node->get_point ();
660 : 3448 : const gimple *stmt = dst_point.get_stmt ();
661 : 3448 : if (stmt)
662 : 1506 : if (get_pure_location (stmt->location) != UNKNOWN_LOCATION)
663 : : return stmt;
664 : : }
665 : :
666 : 0 : gcc_unreachable ();
667 : : return nullptr;
668 : 842 : }
669 : :
670 : 578 : void update_event_loc_info (event_loc_info &) final override
671 : : {
672 : : /* No-op. */
673 : 578 : }
674 : :
675 : : private:
676 : : const exploded_graph &m_eg;
677 : : tree m_var;
678 : : };
679 : :
680 : : /* A measurement of how good EXPR is for presenting to the user, so
681 : : that e.g. we can say prefer printing
682 : : "leak of 'tmp.m_ptr'"
683 : : over:
684 : : "leak of '<unknown>'". */
685 : :
686 : : static int
687 : 20294 : readability (const_tree expr)
688 : : {
689 : : /* Arbitrarily-chosen "high readability" value. */
690 : 34563 : const int HIGH_READABILITY = 65536;
691 : :
692 : 34563 : gcc_assert (expr);
693 : 34563 : switch (TREE_CODE (expr))
694 : : {
695 : 3921 : case COMPONENT_REF:
696 : 3921 : case MEM_REF:
697 : : /* Impose a slight readability penalty relative to that of
698 : : operand 0. */
699 : 3921 : return readability (TREE_OPERAND (expr, 0)) - 16;
700 : :
701 : 15369 : case SSA_NAME:
702 : 15369 : {
703 : 15369 : if (tree var = SSA_NAME_VAR (expr))
704 : : {
705 : 8962 : if (DECL_ARTIFICIAL (var))
706 : : {
707 : : /* If we have an SSA name for an artificial var,
708 : : only use it if it has a debug expr associated with
709 : : it that fixup_tree_for_diagnostic can use. */
710 : 12 : if (VAR_P (var) && DECL_HAS_DEBUG_EXPR_P (var))
711 : 0 : return readability (DECL_DEBUG_EXPR (var)) - 1;
712 : : }
713 : : else
714 : : {
715 : : /* Slightly favor the underlying var over the SSA name to
716 : : avoid having them compare equal. */
717 : 8950 : return readability (var) - 1;
718 : : }
719 : : }
720 : : /* Avoid printing '<unknown>' for SSA names for temporaries. */
721 : : return -1;
722 : : }
723 : 10489 : break;
724 : :
725 : 10489 : case PARM_DECL:
726 : 10489 : case VAR_DECL:
727 : 10489 : if (DECL_NAME (expr))
728 : : return HIGH_READABILITY;
729 : : else
730 : : /* We don't want to print temporaries. For example, the C FE
731 : : prints them as e.g. "<Uxxxx>" where "xxxx" is the low 16 bits
732 : : of the tree pointer (see pp_c_tree_decl_identifier). */
733 : : return -1;
734 : :
735 : : case RESULT_DECL:
736 : : /* Printing "<return-value>" isn't ideal, but is less awful than
737 : : trying to print a temporary. */
738 : : return HIGH_READABILITY / 2;
739 : :
740 : 1398 : case NOP_EXPR:
741 : 1398 : {
742 : : /* Impose a moderate readability penalty for casts. */
743 : 1398 : const int CAST_PENALTY = 32;
744 : 1398 : return readability (TREE_OPERAND (expr, 0)) - CAST_PENALTY;
745 : : }
746 : :
747 : : case INTEGER_CST:
748 : : return HIGH_READABILITY;
749 : :
750 : 312 : default:
751 : 312 : return 0;
752 : : }
753 : :
754 : : return 0;
755 : : }
756 : :
757 : : /* A qsort comparator for trees to sort them into most user-readable to
758 : : least user-readable. */
759 : :
760 : : int
761 : 10147 : readability_comparator (const void *p1, const void *p2)
762 : : {
763 : 10147 : path_var pv1 = *(path_var const *)p1;
764 : 10147 : path_var pv2 = *(path_var const *)p2;
765 : :
766 : 10147 : const int tree_r1 = readability (pv1.m_tree);
767 : 10147 : const int tree_r2 = readability (pv2.m_tree);
768 : :
769 : : /* Favor items that are deeper on the stack and hence more recent;
770 : : this also favors locals over globals. */
771 : 10147 : const int COST_PER_FRAME = 64;
772 : 10147 : const int depth_r1 = pv1.m_stack_depth * COST_PER_FRAME;
773 : 10147 : const int depth_r2 = pv2.m_stack_depth * COST_PER_FRAME;
774 : :
775 : : /* Combine the scores from the tree and from the stack depth.
776 : : This e.g. lets us have a slightly penalized cast in the most
777 : : recent stack frame "beat" an uncast value in an older stack frame. */
778 : 10147 : const int sum_r1 = tree_r1 + depth_r1;
779 : 10147 : const int sum_r2 = tree_r2 + depth_r2;
780 : 10147 : if (int cmp = sum_r2 - sum_r1)
781 : : return cmp;
782 : :
783 : : /* Otherwise, more readable trees win. */
784 : 2242 : if (int cmp = tree_r2 - tree_r1)
785 : : return cmp;
786 : :
787 : : /* Otherwise, if they have the same readability, then impose an
788 : : arbitrary deterministic ordering on them. */
789 : :
790 : 2242 : if (int cmp = TREE_CODE (pv1.m_tree) - TREE_CODE (pv2.m_tree))
791 : : return cmp;
792 : :
793 : 2182 : switch (TREE_CODE (pv1.m_tree))
794 : : {
795 : : default:
796 : : break;
797 : 2152 : case SSA_NAME:
798 : 2152 : if (int cmp = (SSA_NAME_VERSION (pv1.m_tree)
799 : 2152 : - SSA_NAME_VERSION (pv2.m_tree)))
800 : : return cmp;
801 : : break;
802 : 0 : case PARM_DECL:
803 : 0 : case VAR_DECL:
804 : 0 : case RESULT_DECL:
805 : 0 : if (int cmp = DECL_UID (pv1.m_tree) - DECL_UID (pv2.m_tree))
806 : : return cmp;
807 : : break;
808 : : }
809 : :
810 : : /* TODO: We ought to find ways of sorting such cases. */
811 : : return 0;
812 : : }
813 : :
814 : : /* Return true is SNODE is the EXIT node of a function, or is one
815 : : of the final snodes within its function.
816 : :
817 : : Specifically, handle the final supernodes before the EXIT node,
818 : : for the case of clobbers that happen immediately before exiting.
819 : : We need a run of snodes leading to the return_p snode, where all edges are
820 : : intraprocedural, and every snode has just one successor.
821 : :
822 : : We use this when suppressing leak reports at the end of "main". */
823 : :
824 : : static bool
825 : 1884 : returning_from_function_p (const supernode *snode)
826 : : {
827 : 1884 : if (!snode)
828 : : return false;
829 : :
830 : : unsigned count = 0;
831 : : const supernode *iter = snode;
832 : 2956 : while (true)
833 : : {
834 : 2956 : if (iter->return_p ())
835 : : return true;
836 : 1252 : if (iter->m_succs.length () != 1)
837 : : return false;
838 : 1076 : const superedge *sedge = iter->m_succs[0];
839 : 1076 : if (sedge->get_kind () != SUPEREDGE_CFG_EDGE)
840 : : return false;
841 : 1072 : iter = sedge->m_dest;
842 : :
843 : : /* Impose a limit to ensure we terminate for pathological cases.
844 : :
845 : : We only care about the final 3 nodes, due to cases like:
846 : : BB:
847 : : (clobber causing leak)
848 : :
849 : : BB:
850 : : <label>:
851 : : return _val;
852 : :
853 : : EXIT BB.*/
854 : 1072 : if (++count > 3)
855 : : return false;
856 : : }
857 : : }
858 : :
859 : : /* Find the best tree for SVAL and call SM's on_leak vfunc with it.
860 : : If on_leak returns a pending_diagnostic, queue it up to be reported,
861 : : so that we potentially complain about a leak of SVAL in the given STATE. */
862 : :
863 : : void
864 : 1884 : impl_region_model_context::on_state_leak (const state_machine &sm,
865 : : const svalue *sval,
866 : : state_machine::state_t state)
867 : : {
868 : 1884 : logger * const logger = get_logger ();
869 : 1884 : LOG_SCOPE (logger);
870 : 1884 : if (logger)
871 : : {
872 : 0 : logger->start_log_line ();
873 : 0 : logger->log_partial ("considering leak of ");
874 : 0 : sval->dump_to_pp (logger->get_printer (), true);
875 : 0 : logger->end_log_line ();
876 : : }
877 : :
878 : 1884 : if (!m_eg)
879 : : return;
880 : :
881 : : /* m_old_state also needs to be non-NULL so that the sm_ctxt can look
882 : : up the old state of SVAL. */
883 : 1884 : gcc_assert (m_old_state);
884 : :
885 : : /* SVAL has leaked within the new state: it is not used by any reachable
886 : : regions.
887 : : We need to convert it back to a tree, but since it's likely no regions
888 : : use it, we have to find the "best" tree for it in the old_state. */
889 : 1884 : svalue_set visited;
890 : 1884 : path_var leaked_pv
891 : 1884 : = m_old_state->m_region_model->get_representative_path_var (sval,
892 : : &visited,
893 : : nullptr);
894 : :
895 : : /* Strip off top-level casts */
896 : 1884 : if (leaked_pv.m_tree && TREE_CODE (leaked_pv.m_tree) == NOP_EXPR)
897 : 118 : leaked_pv.m_tree = TREE_OPERAND (leaked_pv.m_tree, 0);
898 : :
899 : : /* This might be NULL; the pending_diagnostic subclasses need to cope
900 : : with this. */
901 : 1884 : tree leaked_tree = leaked_pv.m_tree;
902 : 1884 : if (logger)
903 : : {
904 : 0 : if (leaked_tree)
905 : 0 : logger->log ("best leaked_tree: %qE", leaked_tree);
906 : : else
907 : 0 : logger->log ("best leaked_tree: NULL");
908 : : }
909 : :
910 : 1884 : leak_stmt_finder stmt_finder (*m_eg, leaked_tree);
911 : 1884 : gcc_assert (m_enode_for_diag);
912 : :
913 : : /* Don't complain about leaks when returning from "main". */
914 : 1884 : if (returning_from_function_p (m_enode_for_diag->get_supernode ()))
915 : : {
916 : 1708 : tree fndecl = m_enode_for_diag->get_function ()->decl;
917 : 1708 : if (id_equal (DECL_NAME (fndecl), "main"))
918 : : {
919 : 66 : if (logger)
920 : 0 : logger->log ("not reporting leak from main");
921 : 66 : return;
922 : : }
923 : : }
924 : :
925 : 1818 : tree leaked_tree_for_diag = fixup_tree_for_diagnostic (leaked_tree);
926 : 1818 : std::unique_ptr<pending_diagnostic> pd = sm.on_leak (leaked_tree_for_diag,
927 : : m_old_state,
928 : 1818 : m_new_state);
929 : 1818 : if (pd)
930 : : {
931 : 1381 : pending_location ploc (m_enode_for_diag,
932 : 1381 : m_enode_for_diag->get_supernode (),
933 : : m_stmt,
934 : 1381 : &stmt_finder);
935 : 1381 : m_eg->get_diagnostic_manager ().add_diagnostic
936 : 1381 : (&sm, ploc,
937 : : leaked_tree_for_diag, sval, state, std::move (pd));
938 : : }
939 : 1884 : }
940 : :
941 : : /* Implementation of region_model_context::on_condition vfunc.
942 : : Notify all state machines about the condition, which could lead to
943 : : state transitions. */
944 : :
945 : : void
946 : 30449 : impl_region_model_context::on_condition (const svalue *lhs,
947 : : enum tree_code op,
948 : : const svalue *rhs)
949 : : {
950 : 30449 : int sm_idx;
951 : 30449 : sm_state_map *smap;
952 : 243382 : FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
953 : : {
954 : 212933 : const state_machine &sm = m_ext_state.get_sm (sm_idx);
955 : 425866 : impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
956 : : m_old_state, m_new_state,
957 : 425866 : m_old_state->m_checker_states[sm_idx],
958 : 425866 : m_new_state->m_checker_states[sm_idx],
959 : 212933 : m_path_ctxt);
960 : 212933 : sm.on_condition (sm_ctxt,
961 : 212933 : (m_enode_for_diag
962 : 212933 : ? m_enode_for_diag->get_supernode ()
963 : : : nullptr),
964 : : m_stmt,
965 : : lhs, op, rhs);
966 : 212933 : }
967 : 30449 : }
968 : :
969 : : /* Implementation of region_model_context::on_bounded_ranges vfunc.
970 : : Notify all state machines about the ranges, which could lead to
971 : : state transitions. */
972 : :
973 : : void
974 : 6236 : impl_region_model_context::on_bounded_ranges (const svalue &sval,
975 : : const bounded_ranges &ranges)
976 : : {
977 : 6236 : int sm_idx;
978 : 6236 : sm_state_map *smap;
979 : 49888 : FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
980 : : {
981 : 43652 : const state_machine &sm = m_ext_state.get_sm (sm_idx);
982 : 87304 : impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
983 : : m_old_state, m_new_state,
984 : 87304 : m_old_state->m_checker_states[sm_idx],
985 : 87304 : m_new_state->m_checker_states[sm_idx],
986 : 43652 : m_path_ctxt);
987 : 43652 : sm.on_bounded_ranges (sm_ctxt,
988 : 43652 : (m_enode_for_diag
989 : 43652 : ? m_enode_for_diag->get_supernode ()
990 : : : nullptr),
991 : : m_stmt, sval, ranges);
992 : 43652 : }
993 : 6236 : }
994 : :
995 : : /* Implementation of region_model_context::on_pop_frame vfunc.
996 : : Notify all state machines about the frame being popped, which
997 : : could lead to states being discarded. */
998 : :
999 : : void
1000 : 23680 : impl_region_model_context::on_pop_frame (const frame_region *frame_reg)
1001 : : {
1002 : 23680 : int sm_idx;
1003 : 23680 : sm_state_map *smap;
1004 : 189072 : FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
1005 : : {
1006 : 165392 : const state_machine &sm = m_ext_state.get_sm (sm_idx);
1007 : 165392 : sm.on_pop_frame (smap, frame_reg);
1008 : : }
1009 : 23680 : }
1010 : :
1011 : : /* Implementation of region_model_context::on_phi vfunc.
1012 : : Notify all state machines about the phi, which could lead to
1013 : : state transitions. */
1014 : :
1015 : : void
1016 : 23972 : impl_region_model_context::on_phi (const gphi *phi, tree rhs)
1017 : : {
1018 : 23972 : int sm_idx;
1019 : 23972 : sm_state_map *smap;
1020 : 191776 : FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
1021 : : {
1022 : 167804 : const state_machine &sm = m_ext_state.get_sm (sm_idx);
1023 : 335608 : impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
1024 : : m_old_state, m_new_state,
1025 : 335608 : m_old_state->m_checker_states[sm_idx],
1026 : 335608 : m_new_state->m_checker_states[sm_idx],
1027 : 167804 : m_path_ctxt);
1028 : 167804 : sm.on_phi (sm_ctxt, m_enode_for_diag->get_supernode (), phi, rhs);
1029 : 167804 : }
1030 : 23972 : }
1031 : :
1032 : : /* Implementation of region_model_context::on_unexpected_tree_code vfunc.
1033 : : Mark the new state as being invalid for further exploration.
1034 : : TODO(stage1): introduce a warning for when this occurs. */
1035 : :
1036 : : void
1037 : 54 : impl_region_model_context::on_unexpected_tree_code (tree t,
1038 : : const dump_location_t &loc)
1039 : : {
1040 : 54 : logger * const logger = get_logger ();
1041 : 54 : if (logger)
1042 : 0 : logger->log ("unhandled tree code: %qs in %qs at %s:%i",
1043 : 0 : get_tree_code_name (TREE_CODE (t)),
1044 : 0 : loc.get_impl_location ().m_function,
1045 : 0 : loc.get_impl_location ().m_file,
1046 : 0 : loc.get_impl_location ().m_line);
1047 : 54 : if (m_new_state)
1048 : 54 : m_new_state->m_valid = false;
1049 : 54 : }
1050 : :
1051 : : /* Implementation of region_model_context::maybe_did_work vfunc.
1052 : : Mark that "externally visible work" has occurred, and thus we
1053 : : shouldn't report an infinite loop here. */
1054 : :
1055 : : void
1056 : 28601 : impl_region_model_context::maybe_did_work ()
1057 : : {
1058 : 28601 : if (m_out_could_have_done_work)
1059 : 27185 : *m_out_could_have_done_work = true;
1060 : 28601 : }
1061 : :
1062 : : /* struct point_and_state. */
1063 : :
1064 : : /* Assert that this object is sane. */
1065 : :
1066 : : void
1067 : 783226 : point_and_state::validate (const extrinsic_state &ext_state) const
1068 : : {
1069 : : /* Skip this in a release build. */
1070 : : #if !CHECKING_P
1071 : : return;
1072 : : #endif
1073 : :
1074 : 783226 : m_point.validate ();
1075 : :
1076 : 783226 : m_state.validate (ext_state);
1077 : :
1078 : : /* Verify that the callstring's model of the stack corresponds to that
1079 : : of the region_model. */
1080 : : /* They should have the same depth. */
1081 : 1559810 : gcc_assert (m_point.get_stack_depth ()
1082 : : == m_state.m_region_model->get_stack_depth ());
1083 : : /* Check the functions in the callstring vs those in the frames
1084 : : at each depth. */
1085 : 1229026 : for (const frame_region *iter_frame
1086 : 783226 : = m_state.m_region_model->get_current_frame ();
1087 : 2012252 : iter_frame; iter_frame = iter_frame->get_calling_frame ())
1088 : : {
1089 : 1229026 : int index = iter_frame->get_index ();
1090 : 1229026 : gcc_assert (m_point.get_function_at_depth (index)
1091 : : == &iter_frame->get_function ());
1092 : : }
1093 : 783226 : }
1094 : :
1095 : : /* Subroutine of print_enode_indices: print a run of indices from START_IDX
1096 : : to END_IDX to PP, using and updating *FIRST_RUN. */
1097 : :
1098 : : static void
1099 : 8947 : print_run (pretty_printer *pp, int start_idx, int end_idx,
1100 : : bool *first_run)
1101 : : {
1102 : 8947 : if (!(*first_run))
1103 : 6134 : pp_string (pp, ", ");
1104 : 8947 : *first_run = false;
1105 : 8947 : if (start_idx == end_idx)
1106 : 6010 : pp_printf (pp, "EN: %i", start_idx);
1107 : : else
1108 : 2937 : pp_printf (pp, "EN: %i-%i", start_idx, end_idx);
1109 : 8947 : }
1110 : :
1111 : : /* Print the indices within ENODES to PP, collecting them as
1112 : : runs/singletons e.g. "EN: 4-7, EN: 20-23, EN: 42". */
1113 : :
1114 : : static void
1115 : 2825 : print_enode_indices (pretty_printer *pp,
1116 : : const auto_vec<exploded_node *> &enodes)
1117 : : {
1118 : 2825 : int cur_start_idx = -1;
1119 : 2825 : int cur_finish_idx = -1;
1120 : 2825 : bool first_run = true;
1121 : 2825 : unsigned i;
1122 : 2825 : exploded_node *enode;
1123 : 20746 : FOR_EACH_VEC_ELT (enodes, i, enode)
1124 : : {
1125 : 17921 : if (cur_start_idx == -1)
1126 : : {
1127 : 2813 : gcc_assert (cur_finish_idx == -1);
1128 : 2813 : cur_start_idx = cur_finish_idx = enode->m_index;
1129 : : }
1130 : : else
1131 : : {
1132 : 15108 : if (enode->m_index == cur_finish_idx + 1)
1133 : : /* Continuation of a run. */
1134 : : cur_finish_idx = enode->m_index;
1135 : : else
1136 : : {
1137 : : /* Finish existing run, start a new one. */
1138 : 6134 : gcc_assert (cur_start_idx >= 0);
1139 : 6134 : gcc_assert (cur_finish_idx >= 0);
1140 : 6134 : print_run (pp, cur_start_idx, cur_finish_idx,
1141 : : &first_run);
1142 : 6134 : cur_start_idx = cur_finish_idx = enode->m_index;
1143 : : }
1144 : : }
1145 : : }
1146 : : /* Finish any existing run. */
1147 : 2825 : if (cur_start_idx >= 0)
1148 : : {
1149 : 2813 : gcc_assert (cur_finish_idx >= 0);
1150 : 2813 : print_run (pp, cur_start_idx, cur_finish_idx,
1151 : : &first_run);
1152 : : }
1153 : 2825 : }
1154 : :
1155 : : /* struct eg_traits::dump_args_t. */
1156 : :
1157 : : /* The <FILENAME>.eg.dot output can quickly become unwieldy if we show
1158 : : full details for all enodes (both in terms of CPU time to render it,
1159 : : and in terms of being meaningful to a human viewing it).
1160 : :
1161 : : If we show just the IDs then the resulting graph is usually viewable,
1162 : : but then we have to keep switching back and forth between the .dot
1163 : : view and other dumps.
1164 : :
1165 : : This function implements a heuristic for showing detail at the enodes
1166 : : that (we hope) matter, and just the ID at other enodes, fixing the CPU
1167 : : usage of the .dot viewer, and drawing the attention of the viewer
1168 : : to these enodes.
1169 : :
1170 : : Return true if ENODE should be shown in detail in .dot output.
1171 : : Return false if no detail should be shown for ENODE. */
1172 : :
1173 : : bool
1174 : 624 : eg_traits::dump_args_t::show_enode_details_p (const exploded_node &enode) const
1175 : : {
1176 : : /* If the number of exploded nodes isn't too large, we may as well show
1177 : : all enodes in full detail in the .dot output. */
1178 : 624 : if (m_eg.m_nodes.length ()
1179 : 624 : <= (unsigned) param_analyzer_max_enodes_for_full_dump)
1180 : : return true;
1181 : :
1182 : : /* Otherwise, assume that what's most interesting are state explosions,
1183 : : and thus the places where this happened.
1184 : : Expand enodes at program points where we hit the per-enode limit, so we
1185 : : can investigate what exploded. */
1186 : 0 : const per_program_point_data *per_point_data
1187 : 0 : = m_eg.get_per_program_point_data (enode.get_point ());
1188 : 0 : return per_point_data->m_excess_enodes > 0;
1189 : : }
1190 : :
1191 : : /* class exploded_node : public dnode<eg_traits>. */
1192 : :
1193 : : const char *
1194 : 0 : exploded_node::status_to_str (enum status s)
1195 : : {
1196 : 0 : switch (s)
1197 : : {
1198 : 0 : default: gcc_unreachable ();
1199 : : case status::worklist: return "worklist";
1200 : 0 : case status::processed: return "processed";
1201 : 0 : case status::merger: return "merger";
1202 : 0 : case status::bulk_merged: return "bulk_merged";
1203 : : }
1204 : : }
1205 : :
1206 : : /* exploded_node's ctor. */
1207 : :
1208 : 388301 : exploded_node::exploded_node (const point_and_state &ps,
1209 : 388301 : int index)
1210 : 388301 : : m_ps (ps), m_status (status::worklist), m_index (index),
1211 : 388301 : m_num_processed_stmts (0)
1212 : : {
1213 : 388301 : gcc_checking_assert (ps.get_state ().m_region_model->canonicalized_p ());
1214 : 388301 : }
1215 : :
1216 : : /* Get the stmt that was processed in this enode at index IDX.
1217 : : IDX is an index within the stmts processed at this enode, rather
1218 : : than within those of the supernode. */
1219 : :
1220 : : const gimple *
1221 : 118157 : exploded_node::get_processed_stmt (unsigned idx) const
1222 : : {
1223 : 118157 : gcc_assert (idx < m_num_processed_stmts);
1224 : 118157 : const program_point &point = get_point ();
1225 : 118157 : gcc_assert (point.get_kind () == PK_BEFORE_STMT);
1226 : 118157 : const supernode *snode = get_supernode ();
1227 : 118157 : const unsigned int point_stmt_idx = point.get_stmt_idx ();
1228 : 118157 : const unsigned int idx_within_snode = point_stmt_idx + idx;
1229 : 118157 : const gimple *stmt = snode->m_stmts[idx_within_snode];
1230 : 118157 : return stmt;
1231 : : }
1232 : :
1233 : : /* For use by dump_dot, get a value for the .dot "fillcolor" attribute.
1234 : : Colorize by sm-state, to make it easier to see how sm-state propagates
1235 : : through the exploded_graph. */
1236 : :
1237 : : const char *
1238 : 1255 : exploded_node::get_dot_fillcolor () const
1239 : : {
1240 : 1255 : const program_state &state = get_state ();
1241 : :
1242 : : /* We want to be able to easily distinguish the no-sm-state case,
1243 : : and to be able to distinguish cases where there's a single state
1244 : : from each other.
1245 : :
1246 : : Sum the sm_states, and use the result to choose from a table,
1247 : : modulo table-size, special-casing the "no sm-state" case. */
1248 : 1255 : int total_sm_state = 0;
1249 : 1255 : int i;
1250 : 1255 : sm_state_map *smap;
1251 : 10040 : FOR_EACH_VEC_ELT (state.m_checker_states, i, smap)
1252 : : {
1253 : 10109 : for (sm_state_map::iterator_t iter = smap->begin ();
1254 : 10109 : iter != smap->end ();
1255 : 1324 : ++iter)
1256 : 1324 : total_sm_state += (*iter).second.m_state->get_id ();
1257 : 8785 : total_sm_state += smap->get_global_state ()->get_id ();
1258 : : }
1259 : :
1260 : 1255 : if (total_sm_state > 0)
1261 : : {
1262 : : /* An arbitrarily-picked collection of light colors. */
1263 : 792 : const char * const colors[]
1264 : : = {"azure", "coral", "cornsilk", "lightblue", "yellow",
1265 : : "honeydew", "lightpink", "lightsalmon", "palegreen1",
1266 : : "wheat", "seashell"};
1267 : 792 : const int num_colors = ARRAY_SIZE (colors);
1268 : 792 : return colors[total_sm_state % num_colors];
1269 : : }
1270 : : else
1271 : : /* No sm-state. */
1272 : : return "lightgrey";
1273 : : }
1274 : :
1275 : : /* Implementation of dnode::dump_dot vfunc for exploded_node. */
1276 : :
1277 : : void
1278 : 624 : exploded_node::dump_dot (graphviz_out *gv, const dump_args_t &args) const
1279 : : {
1280 : 624 : pretty_printer *pp = gv->get_pp ();
1281 : :
1282 : 624 : dump_dot_id (pp);
1283 : 624 : pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
1284 : : get_dot_fillcolor ());
1285 : 624 : pp_write_text_to_stream (pp);
1286 : :
1287 : 624 : pp_printf (pp, "EN: %i", m_index);
1288 : 624 : if (m_status == status::merger)
1289 : 0 : pp_string (pp, " (merger)");
1290 : 624 : else if (m_status == status::bulk_merged)
1291 : 40 : pp_string (pp, " (bulk merged)");
1292 : 624 : pp_newline (pp);
1293 : :
1294 : 624 : if (args.show_enode_details_p (*this))
1295 : : {
1296 : 624 : format f (true);
1297 : 624 : m_ps.get_point ().print (pp, f);
1298 : 624 : pp_newline (pp);
1299 : :
1300 : 624 : const extrinsic_state &ext_state = args.m_eg.get_ext_state ();
1301 : 624 : const program_state &state = m_ps.get_state ();
1302 : 624 : state.dump_to_pp (ext_state, false, true, pp);
1303 : 624 : pp_newline (pp);
1304 : :
1305 : 624 : dump_processed_stmts (pp);
1306 : : }
1307 : :
1308 : 624 : dump_saved_diagnostics (pp);
1309 : :
1310 : 624 : args.dump_extra_info (this, pp);
1311 : :
1312 : 624 : pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
1313 : :
1314 : 624 : pp_string (pp, "\"];\n\n");
1315 : :
1316 : : /* It can be hard to locate the saved diagnostics as text within the
1317 : : enode nodes, so add extra nodes to the graph for each saved_diagnostic,
1318 : : highlighted in red.
1319 : : Compare with dump_saved_diagnostics. */
1320 : 624 : {
1321 : 624 : unsigned i;
1322 : 624 : const saved_diagnostic *sd;
1323 : 1268 : FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
1324 : : {
1325 : 20 : sd->dump_as_dot_node (pp);
1326 : :
1327 : : /* Add edge connecting this enode to the saved_diagnostic. */
1328 : 20 : dump_dot_id (pp);
1329 : 20 : pp_string (pp, " -> ");
1330 : 20 : sd->dump_dot_id (pp);
1331 : 20 : pp_string (pp, " [style=\"dotted\" arrowhead=\"none\"];");
1332 : 20 : pp_newline (pp);
1333 : : }
1334 : : }
1335 : :
1336 : 624 : pp_flush (pp);
1337 : 624 : }
1338 : :
1339 : : /* Show any stmts that were processed within this enode,
1340 : : and their index within the supernode. */
1341 : : void
1342 : 760 : exploded_node::dump_processed_stmts (pretty_printer *pp) const
1343 : : {
1344 : 760 : if (m_num_processed_stmts > 0)
1345 : : {
1346 : 294 : const program_point &point = get_point ();
1347 : 294 : gcc_assert (point.get_kind () == PK_BEFORE_STMT);
1348 : 294 : const supernode *snode = get_supernode ();
1349 : 294 : const unsigned int point_stmt_idx = point.get_stmt_idx ();
1350 : :
1351 : 294 : pp_printf (pp, "stmts: %i", m_num_processed_stmts);
1352 : 294 : pp_newline (pp);
1353 : 821 : for (unsigned i = 0; i < m_num_processed_stmts; i++)
1354 : : {
1355 : 527 : const unsigned int idx_within_snode = point_stmt_idx + i;
1356 : 527 : const gimple *stmt = snode->m_stmts[idx_within_snode];
1357 : 527 : pp_printf (pp, " %i: ", idx_within_snode);
1358 : 527 : pp_gimple_stmt_1 (pp, stmt, 0, (dump_flags_t)0);
1359 : 527 : pp_newline (pp);
1360 : : }
1361 : : }
1362 : 760 : }
1363 : :
1364 : : /* Dump any saved_diagnostics at this enode to PP. */
1365 : :
1366 : : void
1367 : 760 : exploded_node::dump_saved_diagnostics (pretty_printer *pp) const
1368 : : {
1369 : 760 : unsigned i;
1370 : 760 : const saved_diagnostic *sd;
1371 : 792 : FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
1372 : : {
1373 : 32 : pp_printf (pp, "DIAGNOSTIC: %s (sd: %i)",
1374 : 32 : sd->m_d->get_kind (), sd->get_index ());
1375 : 32 : pp_newline (pp);
1376 : : }
1377 : 760 : }
1378 : :
1379 : : /* Dump this to PP in a form suitable for use as an id in .dot output. */
1380 : :
1381 : : void
1382 : 1900 : exploded_node::dump_dot_id (pretty_printer *pp) const
1383 : : {
1384 : 1900 : pp_printf (pp, "exploded_node_%i", m_index);
1385 : 1900 : }
1386 : :
1387 : : /* Dump a multiline representation of this node to PP. */
1388 : :
1389 : : void
1390 : 0 : exploded_node::dump_to_pp (pretty_printer *pp,
1391 : : const extrinsic_state &ext_state) const
1392 : : {
1393 : 0 : pp_printf (pp, "EN: %i", m_index);
1394 : 0 : pp_newline (pp);
1395 : :
1396 : 0 : format f (true);
1397 : 0 : m_ps.get_point ().print (pp, f);
1398 : 0 : pp_newline (pp);
1399 : :
1400 : 0 : m_ps.get_state ().dump_to_pp (ext_state, false, true, pp);
1401 : 0 : pp_newline (pp);
1402 : 0 : }
1403 : :
1404 : : /* Dump a multiline representation of this node to FILE. */
1405 : :
1406 : : void
1407 : 0 : exploded_node::dump (FILE *fp,
1408 : : const extrinsic_state &ext_state) const
1409 : : {
1410 : 0 : tree_dump_pretty_printer pp (fp);
1411 : 0 : dump_to_pp (&pp, ext_state);
1412 : 0 : }
1413 : :
1414 : : /* Dump a multiline representation of this node to stderr. */
1415 : :
1416 : : DEBUG_FUNCTION void
1417 : 0 : exploded_node::dump (const extrinsic_state &ext_state) const
1418 : : {
1419 : 0 : dump (stderr, ext_state);
1420 : 0 : }
1421 : :
1422 : : /* Return a new json::object of the form
1423 : : {"point" : object for program_point,
1424 : : "state" : object for program_state,
1425 : : "status" : str,
1426 : : "idx" : int,
1427 : : "processed_stmts" : int}. */
1428 : :
1429 : : std::unique_ptr<json::object>
1430 : 0 : exploded_node::to_json (const extrinsic_state &ext_state) const
1431 : : {
1432 : 0 : auto enode_obj = std::make_unique<json::object> ();
1433 : :
1434 : 0 : enode_obj->set ("point", get_point ().to_json ());
1435 : 0 : enode_obj->set ("state", get_state ().to_json (ext_state));
1436 : 0 : enode_obj->set_string ("status", status_to_str (m_status));
1437 : 0 : enode_obj->set_integer ("idx", m_index);
1438 : 0 : enode_obj->set_integer ("processed_stmts", m_num_processed_stmts);
1439 : :
1440 : 0 : return enode_obj;
1441 : : }
1442 : :
1443 : : } // namespace ana
1444 : :
1445 : : /* Return true if FNDECL has a gimple body. */
1446 : : // TODO: is there a pre-canned way to do this?
1447 : :
1448 : : bool
1449 : 29960 : fndecl_has_gimple_body_p (tree fndecl)
1450 : : {
1451 : 29960 : if (fndecl == NULL_TREE)
1452 : : return false;
1453 : :
1454 : 29960 : cgraph_node *n = cgraph_node::get (fndecl);
1455 : 29960 : if (!n)
1456 : : return false;
1457 : :
1458 : 29960 : return n->has_gimple_body_p ();
1459 : : }
1460 : :
1461 : : namespace ana {
1462 : :
1463 : : /* Modify STATE in place, applying the effects of the stmt at this node's
1464 : : point. */
1465 : :
1466 : : exploded_node::on_stmt_flags
1467 : 247552 : exploded_node::on_stmt (exploded_graph &eg,
1468 : : const supernode *snode,
1469 : : const gimple *stmt,
1470 : : program_state *state,
1471 : : uncertainty_t *uncertainty,
1472 : : bool *out_could_have_done_work,
1473 : : path_context *path_ctxt)
1474 : : {
1475 : 247552 : logger *logger = eg.get_logger ();
1476 : 247552 : LOG_SCOPE (logger);
1477 : 247552 : if (logger)
1478 : : {
1479 : 96 : logger->start_log_line ();
1480 : 96 : pp_gimple_stmt_1 (logger->get_printer (), stmt, 0, (dump_flags_t)0);
1481 : 96 : logger->end_log_line ();
1482 : : }
1483 : :
1484 : : /* Update input_location in case of ICE: make it easier to track down which
1485 : : source construct we're failing to handle. */
1486 : 247552 : input_location = stmt->location;
1487 : :
1488 : 247552 : gcc_assert (state->m_region_model);
1489 : :
1490 : : /* Preserve the old state. It is used here for looking
1491 : : up old checker states, for determining state transitions, and
1492 : : also within impl_region_model_context and impl_sm_context for
1493 : : going from tree to svalue_id. */
1494 : 247552 : const program_state old_state (*state);
1495 : :
1496 : 247552 : impl_region_model_context ctxt (eg, this,
1497 : : &old_state, state, uncertainty,
1498 : : path_ctxt, stmt, nullptr,
1499 : 247552 : out_could_have_done_work);
1500 : :
1501 : : /* Handle call summaries here. */
1502 : 247552 : if (cgraph_edge *cgedge
1503 : 247552 : = supergraph_call_edge (snode->get_function (), stmt))
1504 : 7823 : if (eg.get_analysis_plan ().use_summary_p (cgedge))
1505 : : {
1506 : 1017 : function *called_fn = get_ultimate_function_for_cgraph_edge (cgedge);
1507 : 1017 : per_function_data *called_fn_data
1508 : 1017 : = eg.get_per_function_data (called_fn);
1509 : 1017 : if (called_fn_data)
1510 : : {
1511 : 937 : gcc_assert (called_fn);
1512 : 937 : return replay_call_summaries (eg,
1513 : : snode,
1514 : 937 : *as_a <const gcall *> (stmt),
1515 : : state,
1516 : : path_ctxt,
1517 : : *called_fn,
1518 : : *called_fn_data,
1519 : 937 : &ctxt);
1520 : : }
1521 : : }
1522 : :
1523 : 246615 : bool unknown_side_effects = false;
1524 : 246615 : bool terminate_path = false;
1525 : :
1526 : 246615 : on_stmt_pre (eg, stmt, state, &terminate_path,
1527 : : &unknown_side_effects, &ctxt);
1528 : :
1529 : 246615 : if (terminate_path)
1530 : 896 : return on_stmt_flags::terminate_path ();
1531 : :
1532 : : int sm_idx;
1533 : : sm_state_map *smap;
1534 : 1964343 : FOR_EACH_VEC_ELT (old_state.m_checker_states, sm_idx, smap)
1535 : : {
1536 : 1718624 : const state_machine &sm = eg.get_ext_state ().get_sm (sm_idx);
1537 : 1718624 : const sm_state_map *old_smap
1538 : 1718624 : = old_state.m_checker_states[sm_idx];
1539 : 1718624 : sm_state_map *new_smap = state->m_checker_states[sm_idx];
1540 : 1718624 : impl_sm_context sm_ctxt (eg, sm_idx, sm, this, &old_state, state,
1541 : : old_smap, new_smap, path_ctxt, nullptr,
1542 : 1718624 : unknown_side_effects);
1543 : :
1544 : : /* Allow the state_machine to handle the stmt. */
1545 : 1718624 : if (sm.on_stmt (sm_ctxt, snode, stmt))
1546 : 23792 : unknown_side_effects = false;
1547 : 1718624 : }
1548 : :
1549 : 245719 : if (path_ctxt->terminate_path_p ())
1550 : 767 : return on_stmt_flags::terminate_path ();
1551 : :
1552 : 244952 : on_stmt_post (stmt, state, unknown_side_effects, &ctxt);
1553 : :
1554 : 244952 : return on_stmt_flags ();
1555 : 247552 : }
1556 : :
1557 : : /* Handle the pre-sm-state part of STMT, modifying STATE in-place.
1558 : : Write true to *OUT_TERMINATE_PATH if the path should be terminated.
1559 : : Write true to *OUT_UNKNOWN_SIDE_EFFECTS if the stmt has unknown
1560 : : side effects. */
1561 : :
1562 : : void
1563 : 246615 : exploded_node::on_stmt_pre (exploded_graph &eg,
1564 : : const gimple *stmt,
1565 : : program_state *state,
1566 : : bool *out_terminate_path,
1567 : : bool *out_unknown_side_effects,
1568 : : region_model_context *ctxt)
1569 : : {
1570 : : /* Handle special-case calls that require the full program_state. */
1571 : 246615 : if (const gcall *call_stmt = dyn_cast <const gcall *> (stmt))
1572 : : {
1573 : 60647 : const gcall &call = *call_stmt;
1574 : 60647 : if (is_special_named_call_p (call, "__analyzer_dump", 0))
1575 : : {
1576 : : /* Handle the builtin "__analyzer_dump" by dumping state
1577 : : to stderr. */
1578 : 0 : state->dump (eg.get_ext_state (), true);
1579 : 0 : return;
1580 : : }
1581 : 60647 : else if (is_special_named_call_p (call, "__analyzer_dump_sarif", 0))
1582 : : {
1583 : 0 : state->dump_sarif (eg.get_ext_state ());
1584 : 0 : return;
1585 : : }
1586 : 60647 : else if (is_special_named_call_p (call, "__analyzer_dump_dot", 0))
1587 : : {
1588 : 0 : state->dump_dot (eg.get_ext_state ());
1589 : 0 : return;
1590 : : }
1591 : 60647 : else if (is_special_named_call_p (call, "__analyzer_dump_state", 2))
1592 : : {
1593 : 361 : state->impl_call_analyzer_dump_state (call, eg.get_ext_state (),
1594 : : ctxt);
1595 : 361 : return;
1596 : : }
1597 : 60286 : else if (is_setjmp_call_p (call))
1598 : : {
1599 : 34 : state->m_region_model->on_setjmp (call, this, ctxt);
1600 : 34 : if (ctxt)
1601 : 34 : ctxt->maybe_did_work ();
1602 : 34 : return;
1603 : : }
1604 : 60252 : else if (is_longjmp_call_p (call))
1605 : : {
1606 : 63 : on_longjmp (eg, call, state, ctxt);
1607 : 63 : *out_terminate_path = true;
1608 : 63 : if (ctxt)
1609 : 63 : ctxt->maybe_did_work ();
1610 : 63 : return;
1611 : : }
1612 : 60189 : else if (is_cxa_throw_p (call))
1613 : : {
1614 : 106 : on_throw (eg, call, state, false, ctxt);
1615 : 106 : *out_terminate_path = true;
1616 : 106 : return;
1617 : : }
1618 : 60083 : else if (is_cxa_rethrow_p (call))
1619 : : {
1620 : 90 : on_throw (eg, call, state, true, ctxt);
1621 : 90 : *out_terminate_path = true;
1622 : 90 : return;
1623 : : }
1624 : : }
1625 : 185968 : else if (const gresx *resx = dyn_cast <const gresx *> (stmt))
1626 : : {
1627 : 637 : on_resx (eg, *resx, state, ctxt);
1628 : 637 : *out_terminate_path = true;
1629 : 637 : return;
1630 : : }
1631 : :
1632 : : /* Otherwise, defer to m_region_model. */
1633 : 245324 : state->m_region_model->on_stmt_pre (stmt,
1634 : : out_unknown_side_effects,
1635 : : ctxt);
1636 : : }
1637 : :
1638 : : /* Handle the post-sm-state part of STMT, modifying STATE in-place. */
1639 : :
1640 : : void
1641 : 244952 : exploded_node::on_stmt_post (const gimple *stmt,
1642 : : program_state *state,
1643 : : bool unknown_side_effects,
1644 : : region_model_context *ctxt)
1645 : : {
1646 : 244952 : if (const gcall *call = dyn_cast <const gcall *> (stmt))
1647 : 60098 : state->m_region_model->on_call_post (*call, unknown_side_effects, ctxt);
1648 : 244952 : }
1649 : :
1650 : : /* A concrete call_info subclass representing a replay of a call summary. */
1651 : :
1652 : : class call_summary_edge_info : public call_info
1653 : : {
1654 : : public:
1655 : 2207 : call_summary_edge_info (const call_details &cd,
1656 : : const function &called_fn,
1657 : : call_summary &summary,
1658 : : const extrinsic_state &ext_state)
1659 : 2207 : : call_info (cd, called_fn),
1660 : 2207 : m_called_fn (called_fn),
1661 : 2207 : m_summary (summary),
1662 : 2207 : m_ext_state (ext_state)
1663 : : {}
1664 : :
1665 : 1416 : bool update_state (program_state *state,
1666 : : const exploded_edge *,
1667 : : region_model_context *ctxt) const final override
1668 : : {
1669 : : /* Update STATE based on summary_end_state. */
1670 : 1416 : call_details cd (get_call_details (state->m_region_model, ctxt));
1671 : 1416 : call_summary_replay r (cd, m_called_fn, m_summary, m_ext_state);
1672 : 1416 : const program_state &summary_end_state = m_summary.get_state ();
1673 : 1416 : return state->replay_call_summary (r, summary_end_state);
1674 : 1416 : }
1675 : :
1676 : 112 : bool update_model (region_model *model,
1677 : : const exploded_edge *,
1678 : : region_model_context *ctxt) const final override
1679 : : {
1680 : : /* Update STATE based on summary_end_state. */
1681 : 112 : call_details cd (get_call_details (model, ctxt));
1682 : 112 : call_summary_replay r (cd, m_called_fn, m_summary, m_ext_state);
1683 : 112 : const program_state &summary_end_state = m_summary.get_state ();
1684 : 112 : model->replay_call_summary (r, *summary_end_state.m_region_model);
1685 : 112 : return true;
1686 : 112 : }
1687 : :
1688 : 94 : void print_desc (pretty_printer &pp) const final override
1689 : : {
1690 : 94 : pp_string (&pp, m_summary.get_desc ().get ());
1691 : 94 : }
1692 : :
1693 : : private:
1694 : : const function &m_called_fn;
1695 : : call_summary &m_summary;
1696 : : const extrinsic_state &m_ext_state;
1697 : : };
1698 : :
1699 : : /* Use PATH_CTXT to bifurcate, which when handled will add custom edges
1700 : : for a replay of the various feasible summaries in CALLED_FN_DATA. */
1701 : :
1702 : : exploded_node::on_stmt_flags
1703 : 937 : exploded_node::replay_call_summaries (exploded_graph &eg,
1704 : : const supernode *snode,
1705 : : const gcall &call_stmt,
1706 : : program_state *state,
1707 : : path_context *path_ctxt,
1708 : : const function &called_fn,
1709 : : per_function_data &called_fn_data,
1710 : : region_model_context *ctxt)
1711 : : {
1712 : 937 : logger *logger = eg.get_logger ();
1713 : 937 : LOG_SCOPE (logger);
1714 : :
1715 : : /* Each summary will call bifurcate on the PATH_CTXT. */
1716 : 5018 : for (auto summary : called_fn_data.m_summaries)
1717 : : {
1718 : 2207 : gcc_assert (summary);
1719 : 2207 : replay_call_summary (eg, snode, call_stmt, state,
1720 : : path_ctxt, called_fn, *summary, ctxt);
1721 : : }
1722 : 937 : path_ctxt->terminate_path ();
1723 : :
1724 : 937 : return on_stmt_flags ();
1725 : 937 : }
1726 : :
1727 : : /* Use PATH_CTXT to bifurcate, which when handled will add a
1728 : : custom edge for a replay of SUMMARY, if the summary's
1729 : : conditions are feasible based on the current state. */
1730 : :
1731 : : void
1732 : 2207 : exploded_node::replay_call_summary (exploded_graph &eg,
1733 : : const supernode *snode,
1734 : : const gcall &call_stmt,
1735 : : program_state *old_state,
1736 : : path_context *path_ctxt,
1737 : : const function &called_fn,
1738 : : call_summary &summary,
1739 : : region_model_context *ctxt)
1740 : : {
1741 : 2207 : logger *logger = eg.get_logger ();
1742 : 2207 : LOG_SCOPE (logger);
1743 : 2207 : gcc_assert (snode);
1744 : 2207 : gcc_assert (old_state);
1745 : :
1746 : 2207 : if (logger)
1747 : 0 : logger->log ("using %s as summary for call to %qE from %qE",
1748 : 0 : summary.get_desc ().get (),
1749 : 0 : called_fn.decl,
1750 : 0 : snode->get_function ()->decl);
1751 : 2207 : const extrinsic_state &ext_state = eg.get_ext_state ();
1752 : 2207 : const program_state &summary_end_state = summary.get_state ();
1753 : 2207 : if (logger)
1754 : : {
1755 : 0 : pretty_printer *pp = logger->get_printer ();
1756 : :
1757 : 0 : logger->start_log_line ();
1758 : 0 : pp_string (pp, "callsite state: ");
1759 : 0 : old_state->dump_to_pp (ext_state, true, false, pp);
1760 : 0 : logger->end_log_line ();
1761 : :
1762 : 0 : logger->start_log_line ();
1763 : 0 : pp_string (pp, "summary end state: ");
1764 : 0 : summary_end_state.dump_to_pp (ext_state, true, false, pp);
1765 : 0 : logger->end_log_line ();
1766 : : }
1767 : :
1768 : 2207 : program_state new_state (*old_state);
1769 : :
1770 : 2207 : call_details cd (call_stmt, new_state.m_region_model, ctxt);
1771 : 2207 : call_summary_replay r (cd, called_fn, summary, ext_state);
1772 : :
1773 : 2207 : if (path_ctxt)
1774 : 2207 : path_ctxt->bifurcate
1775 : 2207 : (std::make_unique<call_summary_edge_info> (cd,
1776 : : called_fn,
1777 : : summary,
1778 : : ext_state));
1779 : 2207 : }
1780 : :
1781 : :
1782 : : /* Consider the effect of following superedge SUCC from this node.
1783 : :
1784 : : Return true if it's feasible to follow the edge, or false
1785 : : if it's infeasible.
1786 : :
1787 : : Examples: if it's the "true" branch within
1788 : : a CFG and we know the conditional is false, we know it's infeasible.
1789 : : If it's one of multiple interprocedual "return" edges, then only
1790 : : the edge back to the most recent callsite is feasible.
1791 : :
1792 : : Update NEXT_STATE accordingly (e.g. to record that a condition was
1793 : : true or false, or that the NULL-ness of a pointer has been checked,
1794 : : pushing/popping stack frames, etc).
1795 : :
1796 : : Update NEXT_POINT accordingly (updating the call string). */
1797 : :
1798 : : bool
1799 : 151062 : exploded_node::on_edge (exploded_graph &eg,
1800 : : const superedge *succ,
1801 : : program_point *next_point,
1802 : : program_state *next_state,
1803 : : uncertainty_t *uncertainty)
1804 : : {
1805 : 151062 : LOG_FUNC (eg.get_logger ());
1806 : :
1807 : 151062 : if (!next_point->on_edge (eg, succ))
1808 : : return false;
1809 : :
1810 : 126376 : if (!next_state->on_edge (eg, this, succ, uncertainty))
1811 : : return false;
1812 : :
1813 : : return true;
1814 : 151062 : }
1815 : :
1816 : : /* Verify that the stack at LONGJMP_POINT is still valid, given a call
1817 : : to "setjmp" at SETJMP_POINT - the stack frame that "setjmp" was
1818 : : called in must still be valid.
1819 : :
1820 : : Caveat: this merely checks the call_strings in the points; it doesn't
1821 : : detect the case where a frame returns and is then called again. */
1822 : :
1823 : : static bool
1824 : 76 : valid_longjmp_stack_p (const program_point &longjmp_point,
1825 : : const program_point &setjmp_point)
1826 : : {
1827 : 76 : const call_string &cs_at_longjmp = longjmp_point.get_call_string ();
1828 : 76 : const call_string &cs_at_setjmp = setjmp_point.get_call_string ();
1829 : :
1830 : 182 : if (cs_at_longjmp.length () < cs_at_setjmp.length ())
1831 : : return false;
1832 : :
1833 : : /* Check that the call strings match, up to the depth of the
1834 : : setjmp point. */
1835 : 90 : for (unsigned depth = 0; depth < cs_at_setjmp.length (); depth++)
1836 : 35 : if (cs_at_longjmp[depth] != cs_at_setjmp[depth])
1837 : : return false;
1838 : :
1839 : : return true;
1840 : : }
1841 : :
1842 : : /* A pending_diagnostic subclass for complaining about bad longjmps,
1843 : : where the enclosing function of the "setjmp" has returned (and thus
1844 : : the stack frame no longer exists). */
1845 : :
1846 : : class stale_jmp_buf : public pending_diagnostic_subclass<stale_jmp_buf>
1847 : : {
1848 : : public:
1849 : 5 : stale_jmp_buf (const gcall &setjmp_call, const gcall &longjmp_call,
1850 : : const program_point &setjmp_point)
1851 : 5 : : m_setjmp_call (setjmp_call), m_longjmp_call (longjmp_call),
1852 : 5 : m_setjmp_point (setjmp_point), m_stack_pop_event (nullptr)
1853 : : {}
1854 : :
1855 : 10 : int get_controlling_option () const final override
1856 : : {
1857 : 10 : return OPT_Wanalyzer_stale_setjmp_buffer;
1858 : : }
1859 : :
1860 : 5 : bool emit (diagnostic_emission_context &ctxt) final override
1861 : : {
1862 : 5 : return ctxt.warn ("%qs called after enclosing function of %qs has returned",
1863 : : get_user_facing_name (m_longjmp_call),
1864 : 5 : get_user_facing_name (m_setjmp_call));
1865 : : }
1866 : :
1867 : 25 : const char *get_kind () const final override
1868 : 25 : { return "stale_jmp_buf"; }
1869 : :
1870 : 5 : bool operator== (const stale_jmp_buf &other) const
1871 : : {
1872 : 5 : return (&m_setjmp_call == &other.m_setjmp_call
1873 : 5 : && &m_longjmp_call == &other.m_longjmp_call);
1874 : : }
1875 : :
1876 : : bool
1877 : 31 : maybe_add_custom_events_for_superedge (const exploded_edge &eedge,
1878 : : checker_path *emission_path)
1879 : : final override
1880 : : {
1881 : : /* Detect exactly when the stack first becomes invalid,
1882 : : and issue an event then. */
1883 : 31 : if (m_stack_pop_event)
1884 : : return false;
1885 : 31 : const exploded_node *src_node = eedge.m_src;
1886 : 31 : const program_point &src_point = src_node->get_point ();
1887 : 31 : const exploded_node *dst_node = eedge.m_dest;
1888 : 31 : const program_point &dst_point = dst_node->get_point ();
1889 : 31 : if (valid_longjmp_stack_p (src_point, m_setjmp_point)
1890 : 31 : && !valid_longjmp_stack_p (dst_point, m_setjmp_point))
1891 : : {
1892 : : /* Compare with diagnostic_manager::add_events_for_superedge. */
1893 : 5 : const int src_stack_depth = src_point.get_stack_depth ();
1894 : 10 : m_stack_pop_event = new precanned_custom_event
1895 : 5 : (event_loc_info (src_point.get_location (),
1896 : : src_point.get_fndecl (),
1897 : 5 : src_stack_depth),
1898 : 10 : "stack frame is popped here, invalidating saved environment");
1899 : 5 : emission_path->add_event
1900 : 5 : (std::unique_ptr<custom_event> (m_stack_pop_event));
1901 : 5 : return false;
1902 : : }
1903 : : return false;
1904 : : }
1905 : :
1906 : : bool
1907 : 10 : describe_final_event (pretty_printer &pp,
1908 : : const evdesc::final_event &) final override
1909 : : {
1910 : 10 : if (m_stack_pop_event)
1911 : 10 : pp_printf (&pp,
1912 : : "%qs called after enclosing function of %qs returned at %@",
1913 : : get_user_facing_name (m_longjmp_call),
1914 : : get_user_facing_name (m_setjmp_call),
1915 : : m_stack_pop_event->get_id_ptr ());
1916 : : else
1917 : 0 : pp_printf (&pp,
1918 : : "%qs called after enclosing function of %qs has returned",
1919 : : get_user_facing_name (m_longjmp_call),
1920 : : get_user_facing_name (m_setjmp_call));
1921 : 10 : return true;
1922 : : }
1923 : :
1924 : :
1925 : : private:
1926 : : const gcall &m_setjmp_call;
1927 : : const gcall &m_longjmp_call;
1928 : : program_point m_setjmp_point;
1929 : : custom_event *m_stack_pop_event;
1930 : : };
1931 : :
1932 : : /* Handle LONGJMP_CALL, a call to longjmp or siglongjmp.
1933 : :
1934 : : Attempt to locate where setjmp/sigsetjmp was called on the jmp_buf and build
1935 : : an exploded_node and exploded_edge to it representing a rewind to that frame,
1936 : : handling the various kinds of failure that can occur. */
1937 : :
1938 : : void
1939 : 63 : exploded_node::on_longjmp (exploded_graph &eg,
1940 : : const gcall &longjmp_call,
1941 : : program_state *new_state,
1942 : : region_model_context *ctxt)
1943 : : {
1944 : 63 : tree buf_ptr = gimple_call_arg (&longjmp_call, 0);
1945 : 63 : gcc_assert (POINTER_TYPE_P (TREE_TYPE (buf_ptr)));
1946 : :
1947 : 63 : region_model *new_region_model = new_state->m_region_model;
1948 : 63 : const svalue *buf_ptr_sval = new_region_model->get_rvalue (buf_ptr, ctxt);
1949 : 63 : const region *buf = new_region_model->deref_rvalue (buf_ptr_sval, buf_ptr,
1950 : : ctxt);
1951 : :
1952 : 63 : const svalue *buf_content_sval
1953 : 63 : = new_region_model->get_store_value (buf, ctxt);
1954 : 63 : const setjmp_svalue *setjmp_sval
1955 : 63 : = buf_content_sval->dyn_cast_setjmp_svalue ();
1956 : 63 : if (!setjmp_sval)
1957 : 43 : return;
1958 : :
1959 : 25 : const setjmp_record tmp_setjmp_record = setjmp_sval->get_setjmp_record ();
1960 : :
1961 : : /* Build a custom enode and eedge for rewinding from the longjmp/siglongjmp
1962 : : call back to the setjmp/sigsetjmp. */
1963 : 25 : rewind_info_t rewind_info (tmp_setjmp_record, longjmp_call);
1964 : :
1965 : 25 : const gcall &setjmp_call = rewind_info.get_setjmp_call ();
1966 : 25 : const program_point &setjmp_point = rewind_info.get_setjmp_point ();
1967 : :
1968 : 25 : const program_point &longjmp_point = get_point ();
1969 : :
1970 : : /* Verify that the setjmp's call_stack hasn't been popped. */
1971 : 25 : if (!valid_longjmp_stack_p (longjmp_point, setjmp_point))
1972 : : {
1973 : 5 : ctxt->warn (std::make_unique<stale_jmp_buf> (setjmp_call,
1974 : : longjmp_call,
1975 : : setjmp_point));
1976 : 5 : return;
1977 : : }
1978 : :
1979 : 60 : gcc_assert (longjmp_point.get_stack_depth ()
1980 : : >= setjmp_point.get_stack_depth ());
1981 : :
1982 : : /* Update the state for use by the destination node. */
1983 : :
1984 : : /* Stash the current number of diagnostics so that we can update
1985 : : any that this adds to show where the longjmp is rewinding to. */
1986 : :
1987 : 20 : diagnostic_manager *dm = &eg.get_diagnostic_manager ();
1988 : 20 : unsigned prev_num_diagnostics = dm->get_num_diagnostics ();
1989 : :
1990 : 40 : new_region_model->on_longjmp (longjmp_call, setjmp_call,
1991 : : setjmp_point.get_stack_depth (), ctxt);
1992 : :
1993 : : /* Detect leaks in the new state relative to the old state. */
1994 : 20 : program_state::detect_leaks (get_state (), *new_state, nullptr,
1995 : : eg.get_ext_state (), ctxt);
1996 : :
1997 : 20 : program_point next_point
1998 : 20 : = program_point::after_supernode (setjmp_point.get_supernode (),
1999 : : setjmp_point.get_call_string ());
2000 : :
2001 : 20 : exploded_node *next
2002 : 20 : = eg.get_or_create_node (next_point, *new_state, this);
2003 : :
2004 : : /* Create custom exploded_edge for a longjmp. */
2005 : 20 : if (next)
2006 : : {
2007 : 20 : exploded_edge *eedge
2008 : 20 : = eg.add_edge (const_cast<exploded_node *> (this), next, nullptr, true,
2009 : 20 : std::make_unique<rewind_info_t> (tmp_setjmp_record,
2010 : : longjmp_call));
2011 : :
2012 : : /* For any diagnostics that were queued here (such as leaks) we want
2013 : : the checker_path to show the rewinding events after the "final event"
2014 : : so that the user sees where the longjmp is rewinding to (otherwise the
2015 : : path is meaningless).
2016 : :
2017 : : For example, we want to emit something like:
2018 : : | NN | {
2019 : : | NN | longjmp (env, 1);
2020 : : | | ~~~~~~~~~~~~~~~~
2021 : : | | |
2022 : : | | (10) 'ptr' leaks here; was allocated at (7)
2023 : : | | (11) rewinding from 'longjmp' in 'inner'...
2024 : : |
2025 : : <-------------+
2026 : : |
2027 : : 'outer': event 12
2028 : : |
2029 : : | NN | i = setjmp(env);
2030 : : | | ^~~~~~
2031 : : | | |
2032 : : | | (12) ...to 'setjmp' in 'outer' (saved at (2))
2033 : :
2034 : : where the "final" event above is event (10), but we want to append
2035 : : events (11) and (12) afterwards.
2036 : :
2037 : : Do this by setting m_trailing_eedge on any diagnostics that were
2038 : : just saved. */
2039 : 20 : unsigned num_diagnostics = dm->get_num_diagnostics ();
2040 : 28 : for (unsigned i = prev_num_diagnostics; i < num_diagnostics; i++)
2041 : : {
2042 : 8 : saved_diagnostic *sd = dm->get_saved_diagnostic (i);
2043 : 8 : sd->m_trailing_eedge = eedge;
2044 : : }
2045 : : }
2046 : 25 : }
2047 : :
2048 : : /* Subclass of call_info for exploded edges that express
2049 : : a throw or rethrow of an exception (actually a call
2050 : : to __cxa_throw or __cxa_rethrow). */
2051 : :
2052 : : class throw_custom_edge : public call_info
2053 : : {
2054 : : public:
2055 : 196 : throw_custom_edge (const call_details &cd,
2056 : : tree type,
2057 : : bool is_rethrow)
2058 : 196 : : call_info (cd),
2059 : 196 : m_type (type),
2060 : 196 : m_is_rethrow (is_rethrow)
2061 : : {
2062 : : }
2063 : :
2064 : 0 : void print (pretty_printer *pp) const final override
2065 : : {
2066 : 0 : if (m_is_rethrow)
2067 : : {
2068 : 0 : if (m_type)
2069 : 0 : pp_printf (pp, "rethrowing %qT", m_type);
2070 : : else
2071 : 0 : pp_printf (pp, "rethrowing");
2072 : : }
2073 : : else
2074 : : {
2075 : 0 : if (m_type)
2076 : 0 : pp_printf (pp, "throwing %qT", m_type);
2077 : : else
2078 : 0 : pp_printf (pp, "throwing");
2079 : : }
2080 : 0 : }
2081 : :
2082 : 0 : void print_desc (pretty_printer &pp) const final override
2083 : : {
2084 : 0 : print (&pp);
2085 : 0 : }
2086 : :
2087 : 271 : bool update_model (region_model *model,
2088 : : const exploded_edge *,
2089 : : region_model_context *ctxt) const final override
2090 : : {
2091 : 271 : if (m_is_rethrow)
2092 : : {
2093 : 99 : auto eh_node = model->get_current_caught_exception ();
2094 : 0 : gcc_assert (eh_node);
2095 : 99 : model->push_thrown_exception (*eh_node);
2096 : : }
2097 : : else
2098 : : {
2099 : 172 : call_details cd (get_call_details (model, ctxt));
2100 : :
2101 : 172 : const svalue *exception_sval = cd.get_arg_svalue (0);
2102 : 172 : const svalue *tinfo_sval = cd.get_arg_svalue (1);
2103 : 172 : const svalue *destructor_sval = cd.get_arg_svalue (2);
2104 : :
2105 : : /* Push a new exception_node on the model's m_exception_stack. */
2106 : 172 : exception_node eh_node (exception_sval, tinfo_sval, destructor_sval);
2107 : 172 : model->push_thrown_exception (eh_node);
2108 : : }
2109 : :
2110 : 271 : return true;
2111 : : }
2112 : :
2113 : 63 : void add_events_to_path (checker_path *emission_path,
2114 : : const exploded_edge &eedge) const final override
2115 : : {
2116 : 63 : const exploded_node *dst_node = eedge.m_dest;
2117 : 63 : const program_point &dst_point = dst_node->get_point ();
2118 : 63 : const int dst_stack_depth = dst_point.get_stack_depth ();
2119 : :
2120 : 63 : const gcall &call = get_call_stmt ();
2121 : :
2122 : 63 : emission_path->add_event
2123 : 63 : (std::make_unique<explicit_throw_event>
2124 : 63 : (event_loc_info (call.location,
2125 : : dst_point.get_fndecl (),
2126 : 63 : dst_stack_depth),
2127 : : dst_node,
2128 : : call,
2129 : 63 : m_type,
2130 : 63 : m_is_rethrow));
2131 : 63 : }
2132 : :
2133 : : private:
2134 : : tree m_type;
2135 : : bool m_is_rethrow;
2136 : : };
2137 : :
2138 : : /* Subclass of custom_edge_info for an exploded edge that expresses
2139 : : unwinding one stack frame during exception handling. */
2140 : :
2141 : : class unwind_custom_edge : public custom_edge_info
2142 : : {
2143 : : public:
2144 : 6570 : unwind_custom_edge (location_t loc)
2145 : 6570 : : m_loc (loc)
2146 : : {
2147 : : }
2148 : :
2149 : 0 : void print (pretty_printer *pp) const final override
2150 : : {
2151 : 0 : pp_printf (pp, "unwinding frame");
2152 : 0 : }
2153 : :
2154 : 6586 : bool update_model (region_model *model,
2155 : : const exploded_edge *,
2156 : : region_model_context *ctxt) const final override
2157 : : {
2158 : 6586 : model->pop_frame (NULL_TREE, nullptr, ctxt, nullptr, false);
2159 : 6586 : return true;
2160 : : }
2161 : :
2162 : 16 : void add_events_to_path (checker_path *emission_path,
2163 : : const exploded_edge &eedge) const final override
2164 : : {
2165 : 16 : const exploded_node *src_node = eedge.m_src;
2166 : 16 : const program_point &src_point = src_node->get_point ();
2167 : 16 : const int src_stack_depth = src_point.get_stack_depth ();
2168 : 16 : emission_path->add_event
2169 : 16 : (std::make_unique<unwind_event> (event_loc_info (m_loc,
2170 : : src_point.get_fndecl (),
2171 : 16 : src_stack_depth)));
2172 : 16 : }
2173 : :
2174 : : private:
2175 : : location_t m_loc;
2176 : : };
2177 : :
2178 : : /* Locate an SNODE that's a CFG edge with the EH flag,
2179 : : or return nullptr. */
2180 : :
2181 : : static const superedge *
2182 : 7445 : get_eh_outedge (const supernode &snode)
2183 : : {
2184 : 28142 : for (auto out_sedge : snode.m_succs)
2185 : 7916 : if (::edge cfg_edge = out_sedge->get_any_cfg_edge ())
2186 : 7574 : if (cfg_edge->flags & EDGE_EH)
2187 : : return out_sedge;
2188 : :
2189 : : // Not found
2190 : : return nullptr;
2191 : : }
2192 : :
2193 : : /* Given THROWN_ENODE, which expreses a throw or rethrow occurring at
2194 : : THROW_STMT, unwind intraprocedurally and interprocedurally to find
2195 : : the next eh_dispatch statement to handle exceptions, if any.
2196 : :
2197 : : Add eedges and enodes to this graph expressing the actions taken
2198 : : to reach an enode containing the eh_dispatch stmt, if any.
2199 : : Only the final enode is added to this graph's worklist.
2200 : :
2201 : : Use CTXT to warn about problems e.g. memory leaks due to stack frames
2202 : : being unwound. */
2203 : :
2204 : : void
2205 : 6103 : exploded_graph::unwind_from_exception (exploded_node &thrown_enode,
2206 : : const gimple *throw_stmt,
2207 : : region_model_context *ctxt)
2208 : : {
2209 : 6103 : logger * const logger = get_logger ();
2210 : 6103 : LOG_FUNC_1 (logger, "thrown EN: %i", thrown_enode.m_index);
2211 : :
2212 : : /* Iteratively unwind the stack looking for an out-cfg-edge
2213 : : flagged EH. */
2214 : 6103 : exploded_node *iter_enode = &thrown_enode;
2215 : 6103 : while (iter_enode)
2216 : : {
2217 : : /* If we have an out-cfg-edge flagged EH, follow that,
2218 : : presumably to a bb with a label and an eh_dispatch stmt.
2219 : : Otherwise assume no out-cfgs-edges, and we are unwinding to the
2220 : : caller. */
2221 : 7445 : if (auto sedge = get_eh_outedge (*iter_enode->get_supernode ()))
2222 : : {
2223 : : /* Intraprocedural case.
2224 : : Assume we have an out-edge flagged with EH leading to
2225 : : code for dispatch to catch handlers. */
2226 : 875 : const program_point next_point
2227 : 875 : = program_point::before_supernode (sedge->m_dest,
2228 : : sedge,
2229 : 875 : iter_enode->get_point ().get_call_string ());
2230 : 875 : exploded_node *next_enode
2231 : 875 : = get_or_create_node (next_point,
2232 : : iter_enode->get_state (),
2233 : : iter_enode,
2234 : : /* Add this enode to the worklist. */
2235 : : true);
2236 : 875 : if (!next_enode)
2237 : : return;
2238 : :
2239 : 875 : add_edge (iter_enode, next_enode, nullptr, false, nullptr);
2240 : 875 : return;
2241 : : }
2242 : : else
2243 : : {
2244 : : /* Interprocedural case.
2245 : : No out-cfg-edge. Unwind one stack frame. */
2246 : 6570 : program_state unwound_state (iter_enode->get_state ());
2247 : 6570 : location_t loc = throw_stmt ? throw_stmt->location : UNKNOWN_LOCATION;
2248 : 6570 : auto unwind_edge_info
2249 : 6570 : = std::make_unique<unwind_custom_edge> (loc);
2250 : 6570 : unwind_edge_info->update_model (unwound_state.m_region_model, nullptr,
2251 : : ctxt);
2252 : :
2253 : : /* Detect leaks in the new state relative to the old state.
2254 : : Use an alternate ctxt that uses the original enode and the stmt
2255 : : (if any) for the location of any diagnostics. */
2256 : 6570 : {
2257 : 6570 : uncertainty_t uncertainty;
2258 : 6570 : impl_region_model_context ctxt (*this,
2259 : : &thrown_enode,
2260 : 6570 : &iter_enode->get_state (),
2261 : : &unwound_state,
2262 : : &uncertainty,
2263 : : nullptr,
2264 : 6570 : throw_stmt);
2265 : 6570 : program_state::detect_leaks (iter_enode->get_state (),
2266 : : unwound_state,
2267 : : nullptr,
2268 : : get_ext_state (), &ctxt);
2269 : 6570 : }
2270 : 6570 : const call_string &cs = iter_enode->get_point ().get_call_string ();
2271 : 6570 : if (cs.empty_p ())
2272 : : {
2273 : : /* Top-level stack frame in analysis: unwinding
2274 : : to the outside world that called us. */
2275 : : return;
2276 : : }
2277 : : else
2278 : : {
2279 : : /* Nested function in analysis: unwinding to
2280 : : the callsite in the analysis (or beyond). */
2281 : 1624 : program_point unwound_point
2282 : 1624 : = program_point::after_supernode (cs.get_caller_node (), cs);
2283 : 1624 : unwound_point.pop_from_call_stack ();
2284 : :
2285 : 1624 : exploded_node *after_unwind_enode
2286 : 1624 : = get_or_create_node (unwound_point,
2287 : : std::move (unwound_state),
2288 : : iter_enode,
2289 : : /* Don't add this enode to the
2290 : : worklist; we will process it
2291 : : on the next iteration. */
2292 : : false);
2293 : :
2294 : 1624 : if (!after_unwind_enode)
2295 : 282 : return;
2296 : :
2297 : 1342 : add_edge (iter_enode, after_unwind_enode, nullptr, true,
2298 : 1342 : std::move (unwind_edge_info));
2299 : 1342 : iter_enode = after_unwind_enode;
2300 : : }
2301 : 6570 : }
2302 : : }
2303 : 6103 : }
2304 : :
2305 : : /* Handle THROW_CALL, a call to __cxa_throw or __cxa_rethrow.
2306 : :
2307 : : Create an eedge and destination enode for the throw/rethrow, adding
2308 : : them to this egraph. The new enode isn't added to the worklist, but
2309 : : instead exploded_graph::unwind_from_exception is immediately called
2310 : : on it, potentially creating more eedges and enodes leading to an
2311 : : eh_handler stmt. */
2312 : :
2313 : : void
2314 : 196 : exploded_node::on_throw (exploded_graph &eg,
2315 : : const gcall &throw_call,
2316 : : program_state *new_state,
2317 : : bool is_rethrow,
2318 : : region_model_context *ctxt)
2319 : : {
2320 : 196 : region_model *model = new_state->m_region_model;
2321 : 196 : call_details cd (throw_call, model, ctxt);
2322 : :
2323 : : /* Create an enode and eedge for the "throw". */
2324 : 196 : tree type = NULL_TREE;
2325 : 196 : if (is_rethrow)
2326 : : {
2327 : 90 : const exception_node *eh_node = model->get_current_caught_exception ();
2328 : 0 : gcc_assert (eh_node);
2329 : 90 : type = eh_node->maybe_get_type ();
2330 : : }
2331 : : else
2332 : : {
2333 : 106 : const svalue *tinfo_sval = cd.get_arg_svalue (1);
2334 : 106 : type = tinfo_sval->maybe_get_type_from_typeinfo ();
2335 : : }
2336 : 196 : auto throw_edge_info
2337 : 196 : = std::make_unique<throw_custom_edge> (cd, type, is_rethrow);
2338 : 196 : throw_edge_info->update_model (model, nullptr, ctxt);
2339 : :
2340 : 196 : program_point after_throw_point = get_point ().get_next ();
2341 : :
2342 : 196 : exploded_node *after_throw_enode
2343 : 196 : = eg.get_or_create_node (after_throw_point, *new_state, this,
2344 : : /* Don't add to worklist; we process
2345 : : this immediately below. */
2346 : : false);
2347 : :
2348 : 196 : if (!after_throw_enode)
2349 : 0 : return;
2350 : :
2351 : : /* Create custom exploded_edge for a throw. */
2352 : 196 : eg.add_edge (this, after_throw_enode, nullptr, true,
2353 : 196 : std::move (throw_edge_info));
2354 : :
2355 : 196 : eg.unwind_from_exception (*after_throw_enode, &throw_call, ctxt);
2356 : 196 : }
2357 : :
2358 : : /* Handle a gimple "resx" statement by adding eedges and enode.
2359 : : that unwind to the next eh_dispatch statement, if any. Only
2360 : : the final enode is added to the worklist. */
2361 : :
2362 : : void
2363 : 637 : exploded_node::on_resx (exploded_graph &eg,
2364 : : const gresx &/*resx*/,
2365 : : program_state */*new_state*/,
2366 : : region_model_context *ctxt)
2367 : : {
2368 : 637 : eg.unwind_from_exception (*this,
2369 : : nullptr,
2370 : : ctxt);
2371 : 637 : }
2372 : :
2373 : :
2374 : : /* Subroutine of exploded_graph::process_node for finding the successors
2375 : : of the supernode for a function exit basic block.
2376 : :
2377 : : Ensure that pop_frame is called, potentially queuing diagnostics about
2378 : : leaks. */
2379 : :
2380 : : void
2381 : 17091 : exploded_node::detect_leaks (exploded_graph &eg)
2382 : : {
2383 : 17091 : LOG_FUNC_1 (eg.get_logger (), "EN: %i", m_index);
2384 : :
2385 : 17091 : gcc_assert (get_point ().get_supernode ()->return_p ());
2386 : :
2387 : : /* If we're not a "top-level" function, do nothing; pop_frame
2388 : : will be called when handling the return superedge. */
2389 : 23067 : if (get_point ().get_stack_depth () > 1)
2390 : 5976 : return;
2391 : :
2392 : : /* We have a "top-level" function. */
2393 : 11115 : gcc_assert (get_point ().get_stack_depth () == 1);
2394 : :
2395 : 11115 : const program_state &old_state = get_state ();
2396 : :
2397 : : /* Work with a temporary copy of the state: pop the frame, and see
2398 : : what leaks (via purge_unused_svalues). */
2399 : 11115 : program_state new_state (old_state);
2400 : :
2401 : 11115 : gcc_assert (new_state.m_region_model);
2402 : :
2403 : 11115 : uncertainty_t uncertainty;
2404 : 11115 : impl_region_model_context ctxt (eg, this,
2405 : : &old_state, &new_state, &uncertainty, nullptr,
2406 : 11115 : get_stmt ());
2407 : 11115 : const svalue *result = nullptr;
2408 : 11115 : new_state.m_region_model->pop_frame (nullptr, &result, &ctxt, nullptr);
2409 : 11115 : program_state::detect_leaks (old_state, new_state, result,
2410 : : eg.get_ext_state (), &ctxt);
2411 : 28206 : }
2412 : :
2413 : : /* Dump the successors and predecessors of this enode to OUTF. */
2414 : :
2415 : : void
2416 : 0 : exploded_node::dump_succs_and_preds (FILE *outf) const
2417 : : {
2418 : 0 : unsigned i;
2419 : 0 : exploded_edge *e;
2420 : 0 : {
2421 : 0 : auto_vec<exploded_node *> preds (m_preds.length ());
2422 : 0 : FOR_EACH_VEC_ELT (m_preds, i, e)
2423 : 0 : preds.quick_push (e->m_src);
2424 : 0 : pretty_printer pp;
2425 : 0 : print_enode_indices (&pp, preds);
2426 : 0 : fprintf (outf, "preds: %s\n",
2427 : : pp_formatted_text (&pp));
2428 : 0 : }
2429 : 0 : {
2430 : 0 : auto_vec<exploded_node *> succs (m_succs.length ());
2431 : 0 : FOR_EACH_VEC_ELT (m_succs, i, e)
2432 : 0 : succs.quick_push (e->m_dest);
2433 : 0 : pretty_printer pp;
2434 : 0 : print_enode_indices (&pp, succs);
2435 : 0 : fprintf (outf, "succs: %s\n",
2436 : : pp_formatted_text (&pp));
2437 : 0 : }
2438 : 0 : }
2439 : :
2440 : : /* class dynamic_call_info_t : public custom_edge_info. */
2441 : :
2442 : : /* Implementation of custom_edge_info::update_model vfunc
2443 : : for dynamic_call_info_t.
2444 : :
2445 : : Update state for a dynamically discovered call (or return), by pushing
2446 : : or popping the a frame for the appropriate function. */
2447 : :
2448 : : bool
2449 : 304 : dynamic_call_info_t::update_model (region_model *model,
2450 : : const exploded_edge *eedge,
2451 : : region_model_context *ctxt) const
2452 : : {
2453 : 304 : gcc_assert (eedge);
2454 : 304 : if (m_is_returning_call)
2455 : 141 : model->update_for_return_gcall (m_dynamic_call, ctxt);
2456 : : else
2457 : : {
2458 : 163 : function *callee = eedge->m_dest->get_function ();
2459 : 163 : model->update_for_gcall (m_dynamic_call, ctxt, callee);
2460 : : }
2461 : 304 : return true;
2462 : : }
2463 : :
2464 : : /* Implementation of custom_edge_info::add_events_to_path vfunc
2465 : : for dynamic_call_info_t. */
2466 : :
2467 : : void
2468 : 51 : dynamic_call_info_t::add_events_to_path (checker_path *emission_path,
2469 : : const exploded_edge &eedge) const
2470 : : {
2471 : 51 : const exploded_node *src_node = eedge.m_src;
2472 : 51 : const program_point &src_point = src_node->get_point ();
2473 : 51 : const int src_stack_depth = src_point.get_stack_depth ();
2474 : 51 : const exploded_node *dest_node = eedge.m_dest;
2475 : 51 : const program_point &dest_point = dest_node->get_point ();
2476 : 51 : const int dest_stack_depth = dest_point.get_stack_depth ();
2477 : :
2478 : 51 : if (m_is_returning_call)
2479 : 18 : emission_path->add_event
2480 : 18 : (std::make_unique<return_event> (eedge,
2481 : 36 : event_loc_info (m_dynamic_call.location,
2482 : : dest_point.get_fndecl (),
2483 : 18 : dest_stack_depth)));
2484 : : else
2485 : 33 : emission_path->add_event
2486 : 33 : (std::make_unique<call_event> (eedge,
2487 : 66 : event_loc_info (m_dynamic_call.location,
2488 : : src_point.get_fndecl (),
2489 : 33 : src_stack_depth)));
2490 : 51 : }
2491 : :
2492 : : /* class rewind_info_t : public custom_edge_info. */
2493 : :
2494 : : /* Implementation of custom_edge_info::update_model vfunc
2495 : : for rewind_info_t.
2496 : :
2497 : : Update state for the special-case of a rewind of a longjmp
2498 : : to a setjmp (which doesn't have a superedge, but does affect
2499 : : state). */
2500 : :
2501 : : bool
2502 : 11 : rewind_info_t::update_model (region_model *model,
2503 : : const exploded_edge *eedge,
2504 : : region_model_context *) const
2505 : : {
2506 : 11 : gcc_assert (eedge);
2507 : 11 : const program_point &longjmp_point = eedge->m_src->get_point ();
2508 : 11 : const program_point &setjmp_point = eedge->m_dest->get_point ();
2509 : :
2510 : 33 : gcc_assert (longjmp_point.get_stack_depth ()
2511 : : >= setjmp_point.get_stack_depth ());
2512 : :
2513 : 22 : model->on_longjmp (get_longjmp_call (),
2514 : : get_setjmp_call (),
2515 : : setjmp_point.get_stack_depth (), nullptr);
2516 : 11 : return true;
2517 : : }
2518 : :
2519 : : /* Implementation of custom_edge_info::add_events_to_path vfunc
2520 : : for rewind_info_t. */
2521 : :
2522 : : void
2523 : 15 : rewind_info_t::add_events_to_path (checker_path *emission_path,
2524 : : const exploded_edge &eedge) const
2525 : : {
2526 : 15 : const exploded_node *src_node = eedge.m_src;
2527 : 15 : const program_point &src_point = src_node->get_point ();
2528 : 15 : const int src_stack_depth = src_point.get_stack_depth ();
2529 : 15 : const exploded_node *dst_node = eedge.m_dest;
2530 : 15 : const program_point &dst_point = dst_node->get_point ();
2531 : 15 : const int dst_stack_depth = dst_point.get_stack_depth ();
2532 : :
2533 : 15 : emission_path->add_event
2534 : 15 : (std::make_unique<rewind_from_longjmp_event>
2535 : 15 : (&eedge,
2536 : 15 : event_loc_info (get_longjmp_call ().location,
2537 : : src_point.get_fndecl (),
2538 : 15 : src_stack_depth),
2539 : 15 : this));
2540 : 15 : emission_path->add_event
2541 : 15 : (std::make_unique<rewind_to_setjmp_event>
2542 : 15 : (&eedge,
2543 : 15 : event_loc_info (get_setjmp_call ().location,
2544 : : dst_point.get_fndecl (),
2545 : 15 : dst_stack_depth),
2546 : 15 : this));
2547 : 15 : }
2548 : :
2549 : : /* class exploded_edge : public dedge<eg_traits>. */
2550 : :
2551 : : /* exploded_edge's ctor. */
2552 : :
2553 : 401314 : exploded_edge::exploded_edge (exploded_node *src, exploded_node *dest,
2554 : : const superedge *sedge, bool could_do_work,
2555 : 401314 : std::unique_ptr<custom_edge_info> custom_info)
2556 : 401314 : : dedge<eg_traits> (src, dest), m_sedge (sedge),
2557 : 401314 : m_custom_info (std::move (custom_info)),
2558 : 401314 : m_could_do_work_p (could_do_work)
2559 : : {
2560 : 401314 : }
2561 : :
2562 : : /* Implementation of dedge::dump_dot vfunc for exploded_edge.
2563 : : Use the label of the underlying superedge, if any. */
2564 : :
2565 : : void
2566 : 628 : exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &) const
2567 : : {
2568 : 628 : pretty_printer *pp = gv->get_pp ();
2569 : :
2570 : 628 : m_src->dump_dot_id (pp);
2571 : 628 : pp_string (pp, " -> ");
2572 : 628 : m_dest->dump_dot_id (pp);
2573 : 628 : dump_dot_label (pp);
2574 : 628 : }
2575 : :
2576 : : /* Second half of exploded_edge::dump_dot. This is split out
2577 : : for use by trimmed_graph::dump_dot and base_feasible_edge::dump_dot. */
2578 : :
2579 : : void
2580 : 756 : exploded_edge::dump_dot_label (pretty_printer *pp) const
2581 : : {
2582 : 756 : const char *style = "\"solid,bold\"";
2583 : 756 : const char *color = "black";
2584 : 756 : int weight = 10;
2585 : 756 : const char *constraint = "true";
2586 : :
2587 : 756 : if (m_sedge)
2588 : 208 : switch (m_sedge->m_kind)
2589 : : {
2590 : 0 : default:
2591 : 0 : gcc_unreachable ();
2592 : : case SUPEREDGE_CFG_EDGE:
2593 : : break;
2594 : 8 : case SUPEREDGE_CALL:
2595 : 8 : color = "red";
2596 : : //constraint = "false";
2597 : 8 : break;
2598 : 8 : case SUPEREDGE_RETURN:
2599 : 8 : color = "green";
2600 : : //constraint = "false";
2601 : 8 : break;
2602 : 11 : case SUPEREDGE_INTRAPROCEDURAL_CALL:
2603 : 11 : style = "\"dotted\"";
2604 : 11 : break;
2605 : : }
2606 : 756 : if (m_custom_info)
2607 : : {
2608 : 6 : color = "red";
2609 : 6 : style = "\"dotted\"";
2610 : : }
2611 : :
2612 : 756 : pp_printf (pp,
2613 : : (" [style=%s, color=%s, weight=%d, constraint=%s,"
2614 : : " headlabel=\""),
2615 : : style, color, weight, constraint);
2616 : :
2617 : 756 : if (m_sedge)
2618 : 208 : m_sedge->dump_label_to_pp (pp, false);
2619 : 548 : else if (m_custom_info)
2620 : 6 : m_custom_info->print (pp);
2621 : :
2622 : 1451 : pp_printf (pp, "%s",
2623 : 756 : could_do_work_p () ? "(could do work)" : "DOES NO WORK");
2624 : :
2625 : : //pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
2626 : :
2627 : 756 : pp_printf (pp, "\"];\n");
2628 : 756 : }
2629 : :
2630 : : /* Return a new json::object of the form
2631 : : {"src_idx": int, the index of the source exploded edge,
2632 : : "dst_idx": int, the index of the destination exploded edge,
2633 : : "sedge": (optional) object for the superedge, if any,
2634 : : "custom": (optional) str, a description, if this is a custom edge}. */
2635 : :
2636 : : std::unique_ptr<json::object>
2637 : 0 : exploded_edge::to_json () const
2638 : : {
2639 : 0 : auto eedge_obj = std::make_unique<json::object> ();
2640 : 0 : eedge_obj->set_integer ("src_idx", m_src->m_index);
2641 : 0 : eedge_obj->set_integer ("dst_idx", m_dest->m_index);
2642 : 0 : if (m_sedge)
2643 : 0 : eedge_obj->set ("sedge", m_sedge->to_json ());
2644 : 0 : if (m_custom_info)
2645 : : {
2646 : 0 : pretty_printer pp;
2647 : 0 : pp_format_decoder (&pp) = default_tree_printer;
2648 : 0 : m_custom_info->print (&pp);
2649 : 0 : eedge_obj->set_string ("custom", pp_formatted_text (&pp));
2650 : 0 : }
2651 : 0 : return eedge_obj;
2652 : : }
2653 : :
2654 : : /* struct stats. */
2655 : :
2656 : : /* stats' ctor. */
2657 : :
2658 : 25129 : stats::stats (int num_supernodes)
2659 : 25129 : : m_node_reuse_count (0),
2660 : 25129 : m_node_reuse_after_merge_count (0),
2661 : 25129 : m_num_supernodes (num_supernodes)
2662 : : {
2663 : 175903 : for (int i = 0; i < NUM_POINT_KINDS; i++)
2664 : 150774 : m_num_nodes[i] = 0;
2665 : 25129 : }
2666 : :
2667 : : /* Log these stats in multiline form to LOGGER. */
2668 : :
2669 : : void
2670 : 4 : stats::log (logger *logger) const
2671 : : {
2672 : 4 : gcc_assert (logger);
2673 : 28 : for (int i = 0; i < NUM_POINT_KINDS; i++)
2674 : 24 : if (m_num_nodes[i] > 0)
2675 : 14 : logger->log ("m_num_nodes[%s]: %i",
2676 : : point_kind_to_string (static_cast <enum point_kind> (i)),
2677 : : m_num_nodes[i]);
2678 : 4 : logger->log ("m_node_reuse_count: %i", m_node_reuse_count);
2679 : 4 : logger->log ("m_node_reuse_after_merge_count: %i",
2680 : 4 : m_node_reuse_after_merge_count);
2681 : 4 : }
2682 : :
2683 : : /* Dump these stats in multiline form to OUT. */
2684 : :
2685 : : void
2686 : 0 : stats::dump (FILE *out) const
2687 : : {
2688 : 0 : for (int i = 0; i < NUM_POINT_KINDS; i++)
2689 : 0 : if (m_num_nodes[i] > 0)
2690 : 0 : fprintf (out, "m_num_nodes[%s]: %i\n",
2691 : : point_kind_to_string (static_cast <enum point_kind> (i)),
2692 : : m_num_nodes[i]);
2693 : 0 : fprintf (out, "m_node_reuse_count: %i\n", m_node_reuse_count);
2694 : 0 : fprintf (out, "m_node_reuse_after_merge_count: %i\n",
2695 : 0 : m_node_reuse_after_merge_count);
2696 : :
2697 : 0 : if (m_num_supernodes > 0)
2698 : 0 : fprintf (out, "PK_AFTER_SUPERNODE nodes per supernode: %.2f\n",
2699 : 0 : (float)m_num_nodes[PK_AFTER_SUPERNODE] / (float)m_num_supernodes);
2700 : 0 : }
2701 : :
2702 : : /* Return the total number of enodes recorded within this object. */
2703 : :
2704 : : int
2705 : 2 : stats::get_total_enodes () const
2706 : : {
2707 : 2 : int result = 0;
2708 : 14 : for (int i = 0; i < NUM_POINT_KINDS; i++)
2709 : 12 : result += m_num_nodes[i];
2710 : 2 : return result;
2711 : : }
2712 : :
2713 : : /* struct per_function_data. */
2714 : :
2715 : 8309 : per_function_data::~per_function_data ()
2716 : : {
2717 : 35197 : for (auto iter : m_summaries)
2718 : 10270 : delete iter;
2719 : 8309 : }
2720 : :
2721 : : void
2722 : 10270 : per_function_data::add_call_summary (exploded_node *node)
2723 : : {
2724 : 10270 : m_summaries.safe_push (new call_summary (this, node));
2725 : 10270 : }
2726 : :
2727 : : /* strongly_connected_components's ctor. Tarjan's SCC algorithm. */
2728 : :
2729 : 3323 : strongly_connected_components::
2730 : 3323 : strongly_connected_components (const supergraph &sg, logger *logger)
2731 : 6642 : : m_sg (sg), m_per_node (m_sg.num_nodes ())
2732 : : {
2733 : 3323 : LOG_SCOPE (logger);
2734 : 3323 : auto_timevar tv (TV_ANALYZER_SCC);
2735 : :
2736 : 134686 : for (int i = 0; i < m_sg.num_nodes (); i++)
2737 : 64022 : m_per_node.quick_push (per_node_data ());
2738 : :
2739 : 134686 : for (int i = 0; i < m_sg.num_nodes (); i++)
2740 : 64022 : if (m_per_node[i].m_index == -1)
2741 : 10317 : strong_connect (i);
2742 : :
2743 : 3323 : if (0)
2744 : : dump ();
2745 : 3323 : }
2746 : :
2747 : : /* Dump this object to stderr. */
2748 : :
2749 : : DEBUG_FUNCTION void
2750 : 0 : strongly_connected_components::dump () const
2751 : : {
2752 : 0 : for (int i = 0; i < m_sg.num_nodes (); i++)
2753 : : {
2754 : 0 : const per_node_data &v = m_per_node[i];
2755 : 0 : fprintf (stderr, "SN %i: index: %i lowlink: %i on_stack: %i\n",
2756 : 0 : i, v.m_index, v.m_lowlink, v.m_on_stack);
2757 : : }
2758 : 0 : }
2759 : :
2760 : : /* Return a new json::array of per-snode SCC ids. */
2761 : :
2762 : : std::unique_ptr<json::array>
2763 : 0 : strongly_connected_components::to_json () const
2764 : : {
2765 : 0 : auto scc_arr = std::make_unique<json::array> ();
2766 : 0 : for (int i = 0; i < m_sg.num_nodes (); i++)
2767 : 0 : scc_arr->append (std::make_unique<json::integer_number> (get_scc_id (i)));
2768 : 0 : return scc_arr;
2769 : : }
2770 : :
2771 : : /* Subroutine of strongly_connected_components's ctor, part of Tarjan's
2772 : : SCC algorithm. */
2773 : :
2774 : : void
2775 : 64022 : strongly_connected_components::strong_connect (unsigned index)
2776 : : {
2777 : 64022 : supernode *v_snode = m_sg.get_node_by_index (index);
2778 : :
2779 : : /* Set the depth index for v to the smallest unused index. */
2780 : 64022 : per_node_data *v = &m_per_node[index];
2781 : 64022 : v->m_index = index;
2782 : 64022 : v->m_lowlink = index;
2783 : 64022 : m_stack.safe_push (index);
2784 : 64022 : v->m_on_stack = true;
2785 : 64022 : index++;
2786 : :
2787 : : /* Consider successors of v. */
2788 : 64022 : unsigned i;
2789 : 64022 : superedge *sedge;
2790 : 136132 : FOR_EACH_VEC_ELT (v_snode->m_succs, i, sedge)
2791 : : {
2792 : 72110 : if (sedge->get_kind () != SUPEREDGE_CFG_EDGE
2793 : 72110 : && sedge->get_kind () != SUPEREDGE_INTRAPROCEDURAL_CALL)
2794 : 7212 : continue;
2795 : 64898 : supernode *w_snode = sedge->m_dest;
2796 : 64898 : per_node_data *w = &m_per_node[w_snode->m_index];
2797 : 64898 : if (w->m_index == -1)
2798 : : {
2799 : : /* Successor w has not yet been visited; recurse on it. */
2800 : 53705 : strong_connect (w_snode->m_index);
2801 : 53705 : v->m_lowlink = MIN (v->m_lowlink, w->m_lowlink);
2802 : : }
2803 : 11193 : else if (w->m_on_stack)
2804 : : {
2805 : : /* Successor w is in stack S and hence in the current SCC
2806 : : If w is not on stack, then (v, w) is a cross-edge in the DFS
2807 : : tree and must be ignored. */
2808 : 1887 : v->m_lowlink = MIN (v->m_lowlink, w->m_index);
2809 : : }
2810 : : }
2811 : :
2812 : : /* If v is a root node, pop the stack and generate an SCC. */
2813 : :
2814 : 64022 : if (v->m_lowlink == v->m_index)
2815 : : {
2816 : 64022 : per_node_data *w;
2817 : 64022 : do {
2818 : 64022 : int idx = m_stack.pop ();
2819 : 64022 : w = &m_per_node[idx];
2820 : 64022 : w->m_on_stack = false;
2821 : 64022 : } while (w != v);
2822 : : }
2823 : 64022 : }
2824 : :
2825 : : /* worklist's ctor. */
2826 : :
2827 : 3323 : worklist::worklist (const exploded_graph &eg, const analysis_plan &plan)
2828 : 3323 : : m_scc (eg.get_supergraph (), eg.get_logger ()),
2829 : 3323 : m_plan (plan),
2830 : 3323 : m_queue (key_t (*this, nullptr))
2831 : : {
2832 : 3323 : }
2833 : :
2834 : : /* Return the number of nodes in the worklist. */
2835 : :
2836 : : unsigned
2837 : 355867 : worklist::length () const
2838 : : {
2839 : 355867 : return m_queue.nodes ();
2840 : : }
2841 : :
2842 : : /* Return the next node in the worklist, removing it. */
2843 : :
2844 : : exploded_node *
2845 : 381122 : worklist::take_next ()
2846 : : {
2847 : 381122 : return m_queue.extract_min ();
2848 : : }
2849 : :
2850 : : /* Return the next node in the worklist without removing it. */
2851 : :
2852 : : exploded_node *
2853 : 464292 : worklist::peek_next ()
2854 : : {
2855 : 464292 : return m_queue.min ();
2856 : : }
2857 : :
2858 : : /* Add ENODE to the worklist. */
2859 : :
2860 : : void
2861 : 381856 : worklist::add_node (exploded_node *enode)
2862 : : {
2863 : 381856 : gcc_assert (enode->get_status () == exploded_node::status::worklist);
2864 : 381856 : m_queue.insert (key_t (*this, enode), enode);
2865 : 381856 : }
2866 : :
2867 : : /* Comparator for implementing worklist::key_t comparison operators.
2868 : : Return negative if KA is before KB
2869 : : Return positive if KA is after KB
2870 : : Return 0 if they are equal.
2871 : :
2872 : : The ordering of the worklist is critical for performance and for
2873 : : avoiding node explosions. Ideally we want all enodes at a CFG join-point
2874 : : with the same callstring to be sorted next to each other in the worklist
2875 : : so that a run of consecutive enodes can be merged and processed "in bulk"
2876 : : rather than individually or pairwise, minimizing the number of new enodes
2877 : : created. */
2878 : :
2879 : : int
2880 : 1361407 : worklist::key_t::cmp (const worklist::key_t &ka, const worklist::key_t &kb)
2881 : : {
2882 : 1361407 : const program_point &point_a = ka.m_enode->get_point ();
2883 : 1361407 : const program_point &point_b = kb.m_enode->get_point ();
2884 : 1361407 : const call_string &call_string_a = point_a.get_call_string ();
2885 : 1361407 : const call_string &call_string_b = point_b.get_call_string ();
2886 : :
2887 : : /* Order empty-callstring points with different functions based on the
2888 : : analysis_plan, so that we generate summaries before they are used. */
2889 : 1361407 : if (flag_analyzer_call_summaries
2890 : 851046 : && call_string_a.empty_p ()
2891 : 798986 : && call_string_b.empty_p ()
2892 : 798986 : && point_a.get_function () != nullptr
2893 : 798986 : && point_b.get_function () != nullptr
2894 : 2151007 : && point_a.get_function () != point_b.get_function ())
2895 : : {
2896 : 343619 : if (int cmp = ka.m_worklist.m_plan.cmp_function (point_a.get_function (),
2897 : : point_b.get_function ()))
2898 : : return cmp;
2899 : : }
2900 : :
2901 : : /* Sort by callstring, so that nodes with deeper call strings are processed
2902 : : before those with shallower call strings.
2903 : : If we have
2904 : : splitting BB
2905 : : / \
2906 : : / \
2907 : : fn call no fn call
2908 : : \ /
2909 : : \ /
2910 : : join BB
2911 : : then we want the path inside the function call to be fully explored up
2912 : : to the return to the join BB before we explore on the "no fn call" path,
2913 : : so that both enodes at the join BB reach the front of the worklist at
2914 : : the same time and thus have a chance of being merged. */
2915 : 1017788 : int cs_cmp = call_string::cmp (call_string_a, call_string_b);
2916 : 1017788 : if (cs_cmp)
2917 : : return cs_cmp;
2918 : :
2919 : : /* Order by SCC. */
2920 : 817316 : int scc_id_a = ka.get_scc_id (ka.m_enode);
2921 : 817316 : int scc_id_b = kb.get_scc_id (kb.m_enode);
2922 : 817316 : if (scc_id_a != scc_id_b)
2923 : 554729 : return scc_id_a - scc_id_b;
2924 : :
2925 : : /* If in same SCC, order by supernode index (an arbitrary but stable
2926 : : ordering). */
2927 : 262587 : const supernode *snode_a = ka.m_enode->get_supernode ();
2928 : 262587 : const supernode *snode_b = kb.m_enode->get_supernode ();
2929 : 262587 : if (snode_a == nullptr)
2930 : : {
2931 : 0 : if (snode_b != nullptr)
2932 : : /* One is nullptr. */
2933 : : return -1;
2934 : : else
2935 : : /* Both are nullptr. */
2936 : 0 : return 0;
2937 : : }
2938 : 262587 : if (snode_b == nullptr)
2939 : : /* One is nullptr. */
2940 : : return 1;
2941 : : /* Neither are nullptr. */
2942 : 259274 : gcc_assert (snode_a && snode_b);
2943 : 259274 : if (snode_a->m_index != snode_b->m_index)
2944 : 7170 : return snode_a->m_index - snode_b->m_index;
2945 : :
2946 : 252104 : gcc_assert (snode_a == snode_b);
2947 : :
2948 : : /* Order within supernode via program point. */
2949 : 252104 : int within_snode_cmp
2950 : 252104 : = function_point::cmp_within_supernode (point_a.get_function_point (),
2951 : : point_b.get_function_point ());
2952 : 252104 : if (within_snode_cmp)
2953 : : return within_snode_cmp;
2954 : :
2955 : : /* Otherwise, we ought to have the same program_point. */
2956 : 150372 : gcc_assert (point_a == point_b);
2957 : :
2958 : 150372 : const program_state &state_a = ka.m_enode->get_state ();
2959 : 150372 : const program_state &state_b = kb.m_enode->get_state ();
2960 : :
2961 : : /* Sort by sm-state, so that identical sm-states are grouped
2962 : : together in the worklist. */
2963 : 626478 : for (unsigned sm_idx = 0; sm_idx < state_a.m_checker_states.length ();
2964 : : ++sm_idx)
2965 : : {
2966 : 561322 : sm_state_map *smap_a = state_a.m_checker_states[sm_idx];
2967 : 561322 : sm_state_map *smap_b = state_b.m_checker_states[sm_idx];
2968 : :
2969 : 561322 : if (int smap_cmp = sm_state_map::cmp (*smap_a, *smap_b))
2970 : : return smap_cmp;
2971 : : }
2972 : :
2973 : : /* Otherwise, we have two enodes at the same program point but with
2974 : : different states. We don't have a good total ordering on states,
2975 : : so order them by enode index, so that we have at least have a
2976 : : stable sort. */
2977 : 65156 : return ka.m_enode->m_index - kb.m_enode->m_index;
2978 : : }
2979 : :
2980 : : /* Return a new json::object of the form
2981 : : {"scc" : [per-snode-IDs]}, */
2982 : :
2983 : : std::unique_ptr<json::object>
2984 : 0 : worklist::to_json () const
2985 : : {
2986 : 0 : auto worklist_obj = std::make_unique<json::object> ();
2987 : :
2988 : 0 : worklist_obj->set ("scc", m_scc.to_json ());
2989 : :
2990 : : /* The following field isn't yet being JSONified:
2991 : : queue_t m_queue; */
2992 : :
2993 : 0 : return worklist_obj;
2994 : : }
2995 : :
2996 : : /* exploded_graph's ctor. */
2997 : :
2998 : 3323 : exploded_graph::exploded_graph (const supergraph &sg, logger *logger,
2999 : : const extrinsic_state &ext_state,
3000 : : const state_purge_map *purge_map,
3001 : : const analysis_plan &plan,
3002 : 3323 : int verbosity)
3003 : 3323 : : m_sg (sg), m_logger (logger),
3004 : 3323 : m_worklist (*this, plan),
3005 : 3323 : m_ext_state (ext_state),
3006 : 3323 : m_purge_map (purge_map),
3007 : 3323 : m_plan (plan),
3008 : 3323 : m_diagnostic_manager (logger, ext_state.get_engine (), verbosity),
3009 : 6642 : m_global_stats (m_sg.num_nodes ()),
3010 : 6642 : m_functionless_stats (m_sg.num_nodes ()),
3011 : 13288 : m_PK_AFTER_SUPERNODE_per_snode (m_sg.num_nodes ())
3012 : : {
3013 : 6646 : m_origin = get_or_create_node
3014 : 3323 : (program_point::origin (*ext_state.get_model_manager ()),
3015 : 6646 : program_state (ext_state), nullptr);
3016 : 134686 : for (int i = 0; i < m_sg.num_nodes (); i++)
3017 : 64022 : m_PK_AFTER_SUPERNODE_per_snode.quick_push (i);
3018 : 3323 : }
3019 : :
3020 : : /* exploded_graph's dtor. */
3021 : :
3022 : 3323 : exploded_graph::~exploded_graph ()
3023 : : {
3024 : 559425 : for (auto iter : m_per_point_data)
3025 : 556102 : delete iter.second;
3026 : 19941 : for (auto iter : m_per_function_data)
3027 : 8309 : delete iter.second;
3028 : 16758 : for (auto iter : m_per_function_stats)
3029 : 10116 : delete iter.second;
3030 : 20057 : for (auto iter : m_per_call_string_data)
3031 : 8367 : delete iter.second;
3032 : 6646 : }
3033 : :
3034 : : /* Subroutine for use when implementing __attribute__((tainted_args))
3035 : : on functions and on function pointer fields in structs.
3036 : :
3037 : : Called on STATE representing a call to FNDECL.
3038 : : Mark all params of FNDECL in STATE as "tainted". Mark the value of all
3039 : : regions pointed to by params of FNDECL as "tainted".
3040 : :
3041 : : Return true if successful; return false if the "taint" state machine
3042 : : was not found. */
3043 : :
3044 : : static bool
3045 : 184 : mark_params_as_tainted (program_state *state, tree fndecl,
3046 : : const extrinsic_state &ext_state)
3047 : : {
3048 : 184 : unsigned taint_sm_idx;
3049 : 184 : if (!ext_state.get_sm_idx_by_name ("taint", &taint_sm_idx))
3050 : : return false;
3051 : 184 : sm_state_map *smap = state->m_checker_states[taint_sm_idx];
3052 : :
3053 : 184 : const state_machine &sm = ext_state.get_sm (taint_sm_idx);
3054 : 184 : state_machine::state_t tainted = sm.get_state_by_name ("tainted");
3055 : :
3056 : 184 : region_model_manager *mgr = ext_state.get_model_manager ();
3057 : :
3058 : 184 : function *fun = DECL_STRUCT_FUNCTION (fndecl);
3059 : 184 : gcc_assert (fun);
3060 : :
3061 : 435 : for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
3062 : 251 : iter_parm = DECL_CHAIN (iter_parm))
3063 : : {
3064 : 251 : tree param = iter_parm;
3065 : 251 : if (tree parm_default_ssa = ssa_default_def (fun, iter_parm))
3066 : 193 : param = parm_default_ssa;
3067 : 251 : const region *param_reg = state->m_region_model->get_lvalue (param, nullptr);
3068 : 251 : const svalue *init_sval = mgr->get_or_create_initial_value (param_reg);
3069 : 251 : smap->set_state (state->m_region_model, init_sval,
3070 : : tainted, nullptr /*origin_new_sval*/, ext_state);
3071 : 251 : if (POINTER_TYPE_P (TREE_TYPE (param)))
3072 : : {
3073 : 48 : const region *pointee_reg = mgr->get_symbolic_region (init_sval);
3074 : : /* Mark "*param" as tainted. */
3075 : 48 : const svalue *init_pointee_sval
3076 : 48 : = mgr->get_or_create_initial_value (pointee_reg);
3077 : 48 : smap->set_state (state->m_region_model, init_pointee_sval,
3078 : : tainted, nullptr /*origin_new_sval*/, ext_state);
3079 : : }
3080 : : }
3081 : :
3082 : : return true;
3083 : : }
3084 : :
3085 : : /* Custom event for use by tainted_args_function_info when a function
3086 : : has been marked with __attribute__((tainted_args)). */
3087 : :
3088 : : class tainted_args_function_custom_event : public custom_event
3089 : : {
3090 : : public:
3091 : 107 : tainted_args_function_custom_event (const event_loc_info &loc_info)
3092 : 107 : : custom_event (loc_info),
3093 : 107 : m_fndecl (loc_info.m_fndecl)
3094 : : {
3095 : : }
3096 : :
3097 : : void
3098 : 214 : print_desc (pretty_printer &pp) const final override
3099 : : {
3100 : 214 : pp_printf (&pp,
3101 : : "function %qE marked with %<__attribute__((tainted_args))%>",
3102 : 214 : m_fndecl);
3103 : 214 : }
3104 : :
3105 : : private:
3106 : : tree m_fndecl;
3107 : : };
3108 : :
3109 : : /* Custom exploded_edge info for top-level calls to a function
3110 : : marked with __attribute__((tainted_args)). */
3111 : :
3112 : : class tainted_args_function_info : public custom_edge_info
3113 : : {
3114 : : public:
3115 : 174 : tainted_args_function_info (tree fndecl)
3116 : 174 : : m_fndecl (fndecl)
3117 : : {}
3118 : :
3119 : 0 : void print (pretty_printer *pp) const final override
3120 : : {
3121 : 0 : pp_string (pp, "call to tainted_args function");
3122 : 0 : };
3123 : :
3124 : 0 : bool update_model (region_model *,
3125 : : const exploded_edge *,
3126 : : region_model_context *) const final override
3127 : : {
3128 : : /* No-op. */
3129 : 0 : return true;
3130 : : }
3131 : :
3132 : 107 : void add_events_to_path (checker_path *emission_path,
3133 : : const exploded_edge &) const final override
3134 : : {
3135 : 107 : emission_path->add_event
3136 : 107 : (std::make_unique<tainted_args_function_custom_event>
3137 : 107 : (event_loc_info (DECL_SOURCE_LOCATION (m_fndecl), m_fndecl, 0)));
3138 : 107 : }
3139 : :
3140 : : private:
3141 : : tree m_fndecl;
3142 : : };
3143 : :
3144 : : /* Ensure that there is an exploded_node representing an external call to
3145 : : FUN, adding it to the worklist if creating it.
3146 : :
3147 : : Add an edge from the origin exploded_node to the function entrypoint
3148 : : exploded_node.
3149 : :
3150 : : Return the exploded_node for the entrypoint to the function. */
3151 : :
3152 : : exploded_node *
3153 : 10345 : exploded_graph::add_function_entry (const function &fun)
3154 : : {
3155 : 10345 : gcc_assert (gimple_has_body_p (fun.decl));
3156 : :
3157 : : /* Be idempotent. */
3158 : 10345 : function *key = const_cast<function *> (&fun);
3159 : 10345 : if (m_functions_with_enodes.contains (key))
3160 : : {
3161 : 373 : logger * const logger = get_logger ();
3162 : 373 : if (logger)
3163 : 0 : logger->log ("entrypoint for %qE already exists", fun.decl);
3164 : 373 : return nullptr;
3165 : : }
3166 : :
3167 : 9972 : program_point point
3168 : 9972 : = program_point::from_function_entry (*m_ext_state.get_model_manager (),
3169 : : m_sg, fun);
3170 : 9972 : program_state state (m_ext_state);
3171 : 9972 : state.push_frame (m_ext_state, fun);
3172 : :
3173 : 9972 : std::unique_ptr<custom_edge_info> edge_info = nullptr;
3174 : :
3175 : 9972 : if (lookup_attribute ("tainted_args", DECL_ATTRIBUTES (fun.decl)))
3176 : : {
3177 : 174 : if (mark_params_as_tainted (&state, fun.decl, m_ext_state))
3178 : 174 : edge_info = std::make_unique<tainted_args_function_info> (fun.decl);
3179 : : }
3180 : :
3181 : 9972 : if (!state.m_valid)
3182 : : return nullptr;
3183 : :
3184 : 9972 : exploded_node *enode = get_or_create_node (point, state, nullptr);
3185 : 9972 : if (!enode)
3186 : : return nullptr;
3187 : :
3188 : 9964 : add_edge (m_origin, enode, nullptr, false, std::move (edge_info));
3189 : :
3190 : 9964 : m_functions_with_enodes.add (key);
3191 : :
3192 : 9964 : return enode;
3193 : 9972 : }
3194 : :
3195 : : /* Get or create an exploded_node for (POINT, STATE).
3196 : : If a new node is created and ADD_TO_WORKLIST is true,
3197 : : it is added to the worklist.
3198 : :
3199 : : Use ENODE_FOR_DIAG, a pre-existing enode, for any diagnostics
3200 : : that need to be emitted (e.g. when purging state *before* we have
3201 : : a new enode). */
3202 : :
3203 : : exploded_node *
3204 : 394929 : exploded_graph::get_or_create_node (const program_point &point,
3205 : : const program_state &state,
3206 : : exploded_node *enode_for_diag,
3207 : : bool add_to_worklist)
3208 : : {
3209 : 394929 : logger * const logger = get_logger ();
3210 : 394929 : LOG_FUNC (logger);
3211 : 394929 : if (logger)
3212 : : {
3213 : 122 : format f (false);
3214 : 122 : pretty_printer *pp = logger->get_printer ();
3215 : 122 : logger->start_log_line ();
3216 : 122 : pp_string (pp, "point: ");
3217 : 122 : point.print (pp, f);
3218 : 122 : logger->end_log_line ();
3219 : 122 : logger->start_log_line ();
3220 : 122 : pp_string (pp, "state: ");
3221 : 122 : state.dump_to_pp (m_ext_state, true, false, pp);
3222 : 122 : logger->end_log_line ();
3223 : : }
3224 : :
3225 : : /* Stop exploring paths for which we don't know how to effectively
3226 : : model the state. */
3227 : 394929 : if (!state.m_valid)
3228 : : {
3229 : 4 : if (logger)
3230 : 0 : logger->log ("invalid state; not creating node");
3231 : 4 : return nullptr;
3232 : : }
3233 : :
3234 : 394925 : auto_cfun sentinel (point.get_function ());
3235 : :
3236 : 394925 : state.validate (get_ext_state ());
3237 : :
3238 : : //state.dump (get_ext_state ());
3239 : :
3240 : : /* Prune state to try to improve the chances of a cache hit,
3241 : : avoiding generating redundant nodes. */
3242 : 394925 : uncertainty_t uncertainty;
3243 : 394925 : program_state pruned_state
3244 : 394925 : = state.prune_for_point (*this, point, enode_for_diag, &uncertainty);
3245 : :
3246 : 394925 : pruned_state.validate (get_ext_state ());
3247 : :
3248 : : //pruned_state.dump (get_ext_state ());
3249 : :
3250 : 394925 : if (logger)
3251 : : {
3252 : 122 : pretty_printer *pp = logger->get_printer ();
3253 : 122 : logger->start_log_line ();
3254 : 122 : pp_string (pp, "pruned_state: ");
3255 : 122 : pruned_state.dump_to_pp (m_ext_state, true, false, pp);
3256 : 122 : logger->end_log_line ();
3257 : 122 : pruned_state.m_region_model->dump_to_pp (logger->get_printer (), true,
3258 : : false);
3259 : : }
3260 : :
3261 : 394925 : stats *per_fn_stats = get_or_create_function_stats (point.get_function ());
3262 : :
3263 : 394925 : stats *per_cs_stats
3264 : 394925 : = &get_or_create_per_call_string_data (point.get_call_string ())->m_stats;
3265 : :
3266 : 394925 : point_and_state ps (point, pruned_state);
3267 : 394925 : ps.validate (m_ext_state);
3268 : 394925 : if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
3269 : : {
3270 : : /* An exploded_node for PS already exists. */
3271 : 2636 : if (logger)
3272 : 5 : logger->log ("reused EN: %i", (*slot)->m_index);
3273 : 2636 : m_global_stats.m_node_reuse_count++;
3274 : 2636 : per_fn_stats->m_node_reuse_count++;
3275 : 2636 : per_cs_stats->m_node_reuse_count++;
3276 : 2636 : return *slot;
3277 : : }
3278 : :
3279 : 392289 : per_program_point_data *per_point_data
3280 : 392289 : = get_or_create_per_program_point_data (point);
3281 : :
3282 : : /* Consider merging state with another enode at this program_point. */
3283 : 392289 : if (flag_analyzer_state_merge)
3284 : : {
3285 : : exploded_node *existing_enode;
3286 : : unsigned i;
3287 : 637102 : FOR_EACH_VEC_ELT (per_point_data->m_enodes, i, existing_enode)
3288 : : {
3289 : 250748 : if (logger)
3290 : 157 : logger->log ("considering merging with existing EN: %i for point",
3291 : 157 : existing_enode->m_index);
3292 : 250748 : gcc_assert (existing_enode->get_point () == point);
3293 : 250748 : const program_state &existing_state = existing_enode->get_state ();
3294 : :
3295 : : /* This merges successfully within the loop. */
3296 : :
3297 : 250748 : program_state merged_state (m_ext_state);
3298 : 250748 : if (pruned_state.can_merge_with_p (existing_state, m_ext_state, point,
3299 : : &merged_state))
3300 : : {
3301 : 46323 : merged_state.validate (m_ext_state);
3302 : 46323 : if (logger)
3303 : 114 : logger->log ("merging new state with that of EN: %i",
3304 : 114 : existing_enode->m_index);
3305 : :
3306 : : /* Try again for a cache hit.
3307 : : Whether we get one or not, merged_state's value_ids have no
3308 : : relationship to those of the input state, and thus to those
3309 : : of CHANGE, so we must purge any svalue_ids from *CHANGE. */
3310 : 46323 : ps.set_state (merged_state);
3311 : :
3312 : 46323 : if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
3313 : : {
3314 : : /* An exploded_node for PS already exists. */
3315 : 1871 : if (logger)
3316 : 2 : logger->log ("reused EN: %i", (*slot)->m_index);
3317 : 1871 : m_global_stats.m_node_reuse_after_merge_count++;
3318 : 1871 : per_fn_stats->m_node_reuse_after_merge_count++;
3319 : 1871 : per_cs_stats->m_node_reuse_after_merge_count++;
3320 : 1871 : return *slot;
3321 : : }
3322 : : }
3323 : : else
3324 : 204425 : if (logger)
3325 : 43 : logger->log ("not merging new state with that of EN: %i",
3326 : 43 : existing_enode->m_index);
3327 : 250748 : }
3328 : : }
3329 : :
3330 : : /* Impose a limit on the number of enodes per program point, and
3331 : : simply stop if we exceed it. */
3332 : 390418 : if ((int)per_point_data->m_enodes.length ()
3333 : 390418 : >= param_analyzer_max_enodes_per_program_point)
3334 : : {
3335 : 2117 : pretty_printer pp;
3336 : 2117 : point.print (&pp, format (false));
3337 : 2117 : print_enode_indices (&pp, per_point_data->m_enodes);
3338 : 2117 : if (logger)
3339 : 0 : logger->log ("not creating enode; too many at program point: %s",
3340 : : pp_formatted_text (&pp));
3341 : 2117 : warning_at (point.get_location (), OPT_Wanalyzer_too_complex,
3342 : : "terminating analysis for this program point: %s",
3343 : : pp_formatted_text (&pp));
3344 : 2117 : per_point_data->m_excess_enodes++;
3345 : 2117 : return nullptr;
3346 : 2117 : }
3347 : :
3348 : 388301 : ps.validate (m_ext_state);
3349 : :
3350 : : /* An exploded_node for "ps" doesn't already exist; create one. */
3351 : 773283 : exploded_node *node = new exploded_node (ps, m_nodes.length ());
3352 : 388301 : add_node (node);
3353 : 388301 : m_point_and_state_to_node.put (node->get_ps_key (), node);
3354 : :
3355 : : /* Update per-program_point data. */
3356 : 388301 : per_point_data->m_enodes.safe_push (node);
3357 : :
3358 : 388301 : const enum point_kind node_pk = node->get_point ().get_kind ();
3359 : 388301 : m_global_stats.m_num_nodes[node_pk]++;
3360 : 388301 : per_fn_stats->m_num_nodes[node_pk]++;
3361 : 388301 : per_cs_stats->m_num_nodes[node_pk]++;
3362 : :
3363 : 388301 : if (node_pk == PK_AFTER_SUPERNODE)
3364 : 119326 : m_PK_AFTER_SUPERNODE_per_snode[point.get_supernode ()->m_index]++;
3365 : :
3366 : 388301 : if (logger)
3367 : : {
3368 : 115 : format f (false);
3369 : 115 : pretty_printer *pp = logger->get_printer ();
3370 : 115 : logger->log ("created EN: %i", node->m_index);
3371 : 115 : logger->start_log_line ();
3372 : 115 : pp_string (pp, "point: ");
3373 : 115 : point.print (pp, f);
3374 : 115 : logger->end_log_line ();
3375 : 115 : logger->start_log_line ();
3376 : 115 : pp_string (pp, "pruned_state: ");
3377 : 115 : pruned_state.dump_to_pp (m_ext_state, true, false, pp);
3378 : 115 : logger->end_log_line ();
3379 : : }
3380 : :
3381 : : /* Add the new node to the worlist. */
3382 : 388301 : if (add_to_worklist)
3383 : 381856 : m_worklist.add_node (node);
3384 : : else
3385 : 6445 : node->set_status (exploded_node::status::special);
3386 : : return node;
3387 : 789854 : }
3388 : :
3389 : : /* Add an exploded_edge from SRC to DEST, recording its association
3390 : : with SEDGE (which may be NULL), and, if non-NULL, taking ownership
3391 : : of CUSTOM_INFO. COULD_DO_WORK is used for detecting infinite loops.
3392 : : Return the newly-created eedge. */
3393 : :
3394 : : exploded_edge *
3395 : 401314 : exploded_graph::add_edge (exploded_node *src, exploded_node *dest,
3396 : : const superedge *sedge, bool could_do_work,
3397 : : std::unique_ptr<custom_edge_info> custom_info)
3398 : : {
3399 : 401314 : if (get_logger ())
3400 : 124 : get_logger ()->log ("creating edge EN: %i -> EN: %i",
3401 : 124 : src->m_index, dest->m_index);
3402 : 401314 : exploded_edge *e
3403 : : = new exploded_edge (src, dest, sedge, could_do_work,
3404 : 401314 : std::move (custom_info));
3405 : 401314 : digraph<eg_traits>::add_edge (e);
3406 : 401314 : return e;
3407 : : }
3408 : :
3409 : : /* Ensure that this graph has per-program_point-data for POINT;
3410 : : borrow a pointer to it. */
3411 : :
3412 : : per_program_point_data *
3413 : 392289 : exploded_graph::
3414 : : get_or_create_per_program_point_data (const program_point &point)
3415 : : {
3416 : 392289 : if (per_program_point_data **slot = m_per_point_data.get (&point))
3417 : 114238 : return *slot;
3418 : :
3419 : 278051 : per_program_point_data *per_point_data = new per_program_point_data (point);
3420 : 278051 : m_per_point_data.put (&per_point_data->m_key, per_point_data);
3421 : 278051 : return per_point_data;
3422 : : }
3423 : :
3424 : : /* Get this graph's per-program-point-data for POINT if there is any,
3425 : : otherwise nullptr. */
3426 : :
3427 : : per_program_point_data *
3428 : 0 : exploded_graph::get_per_program_point_data (const program_point &point) const
3429 : : {
3430 : 0 : if (per_program_point_data **slot
3431 : 0 : = const_cast <point_map_t &> (m_per_point_data).get (&point))
3432 : 0 : return *slot;
3433 : :
3434 : : return nullptr;
3435 : : }
3436 : :
3437 : : /* Ensure that this graph has per-call_string-data for CS;
3438 : : borrow a pointer to it. */
3439 : :
3440 : : per_call_string_data *
3441 : 394925 : exploded_graph::get_or_create_per_call_string_data (const call_string &cs)
3442 : : {
3443 : 394925 : if (per_call_string_data **slot = m_per_call_string_data.get (&cs))
3444 : 386558 : return *slot;
3445 : :
3446 : 16730 : per_call_string_data *data = new per_call_string_data (cs, m_sg.num_nodes ());
3447 : 8367 : m_per_call_string_data.put (&data->m_key,
3448 : : data);
3449 : 8367 : return data;
3450 : : }
3451 : :
3452 : : /* Ensure that this graph has per-function-data for FUN;
3453 : : borrow a pointer to it. */
3454 : :
3455 : : per_function_data *
3456 : 10270 : exploded_graph::get_or_create_per_function_data (function *fun)
3457 : : {
3458 : 10270 : if (per_function_data **slot = m_per_function_data.get (fun))
3459 : 1961 : return *slot;
3460 : :
3461 : 8309 : per_function_data *data = new per_function_data ();
3462 : 8309 : m_per_function_data.put (fun, data);
3463 : 8309 : return data;
3464 : : }
3465 : :
3466 : : /* Get this graph's per-function-data for FUN if there is any,
3467 : : otherwise nullptr. */
3468 : :
3469 : : per_function_data *
3470 : 1029 : exploded_graph::get_per_function_data (function *fun) const
3471 : : {
3472 : 2058 : if (per_function_data **slot
3473 : 1029 : = const_cast <per_function_data_t &> (m_per_function_data).get (fun))
3474 : 949 : return *slot;
3475 : :
3476 : : return nullptr;
3477 : : }
3478 : :
3479 : : /* Return true if FUN should be traversed directly, rather than only as
3480 : : called via other functions. */
3481 : :
3482 : : static bool
3483 : 10122 : toplevel_function_p (const function &fun, logger *logger)
3484 : : {
3485 : : /* Don't directly traverse into functions that have an "__analyzer_"
3486 : : prefix. Doing so is useful for the analyzer test suite, allowing
3487 : : us to have functions that are called in traversals, but not directly
3488 : : explored, thus testing how the analyzer handles calls and returns.
3489 : : With this, we can have DejaGnu directives that cover just the case
3490 : : of where a function is called by another function, without generating
3491 : : excess messages from the case of the first function being traversed
3492 : : directly. */
3493 : : #define ANALYZER_PREFIX "__analyzer_"
3494 : 10122 : if (!strncmp (IDENTIFIER_POINTER (DECL_NAME (fun.decl)), ANALYZER_PREFIX,
3495 : : strlen (ANALYZER_PREFIX)))
3496 : : {
3497 : 150 : if (logger)
3498 : 0 : logger->log ("not traversing %qE (starts with %qs)",
3499 : : fun.decl, ANALYZER_PREFIX);
3500 : 150 : return false;
3501 : : }
3502 : :
3503 : 9972 : if (logger)
3504 : 2 : logger->log ("traversing %qE (all checks passed)", fun.decl);
3505 : :
3506 : : return true;
3507 : : }
3508 : :
3509 : : /* Custom event for use by tainted_call_info when a callback field has been
3510 : : marked with __attribute__((tainted_args)), for labelling the field. */
3511 : :
3512 : : class tainted_args_field_custom_event : public custom_event
3513 : : {
3514 : : public:
3515 : 4 : tainted_args_field_custom_event (tree field)
3516 : 4 : : custom_event (event_loc_info (DECL_SOURCE_LOCATION (field), NULL_TREE, 0)),
3517 : 4 : m_field (field)
3518 : : {
3519 : 4 : }
3520 : :
3521 : 8 : void print_desc (pretty_printer &pp) const final override
3522 : : {
3523 : 8 : pp_printf (&pp,
3524 : : "field %qE of %qT"
3525 : : " is marked with %<__attribute__((tainted_args))%>",
3526 : 8 : m_field, DECL_CONTEXT (m_field));
3527 : 8 : }
3528 : :
3529 : : private:
3530 : : tree m_field;
3531 : : };
3532 : :
3533 : : /* Custom event for use by tainted_call_info when a callback field has been
3534 : : marked with __attribute__((tainted_args)), for labelling the function used
3535 : : in that callback. */
3536 : :
3537 : : class tainted_args_callback_custom_event : public custom_event
3538 : : {
3539 : : public:
3540 : 4 : tainted_args_callback_custom_event (const event_loc_info &loc_info,
3541 : : tree field)
3542 : 4 : : custom_event (loc_info),
3543 : 4 : m_field (field)
3544 : : {
3545 : : }
3546 : :
3547 : 8 : void print_desc (pretty_printer &pp) const final override
3548 : : {
3549 : 8 : pp_printf (&pp,
3550 : : "function %qE used as initializer for field %qE"
3551 : : " marked with %<__attribute__((tainted_args))%>",
3552 : 8 : get_fndecl (), m_field);
3553 : 8 : }
3554 : :
3555 : : private:
3556 : : tree m_field;
3557 : : };
3558 : :
3559 : : /* Custom edge info for use when adding a function used by a callback field
3560 : : marked with '__attribute__((tainted_args))'. */
3561 : :
3562 : : class tainted_args_call_info : public custom_edge_info
3563 : : {
3564 : : public:
3565 : 10 : tainted_args_call_info (tree field, tree fndecl, location_t loc)
3566 : 10 : : m_field (field), m_fndecl (fndecl), m_loc (loc)
3567 : : {}
3568 : :
3569 : 0 : void print (pretty_printer *pp) const final override
3570 : : {
3571 : 0 : pp_string (pp, "call to tainted field");
3572 : 0 : };
3573 : :
3574 : 0 : bool update_model (region_model *,
3575 : : const exploded_edge *,
3576 : : region_model_context *) const final override
3577 : : {
3578 : : /* No-op. */
3579 : 0 : return true;
3580 : : }
3581 : :
3582 : 4 : void add_events_to_path (checker_path *emission_path,
3583 : : const exploded_edge &) const final override
3584 : : {
3585 : : /* Show the field in the struct declaration, e.g.
3586 : : "(1) field 'store' is marked with '__attribute__((tainted_args))'" */
3587 : 4 : emission_path->add_event
3588 : 4 : (std::make_unique<tainted_args_field_custom_event> (m_field));
3589 : :
3590 : : /* Show the callback in the initializer
3591 : : e.g.
3592 : : "(2) function 'gadget_dev_desc_UDC_store' used as initializer
3593 : : for field 'store' marked with '__attribute__((tainted_args))'". */
3594 : 4 : emission_path->add_event
3595 : 4 : (std::make_unique<tainted_args_callback_custom_event>
3596 : 4 : (event_loc_info (m_loc, m_fndecl, 0),
3597 : : m_field));
3598 : 4 : }
3599 : :
3600 : : private:
3601 : : tree m_field;
3602 : : tree m_fndecl;
3603 : : location_t m_loc;
3604 : : };
3605 : :
3606 : : /* Given an initializer at LOC for FIELD marked with
3607 : : '__attribute__((tainted_args))' initialized with FNDECL, add an
3608 : : entrypoint to FNDECL to EG (and to its worklist) where the params to
3609 : : FNDECL are marked as tainted. */
3610 : :
3611 : : static void
3612 : 10 : add_tainted_args_callback (exploded_graph *eg, tree field, tree fndecl,
3613 : : location_t loc)
3614 : : {
3615 : 10 : logger *logger = eg->get_logger ();
3616 : :
3617 : 10 : LOG_SCOPE (logger);
3618 : :
3619 : 10 : if (!gimple_has_body_p (fndecl))
3620 : : return;
3621 : :
3622 : 10 : const extrinsic_state &ext_state = eg->get_ext_state ();
3623 : :
3624 : 10 : function *fun = DECL_STRUCT_FUNCTION (fndecl);
3625 : 10 : gcc_assert (fun);
3626 : :
3627 : 10 : program_point point
3628 : 10 : = program_point::from_function_entry (*ext_state.get_model_manager (),
3629 : : eg->get_supergraph (), *fun);
3630 : 10 : program_state state (ext_state);
3631 : 10 : state.push_frame (ext_state, *fun);
3632 : :
3633 : 10 : if (!mark_params_as_tainted (&state, fndecl, ext_state))
3634 : : return;
3635 : :
3636 : 10 : if (!state.m_valid)
3637 : : return;
3638 : :
3639 : 10 : exploded_node *enode = eg->get_or_create_node (point, state, nullptr);
3640 : 10 : if (logger)
3641 : : {
3642 : 0 : if (enode)
3643 : 0 : logger->log ("created EN %i for tainted_args %qE entrypoint",
3644 : 0 : enode->m_index, fndecl);
3645 : : else
3646 : : {
3647 : 0 : logger->log ("did not create enode for tainted_args %qE entrypoint",
3648 : : fndecl);
3649 : 0 : return;
3650 : : }
3651 : : }
3652 : :
3653 : 10 : eg->add_edge (eg->get_origin (), enode, nullptr, false,
3654 : 10 : std::make_unique<tainted_args_call_info> (field, fndecl, loc));
3655 : 10 : }
3656 : :
3657 : : /* Callback for walk_tree for finding callbacks within initializers;
3658 : : ensure that any callback initializer where the corresponding field is
3659 : : marked with '__attribute__((tainted_args))' is treated as an entrypoint
3660 : : to the analysis, special-casing that the inputs to the callback are
3661 : : untrustworthy. */
3662 : :
3663 : : static tree
3664 : 27670 : add_any_callbacks (tree *tp, int *, void *data)
3665 : : {
3666 : 27670 : exploded_graph *eg = (exploded_graph *)data;
3667 : 27670 : if (TREE_CODE (*tp) == CONSTRUCTOR)
3668 : : {
3669 : : /* Find fields with the "tainted_args" attribute.
3670 : : walk_tree only walks the values, not the index values;
3671 : : look at the index values. */
3672 : : unsigned HOST_WIDE_INT idx;
3673 : : constructor_elt *ce;
3674 : :
3675 : 18519 : for (idx = 0; vec_safe_iterate (CONSTRUCTOR_ELTS (*tp), idx, &ce);
3676 : : idx++)
3677 : 13762 : if (ce->index && TREE_CODE (ce->index) == FIELD_DECL)
3678 : 12773 : if (lookup_attribute ("tainted_args", DECL_ATTRIBUTES (ce->index)))
3679 : : {
3680 : 10 : tree value = ce->value;
3681 : 10 : if (TREE_CODE (value) == ADDR_EXPR
3682 : 10 : && TREE_CODE (TREE_OPERAND (value, 0)) == FUNCTION_DECL)
3683 : 20 : add_tainted_args_callback (eg, ce->index,
3684 : 10 : TREE_OPERAND (value, 0),
3685 : 10 : EXPR_LOCATION (value));
3686 : : }
3687 : : }
3688 : :
3689 : 27670 : return NULL_TREE;
3690 : : }
3691 : :
3692 : : /* Add initial nodes to EG, with entrypoints for externally-callable
3693 : : functions. */
3694 : :
3695 : : void
3696 : 3323 : exploded_graph::build_initial_worklist ()
3697 : : {
3698 : 3323 : logger * const logger = get_logger ();
3699 : 3323 : LOG_SCOPE (logger);
3700 : :
3701 : 3323 : cgraph_node *node;
3702 : 13445 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
3703 : : {
3704 : 10122 : function *fun = node->get_fun ();
3705 : 10122 : gcc_assert (fun);
3706 : 10122 : if (!toplevel_function_p (*fun, logger))
3707 : 150 : continue;
3708 : 9972 : exploded_node *enode = add_function_entry (*fun);
3709 : 9972 : if (logger)
3710 : : {
3711 : 2 : if (enode)
3712 : 2 : logger->log ("created EN %i for %qE entrypoint",
3713 : 2 : enode->m_index, fun->decl);
3714 : : else
3715 : 0 : logger->log ("did not create enode for %qE entrypoint", fun->decl);
3716 : : }
3717 : : }
3718 : :
3719 : : /* Find callbacks that are reachable from global initializers. */
3720 : 3323 : varpool_node *vpnode;
3721 : 18616 : FOR_EACH_VARIABLE (vpnode)
3722 : : {
3723 : 5985 : tree decl = vpnode->decl;
3724 : 5985 : tree init = DECL_INITIAL (decl);
3725 : 5985 : if (!init)
3726 : 1199 : continue;
3727 : 4786 : walk_tree (&init, add_any_callbacks, this, nullptr);
3728 : : }
3729 : 3323 : }
3730 : :
3731 : : /* The main loop of the analysis.
3732 : : Take freshly-created exploded_nodes from the worklist, calling
3733 : : process_node on them to explore the <point, state> graph.
3734 : : Add edges to their successors, potentially creating new successors
3735 : : (which are also added to the worklist). */
3736 : :
3737 : : void
3738 : 3323 : exploded_graph::process_worklist ()
3739 : : {
3740 : 3323 : logger * const logger = get_logger ();
3741 : 3323 : LOG_SCOPE (logger);
3742 : 3323 : auto_timevar tv (TV_ANALYZER_WORKLIST);
3743 : :
3744 : 359188 : while (m_worklist.length () > 0)
3745 : : {
3746 : 352608 : exploded_node *node = m_worklist.take_next ();
3747 : 352608 : gcc_assert (node->get_status () == exploded_node::status::worklist);
3748 : 352608 : gcc_assert (node->m_succs.length () == 0
3749 : : || node == m_origin);
3750 : :
3751 : 352608 : if (logger)
3752 : 110 : logger->log ("next to process: EN: %i", node->m_index);
3753 : :
3754 : : /* If we have a run of nodes that are before-supernode, try merging and
3755 : : processing them together, rather than pairwise or individually. */
3756 : 352608 : if (flag_analyzer_state_merge && node != m_origin)
3757 : 345449 : if (maybe_process_run_of_before_supernode_enodes (node))
3758 : 13148 : goto handle_limit;
3759 : :
3760 : : /* Avoid exponential explosions of nodes by attempting to merge
3761 : : nodes that are at the same program point and which have
3762 : : sufficiently similar state. */
3763 : 339460 : if (flag_analyzer_state_merge && node != m_origin)
3764 : 332301 : if (exploded_node *node_2 = m_worklist.peek_next ())
3765 : : {
3766 : 266291 : gcc_assert (node_2->get_status ()
3767 : : == exploded_node::status::worklist);
3768 : 266291 : gcc_assert (node->m_succs.length () == 0);
3769 : 266291 : gcc_assert (node_2->m_succs.length () == 0);
3770 : :
3771 : 266291 : gcc_assert (node != node_2);
3772 : :
3773 : 266291 : if (logger)
3774 : 85 : logger->log ("peek worklist: EN: %i", node_2->m_index);
3775 : :
3776 : 266291 : if (node->get_point () == node_2->get_point ())
3777 : : {
3778 : 33799 : const program_point &point = node->get_point ();
3779 : 33799 : if (logger)
3780 : : {
3781 : 3 : format f (false);
3782 : 3 : pretty_printer *pp = logger->get_printer ();
3783 : 3 : logger->start_log_line ();
3784 : 3 : logger->log_partial
3785 : 3 : ("got potential merge EN: %i and EN: %i at ",
3786 : 3 : node->m_index, node_2->m_index);
3787 : 3 : point.print (pp, f);
3788 : 3 : logger->end_log_line ();
3789 : : }
3790 : 33799 : const program_state &state = node->get_state ();
3791 : 33799 : const program_state &state_2 = node_2->get_state ();
3792 : :
3793 : : /* They shouldn't be equal, or we wouldn't have two
3794 : : separate nodes. */
3795 : 33799 : gcc_assert (state != state_2);
3796 : :
3797 : 33799 : program_state merged_state (m_ext_state);
3798 : 33799 : if (state.can_merge_with_p (state_2, m_ext_state,
3799 : : point, &merged_state))
3800 : : {
3801 : 427 : if (logger)
3802 : 0 : logger->log ("merging EN: %i and EN: %i",
3803 : 0 : node->m_index, node_2->m_index);
3804 : :
3805 : 427 : if (merged_state == state)
3806 : : {
3807 : : /* Then merge node_2 into node by adding an edge. */
3808 : 14 : add_edge (node_2, node, nullptr, false);
3809 : :
3810 : : /* Remove node_2 from the worklist. */
3811 : 14 : m_worklist.take_next ();
3812 : 14 : node_2->set_status (exploded_node::status::merger);
3813 : :
3814 : : /* Continue processing "node" below. */
3815 : : }
3816 : 413 : else if (merged_state == state_2)
3817 : : {
3818 : : /* Then merge node into node_2, and leave node_2
3819 : : in the worklist, to be processed on the next
3820 : : iteration. */
3821 : 370 : add_edge (node, node_2, nullptr, false);
3822 : 370 : node->set_status (exploded_node::status::merger);
3823 : 370 : continue;
3824 : : }
3825 : : else
3826 : : {
3827 : : /* We have a merged state that differs from
3828 : : both state and state_2. */
3829 : :
3830 : : /* Remove node_2 from the worklist. */
3831 : 43 : m_worklist.take_next ();
3832 : :
3833 : : /* Create (or get) an exploded node for the merged
3834 : : states, adding to the worklist. */
3835 : 43 : exploded_node *merged_enode
3836 : 43 : = get_or_create_node (node->get_point (),
3837 : : merged_state, node);
3838 : 43 : if (merged_enode == nullptr)
3839 : 1 : continue;
3840 : :
3841 : 42 : if (logger)
3842 : 0 : logger->log ("merged EN: %i and EN: %i into EN: %i",
3843 : 0 : node->m_index, node_2->m_index,
3844 : 0 : merged_enode->m_index);
3845 : :
3846 : : /* "node" and "node_2" have both now been removed
3847 : : from the worklist; we should not process them.
3848 : :
3849 : : "merged_enode" may be a new node; if so it will be
3850 : : processed in a subsequent iteration.
3851 : : Alternatively, "merged_enode" could be an existing
3852 : : node; one way the latter can
3853 : : happen is if we end up merging a succession of
3854 : : similar nodes into one. */
3855 : :
3856 : : /* If merged_node is one of the two we were merging,
3857 : : add it back to the worklist to ensure it gets
3858 : : processed.
3859 : :
3860 : : Add edges from the merged nodes to it (but not a
3861 : : self-edge). */
3862 : 42 : if (merged_enode == node)
3863 : 0 : m_worklist.add_node (merged_enode);
3864 : : else
3865 : : {
3866 : 42 : add_edge (node, merged_enode, nullptr, false);
3867 : 42 : node->set_status (exploded_node::status::merger);
3868 : : }
3869 : :
3870 : 42 : if (merged_enode == node_2)
3871 : 0 : m_worklist.add_node (merged_enode);
3872 : : else
3873 : : {
3874 : 42 : add_edge (node_2, merged_enode, nullptr, false);
3875 : 42 : node_2->set_status (exploded_node::status::merger);
3876 : : }
3877 : :
3878 : 42 : continue;
3879 : 42 : }
3880 : : }
3881 : :
3882 : : /* TODO: should we attempt more than two nodes,
3883 : : or just do pairs of nodes? (and hope that we get
3884 : : a cascade of mergers). */
3885 : 33799 : }
3886 : : }
3887 : :
3888 : 339047 : process_node (node);
3889 : :
3890 : 352195 : handle_limit:
3891 : : /* Impose a hard limit on the number of exploded nodes, to ensure
3892 : : that the analysis terminates in the face of pathological state
3893 : : explosion (or bugs).
3894 : :
3895 : : Specifically, the limit is on the number of PK_AFTER_SUPERNODE
3896 : : exploded nodes, looking at supernode exit events.
3897 : :
3898 : : We use exit rather than entry since there can be multiple
3899 : : entry ENs, one per phi; the number of PK_AFTER_SUPERNODE ought
3900 : : to be equivalent to the number of supernodes multiplied by the
3901 : : number of states. */
3902 : 352195 : const int limit = m_sg.num_nodes () * param_analyzer_bb_explosion_factor;
3903 : 352195 : if (m_global_stats.m_num_nodes[PK_AFTER_SUPERNODE] > limit)
3904 : : {
3905 : 66 : if (logger)
3906 : 0 : logger->log ("bailing out; too many nodes");
3907 : 132 : warning_at (node->get_point ().get_location (),
3908 : 66 : OPT_Wanalyzer_too_complex,
3909 : : "analysis bailed out early"
3910 : : " (%i 'after-snode' enodes; %i enodes)",
3911 : : m_global_stats.m_num_nodes[PK_AFTER_SUPERNODE],
3912 : : m_nodes.length ());
3913 : 66 : return;
3914 : : }
3915 : : }
3916 : 3323 : }
3917 : :
3918 : : /* Attempt to process a consecutive run of sufficiently-similar nodes in
3919 : : the worklist at a CFG join-point (having already popped ENODE from the
3920 : : head of the worklist).
3921 : :
3922 : : If ENODE's point is of the form (before-supernode, SNODE) and the next
3923 : : nodes in the worklist are a consecutive run of enodes of the same form,
3924 : : for the same supernode as ENODE (but potentially from different in-edges),
3925 : : process them all together, setting their status to status::bulk_merged,
3926 : : and return true.
3927 : : Otherwise, return false, in which case ENODE must be processed in the
3928 : : normal way.
3929 : :
3930 : : When processing them all together, generate successor states based
3931 : : on phi nodes for the appropriate CFG edges, and then attempt to merge
3932 : : these states into a minimal set of merged successor states, partitioning
3933 : : the inputs by merged successor state.
3934 : :
3935 : : Create new exploded nodes for all of the merged states, and add edges
3936 : : connecting the input enodes to the corresponding merger exploded nodes.
3937 : :
3938 : : We hope we have a much smaller number of merged successor states
3939 : : compared to the number of input enodes - ideally just one,
3940 : : if all successor states can be merged.
3941 : :
3942 : : Processing and merging many together as one operation rather than as
3943 : : pairs avoids scaling issues where per-pair mergers could bloat the
3944 : : graph with merger nodes (especially so after switch statements). */
3945 : :
3946 : : bool
3947 : 345449 : exploded_graph::
3948 : : maybe_process_run_of_before_supernode_enodes (exploded_node *enode)
3949 : : {
3950 : : /* A struct for tracking per-input state. */
3951 : 41605 : struct item
3952 : : {
3953 : 41605 : item (exploded_node *input_enode)
3954 : 41605 : : m_input_enode (input_enode),
3955 : 41605 : m_processed_state (input_enode->get_state ()),
3956 : 41605 : m_merger_idx (-1)
3957 : : {}
3958 : :
3959 : : exploded_node *m_input_enode;
3960 : : program_state m_processed_state;
3961 : : int m_merger_idx;
3962 : : };
3963 : :
3964 : 345449 : gcc_assert (enode->get_status () == exploded_node::status::worklist);
3965 : 345449 : gcc_assert (enode->m_succs.length () == 0);
3966 : :
3967 : 345449 : const program_point &point = enode->get_point ();
3968 : :
3969 : 345449 : if (point.get_kind () != PK_BEFORE_SUPERNODE)
3970 : : return false;
3971 : :
3972 : 103534 : const supernode *snode = point.get_supernode ();
3973 : :
3974 : 103534 : logger * const logger = get_logger ();
3975 : 103534 : LOG_SCOPE (logger);
3976 : :
3977 : : /* Find a run of enodes in the worklist that are before the same supernode,
3978 : : but potentially from different in-edges. */
3979 : 103534 : auto_vec <exploded_node *> enodes;
3980 : 103534 : enodes.safe_push (enode);
3981 : 131991 : while (exploded_node *enode_2 = m_worklist.peek_next ())
3982 : : {
3983 : 111954 : gcc_assert (enode_2->get_status ()
3984 : : == exploded_node::status::worklist);
3985 : 111954 : gcc_assert (enode_2->m_succs.length () == 0);
3986 : :
3987 : 111954 : const program_point &point_2 = enode_2->get_point ();
3988 : :
3989 : 111954 : if (point_2.get_kind () == PK_BEFORE_SUPERNODE
3990 : 106151 : && point_2.get_supernode () == snode
3991 : 140660 : && &point_2.get_call_string () == &point.get_call_string ())
3992 : : {
3993 : 28457 : enodes.safe_push (enode_2);
3994 : 28457 : m_worklist.take_next ();
3995 : : }
3996 : : else
3997 : : break;
3998 : 28457 : }
3999 : :
4000 : : /* If the only node is ENODE, then give up. */
4001 : 103534 : if (enodes.length () == 1)
4002 : : return false;
4003 : :
4004 : 13148 : if (logger)
4005 : 2 : logger->log ("got run of %i enodes for SN: %i",
4006 : 2 : enodes.length (), snode->m_index);
4007 : :
4008 : : /* All of these enodes have a shared successor point (even if they
4009 : : were for different in-edges). */
4010 : 13148 : program_point next_point (point.get_next ());
4011 : :
4012 : : /* Calculate the successor state for each enode in enodes. */
4013 : 26296 : auto_delete_vec<item> items (enodes.length ());
4014 : 13148 : unsigned i;
4015 : 13148 : exploded_node *iter_enode;
4016 : 54753 : FOR_EACH_VEC_ELT (enodes, i, iter_enode)
4017 : : {
4018 : 41605 : item *it = new item (iter_enode);
4019 : 41605 : items.quick_push (it);
4020 : 41605 : const program_state &state = iter_enode->get_state ();
4021 : 41605 : program_state *next_state = &it->m_processed_state;
4022 : 41605 : next_state->validate (m_ext_state);
4023 : 41605 : const program_point &iter_point = iter_enode->get_point ();
4024 : 41605 : if (const superedge *iter_sedge = iter_point.get_from_edge ())
4025 : : {
4026 : 38376 : uncertainty_t uncertainty;
4027 : 38376 : impl_region_model_context ctxt (*this, iter_enode,
4028 : : &state, next_state,
4029 : 38376 : &uncertainty, nullptr, nullptr);
4030 : 38376 : const cfg_superedge *last_cfg_superedge
4031 : 38376 : = iter_sedge->dyn_cast_cfg_superedge ();
4032 : 38376 : if (last_cfg_superedge)
4033 : 38376 : next_state->m_region_model->update_for_phis
4034 : 38376 : (snode, last_cfg_superedge, &ctxt);
4035 : 76752 : }
4036 : 41605 : next_state->validate (m_ext_state);
4037 : : }
4038 : :
4039 : : /* Attempt to partition the items into a set of merged states.
4040 : : We hope we have a much smaller number of merged states
4041 : : compared to the number of input enodes - ideally just one,
4042 : : if all can be merged. */
4043 : 13148 : auto_delete_vec <program_state> merged_states;
4044 : 13148 : auto_vec<item *> first_item_for_each_merged_state;
4045 : 13148 : item *it;
4046 : 54753 : FOR_EACH_VEC_ELT (items, i, it)
4047 : : {
4048 : 41605 : const program_state &it_state = it->m_processed_state;
4049 : 41605 : program_state *merged_state;
4050 : 41605 : unsigned iter_merger_idx;
4051 : 79944 : FOR_EACH_VEC_ELT (merged_states, iter_merger_idx, merged_state)
4052 : : {
4053 : 49752 : merged_state->validate (m_ext_state);
4054 : 49752 : program_state merge (m_ext_state);
4055 : 49752 : if (it_state.can_merge_with_p (*merged_state, m_ext_state,
4056 : : next_point, &merge))
4057 : : {
4058 : 11413 : *merged_state = merge;
4059 : 11413 : merged_state->validate (m_ext_state);
4060 : 11413 : it->m_merger_idx = iter_merger_idx;
4061 : 11413 : if (logger)
4062 : 4 : logger->log ("reusing merger state %i for item %i (EN: %i)",
4063 : 4 : it->m_merger_idx, i, it->m_input_enode->m_index);
4064 : 11413 : goto got_merger;
4065 : : }
4066 : 49752 : }
4067 : : /* If it couldn't be merged with any existing merged_states,
4068 : : create a new one. */
4069 : 30192 : if (it->m_merger_idx == -1)
4070 : : {
4071 : 30192 : it->m_merger_idx = merged_states.length ();
4072 : 30192 : merged_states.safe_push (new program_state (it_state));
4073 : 30192 : first_item_for_each_merged_state.safe_push (it);
4074 : 30192 : if (logger)
4075 : 3 : logger->log ("using new merger state %i for item %i (EN: %i)",
4076 : 3 : it->m_merger_idx, i, it->m_input_enode->m_index);
4077 : : }
4078 : 0 : got_merger:
4079 : 41605 : gcc_assert (it->m_merger_idx >= 0);
4080 : 41605 : gcc_assert ((unsigned)it->m_merger_idx < merged_states.length ());
4081 : : }
4082 : :
4083 : : /* Create merger nodes. */
4084 : 39444 : auto_vec<exploded_node *> next_enodes (merged_states.length ());
4085 : 13148 : program_state *merged_state;
4086 : 43340 : FOR_EACH_VEC_ELT (merged_states, i, merged_state)
4087 : : {
4088 : 30192 : exploded_node *src_enode
4089 : 30192 : = first_item_for_each_merged_state[i]->m_input_enode;
4090 : 30192 : exploded_node *next
4091 : 30192 : = get_or_create_node (next_point, *merged_state, src_enode);
4092 : : /* "next" could be nullptr; we handle that when adding the edges below. */
4093 : 30192 : next_enodes.quick_push (next);
4094 : 30192 : if (logger)
4095 : : {
4096 : 3 : if (next)
4097 : 3 : logger->log ("using EN: %i for merger state %i", next->m_index, i);
4098 : : else
4099 : 0 : logger->log ("using NULL enode for merger state %i", i);
4100 : : }
4101 : : }
4102 : :
4103 : : /* Create edges from each input enode to the appropriate successor enode.
4104 : : Update the status of the now-processed input enodes. */
4105 : 54753 : FOR_EACH_VEC_ELT (items, i, it)
4106 : : {
4107 : 41605 : exploded_node *next = next_enodes[it->m_merger_idx];
4108 : 41605 : if (next)
4109 : 41273 : add_edge (it->m_input_enode, next, nullptr,
4110 : : false); /* no "work" is done during merger. */
4111 : 41605 : it->m_input_enode->set_status (exploded_node::status::bulk_merged);
4112 : : }
4113 : :
4114 : 13148 : if (logger)
4115 : 4 : logger->log ("merged %i in-enodes into %i out-enode(s) at SN: %i",
4116 : 2 : items.length (), merged_states.length (), snode->m_index);
4117 : :
4118 : 13148 : return true;
4119 : 116682 : }
4120 : :
4121 : : /* Return true if STMT must appear at the start of its exploded node, and
4122 : : thus we can't consolidate its effects within a run of other statements,
4123 : : where PREV_STMT was the previous statement. */
4124 : :
4125 : : static bool
4126 : 128743 : stmt_requires_new_enode_p (const gimple *stmt,
4127 : : const gimple *prev_stmt)
4128 : : {
4129 : 128743 : if (const gcall *call_stmt = dyn_cast <const gcall *> (stmt))
4130 : : {
4131 : 20111 : const gcall &call = *call_stmt;
4132 : : /* Stop consolidating at calls to
4133 : : "__analyzer_dump_exploded_nodes", so they always appear at the
4134 : : start of an exploded_node. */
4135 : 20111 : if (is_special_named_call_p (call, "__analyzer_dump_exploded_nodes",
4136 : : 1))
4137 : : return true;
4138 : :
4139 : : /* sm-signal.cc injects an additional custom eedge at "signal" calls
4140 : : from the registration enode to the handler enode, separate from the
4141 : : regular next state, which defeats the "detect state change" logic
4142 : : in process_node. Work around this via special-casing, to ensure
4143 : : we split the enode immediately before any "signal" call. */
4144 : 19835 : if (is_special_named_call_p (call, "signal", 2, true))
4145 : : return true;
4146 : : }
4147 : :
4148 : : /* If we had a PREV_STMT with an unknown location, and this stmt
4149 : : has a known location, then if a state change happens here, it
4150 : : could be consolidated into PREV_STMT, giving us an event with
4151 : : no location. Ensure that STMT gets its own exploded_node to
4152 : : avoid this. */
4153 : 128466 : if (get_pure_location (prev_stmt->location) == UNKNOWN_LOCATION
4154 : 128466 : && get_pure_location (stmt->location) != UNKNOWN_LOCATION)
4155 : : return true;
4156 : :
4157 : : return false;
4158 : : }
4159 : :
4160 : : /* Return true if OLD_STATE and NEW_STATE are sufficiently different that
4161 : : we should split enodes and create an exploded_edge separating them
4162 : : (which makes it easier to identify state changes of intereset when
4163 : : constructing checker_paths). */
4164 : :
4165 : : static bool
4166 : 245170 : state_change_requires_new_enode_p (const program_state &old_state,
4167 : : const program_state &new_state)
4168 : : {
4169 : : /* Changes in dynamic extents signify creations of heap/alloca regions
4170 : : and resizings of heap regions; likely to be of interest in
4171 : : diagnostic paths. */
4172 : 490340 : if (old_state.m_region_model->get_dynamic_extents ()
4173 : 245170 : != new_state.m_region_model->get_dynamic_extents ())
4174 : : return true;
4175 : :
4176 : : /* Changes in sm-state are of interest. */
4177 : : int sm_idx;
4178 : : sm_state_map *smap;
4179 : 1729378 : FOR_EACH_VEC_ELT (old_state.m_checker_states, sm_idx, smap)
4180 : : {
4181 : 1516883 : const sm_state_map *old_smap = old_state.m_checker_states[sm_idx];
4182 : 1516883 : const sm_state_map *new_smap = new_state.m_checker_states[sm_idx];
4183 : 1516883 : if (*old_smap != *new_smap)
4184 : : return true;
4185 : : }
4186 : :
4187 : : return false;
4188 : : }
4189 : :
4190 : : /* Create enodes and eedges for the function calls that doesn't have an
4191 : : underlying call superedge.
4192 : :
4193 : : Such case occurs when GCC's middle end didn't know which function to
4194 : : call but the analyzer does (with the help of current state).
4195 : :
4196 : : Some example such calls are dynamically dispatched calls to virtual
4197 : : functions or calls that happen via function pointer. */
4198 : :
4199 : : bool
4200 : 154 : exploded_graph::maybe_create_dynamic_call (const gcall &call,
4201 : : tree fn_decl,
4202 : : exploded_node *node,
4203 : : program_state next_state,
4204 : : program_point &next_point,
4205 : : uncertainty_t *uncertainty,
4206 : : logger *logger)
4207 : : {
4208 : 154 : LOG_FUNC (logger);
4209 : :
4210 : 154 : const program_point *this_point = &node->get_point ();
4211 : 154 : function *fun = DECL_STRUCT_FUNCTION (fn_decl);
4212 : 154 : if (fun)
4213 : : {
4214 : 80 : const supergraph &sg = this->get_supergraph ();
4215 : 80 : supernode *sn_entry = sg.get_node_for_function_entry (*fun);
4216 : 80 : supernode *sn_exit = sg.get_node_for_function_exit (*fun);
4217 : :
4218 : 80 : program_point new_point
4219 : 80 : = program_point::before_supernode (sn_entry,
4220 : : nullptr,
4221 : : this_point->get_call_string ());
4222 : :
4223 : 80 : new_point.push_to_call_stack (sn_exit,
4224 : : next_point.get_supernode());
4225 : :
4226 : : /* Impose a maximum recursion depth and don't analyze paths
4227 : : that exceed it further.
4228 : : This is something of a blunt workaround, but it only
4229 : : applies to recursion (and mutual recursion), not to
4230 : : general call stacks. */
4231 : 80 : if (new_point.get_call_string ().calc_recursion_depth ()
4232 : 80 : > param_analyzer_max_recursion_depth)
4233 : : {
4234 : 3 : if (logger)
4235 : 0 : logger->log ("rejecting call edge: recursion limit exceeded");
4236 : 80 : return false;
4237 : : }
4238 : :
4239 : 77 : next_state.push_call (*this, node, call, uncertainty);
4240 : :
4241 : 77 : if (next_state.m_valid)
4242 : : {
4243 : 77 : if (logger)
4244 : 0 : logger->log ("Discovered call to %s [SN: %i -> SN: %i]",
4245 : : function_name(fun),
4246 : 0 : this_point->get_supernode ()->m_index,
4247 : 0 : sn_entry->m_index);
4248 : :
4249 : 77 : exploded_node *enode = get_or_create_node (new_point,
4250 : : next_state,
4251 : : node);
4252 : 77 : if (enode)
4253 : 77 : add_edge (node,enode, nullptr,
4254 : : false, /* No work is done by the call itself. */
4255 : 77 : std::make_unique<dynamic_call_info_t> (call));
4256 : 77 : return true;
4257 : : }
4258 : : }
4259 : : return false;
4260 : 154 : }
4261 : :
4262 : : /* Subclass of path_context for use within exploded_graph::process_node,
4263 : : so that we can split states e.g. at "realloc" calls. */
4264 : :
4265 : : class impl_path_context : public path_context
4266 : : {
4267 : : public:
4268 : 127053 : impl_path_context (const program_state *cur_state,
4269 : : logger *logger)
4270 : 127053 : : m_cur_state (cur_state),
4271 : 127053 : m_logger (logger),
4272 : 127053 : m_terminate_path (false)
4273 : : {
4274 : : }
4275 : :
4276 : 212495 : bool bifurcation_p () const
4277 : : {
4278 : 212495 : return m_custom_eedge_infos.length () > 0;
4279 : : }
4280 : :
4281 : 19322 : const program_state &get_state_at_bifurcation () const
4282 : : {
4283 : 19322 : gcc_assert (m_state_at_bifurcation);
4284 : 19322 : return *m_state_at_bifurcation;
4285 : : }
4286 : :
4287 : : void
4288 : 12836 : bifurcate (std::unique_ptr<custom_edge_info> info) final override
4289 : : {
4290 : 12836 : if (m_logger)
4291 : 0 : m_logger->log ("bifurcating path");
4292 : :
4293 : 12836 : if (m_state_at_bifurcation)
4294 : : /* Verify that the state at bifurcation is consistent when we
4295 : : split into multiple out-edges. */
4296 : 3656 : gcc_assert (*m_state_at_bifurcation == *m_cur_state);
4297 : : else
4298 : : /* Take a copy of the cur_state at the moment when bifurcation
4299 : : happens. */
4300 : 9180 : m_state_at_bifurcation
4301 : 9180 : = std::unique_ptr<program_state> (new program_state (*m_cur_state));
4302 : :
4303 : : /* Take ownership of INFO. */
4304 : 12836 : m_custom_eedge_infos.safe_push (info.release ());
4305 : 12836 : }
4306 : :
4307 : 3421 : void terminate_path () final override
4308 : : {
4309 : 3421 : if (m_logger)
4310 : 0 : m_logger->log ("terminating path");
4311 : 3421 : m_terminate_path = true;
4312 : 3421 : }
4313 : :
4314 : 567937 : bool terminate_path_p () const final override
4315 : : {
4316 : 567937 : return m_terminate_path;
4317 : : }
4318 : :
4319 : : const vec<custom_edge_info *> & get_custom_eedge_infos ()
4320 : : {
4321 : : return m_custom_eedge_infos;
4322 : : }
4323 : :
4324 : : private:
4325 : : const program_state *m_cur_state;
4326 : :
4327 : : logger *m_logger;
4328 : :
4329 : : /* Lazily-created copy of the state before the split. */
4330 : : std::unique_ptr<program_state> m_state_at_bifurcation;
4331 : :
4332 : : auto_vec <custom_edge_info *> m_custom_eedge_infos;
4333 : :
4334 : : bool m_terminate_path;
4335 : : };
4336 : :
4337 : : /* A subclass of pending_diagnostic for complaining about jumps through NULL
4338 : : function pointers. */
4339 : :
4340 : : class jump_through_null : public pending_diagnostic_subclass<jump_through_null>
4341 : : {
4342 : : public:
4343 : 16 : jump_through_null (const gcall &call)
4344 : 16 : : m_call (call)
4345 : : {}
4346 : :
4347 : 151 : const char *get_kind () const final override
4348 : : {
4349 : 151 : return "jump_through_null";
4350 : : }
4351 : :
4352 : 16 : bool operator== (const jump_through_null &other) const
4353 : : {
4354 : 16 : return &m_call == &other.m_call;
4355 : : }
4356 : :
4357 : 32 : int get_controlling_option () const final override
4358 : : {
4359 : 32 : return OPT_Wanalyzer_jump_through_null;
4360 : : }
4361 : :
4362 : 16 : bool emit (diagnostic_emission_context &ctxt) final override
4363 : : {
4364 : 16 : return ctxt.warn ("jump through null pointer");
4365 : : }
4366 : :
4367 : 32 : bool describe_final_event (pretty_printer &pp,
4368 : : const evdesc::final_event &) final override
4369 : : {
4370 : 32 : pp_string (&pp, "jump through null pointer here");
4371 : 32 : return true;
4372 : : }
4373 : :
4374 : : private:
4375 : : const gcall &m_call;
4376 : : };
4377 : :
4378 : : /* The core of exploded_graph::process_worklist (the main analysis loop),
4379 : : handling one node in the worklist.
4380 : :
4381 : : Get successor <point, state> pairs for NODE, calling get_or_create on
4382 : : them, and adding an exploded_edge to each successors.
4383 : :
4384 : : Freshly-created nodes will be added to the worklist. */
4385 : :
4386 : : void
4387 : 339047 : exploded_graph::process_node (exploded_node *node)
4388 : : {
4389 : 339047 : logger * const logger = get_logger ();
4390 : 339047 : LOG_FUNC_1 (logger, "EN: %i", node->m_index);
4391 : :
4392 : 339047 : node->set_status (exploded_node::status::processed);
4393 : :
4394 : 339047 : const program_point &point = node->get_point ();
4395 : :
4396 : : /* Update cfun and input_location in case of an ICE: make it easier to
4397 : : track down which source construct we're failing to handle. */
4398 : 339047 : auto_cfun sentinel (node->get_function ());
4399 : 339047 : const gimple *stmt = point.get_stmt ();
4400 : 339047 : if (stmt)
4401 : 207061 : input_location = stmt->location;
4402 : :
4403 : 339047 : const program_state &state = node->get_state ();
4404 : 339047 : if (logger)
4405 : : {
4406 : 108 : pretty_printer *pp = logger->get_printer ();
4407 : 108 : logger->start_log_line ();
4408 : 108 : pp_string (pp, "point: ");
4409 : 108 : point.print (pp, format (false));
4410 : 108 : pp_string (pp, ", state: ");
4411 : 108 : state.dump_to_pp (m_ext_state, true, false, pp);
4412 : 108 : logger->end_log_line ();
4413 : : }
4414 : :
4415 : 339047 : switch (point.get_kind ())
4416 : : {
4417 : 0 : default:
4418 : 0 : gcc_unreachable ();
4419 : : case PK_ORIGIN:
4420 : : /* This node exists to simplify finding the shortest path
4421 : : to an exploded_node. */
4422 : : break;
4423 : :
4424 : 91803 : case PK_BEFORE_SUPERNODE:
4425 : 91803 : {
4426 : 91803 : program_state next_state (state);
4427 : 91803 : uncertainty_t uncertainty;
4428 : :
4429 : 91803 : if (point.get_from_edge ())
4430 : : {
4431 : 68203 : impl_region_model_context ctxt (*this, node,
4432 : : &state, &next_state,
4433 : 68203 : &uncertainty, nullptr, nullptr);
4434 : 68203 : const cfg_superedge *last_cfg_superedge
4435 : 68203 : = point.get_from_edge ()->dyn_cast_cfg_superedge ();
4436 : 68203 : if (last_cfg_superedge)
4437 : 68203 : next_state.m_region_model->update_for_phis
4438 : 68203 : (node->get_supernode (),
4439 : : last_cfg_superedge,
4440 : : &ctxt);
4441 : 68203 : program_state::detect_leaks (state, next_state, nullptr,
4442 : : get_ext_state (), &ctxt);
4443 : 68203 : }
4444 : :
4445 : 91803 : program_point next_point (point.get_next ());
4446 : 91803 : exploded_node *next = get_or_create_node (next_point, next_state, node);
4447 : 91803 : if (next)
4448 : 91247 : add_edge (node, next, nullptr,
4449 : : false); /* Assume no work is done at phi nodes. */
4450 : 91803 : }
4451 : 91803 : break;
4452 : 127053 : case PK_BEFORE_STMT:
4453 : 127053 : {
4454 : : /* Determine the effect of a run of one or more statements
4455 : : within one supernode, generating an edge to the program_point
4456 : : after the last statement that's processed.
4457 : :
4458 : : Stop iterating statements and thus consolidating into one enode
4459 : : when:
4460 : : - reaching the end of the statements in the supernode
4461 : : - if an sm-state-change occurs (so that it gets its own
4462 : : exploded_node)
4463 : : - if "-fanalyzer-fine-grained" is active
4464 : : - encountering certain statements must appear at the start of
4465 : : their enode (for which stmt_requires_new_enode_p returns true)
4466 : :
4467 : : Update next_state in-place, to get the result of the one
4468 : : or more stmts that are processed.
4469 : :
4470 : : Split the node in-place if an sm-state-change occurs, so that
4471 : : the sm-state-change occurs on an edge where the src enode has
4472 : : exactly one stmt, the one that caused the change. */
4473 : 127053 : program_state next_state (state);
4474 : :
4475 : 127053 : impl_path_context path_ctxt (&next_state, logger);
4476 : :
4477 : 127053 : bool could_have_done_work = false;
4478 : 127053 : uncertainty_t uncertainty;
4479 : 127053 : const supernode *snode = point.get_supernode ();
4480 : 127053 : unsigned stmt_idx;
4481 : 127053 : const gimple *prev_stmt = nullptr;
4482 : 331234 : for (stmt_idx = point.get_stmt_idx ();
4483 : 331234 : stmt_idx < snode->m_stmts.length ();
4484 : : stmt_idx++)
4485 : : {
4486 : 255796 : const gimple *stmt = snode->m_stmts[stmt_idx];
4487 : :
4488 : 255796 : if (stmt_idx > point.get_stmt_idx ())
4489 : 128743 : if (stmt_requires_new_enode_p (stmt, prev_stmt))
4490 : : {
4491 : 8244 : stmt_idx--;
4492 : 42573 : break;
4493 : : }
4494 : 247552 : prev_stmt = stmt;
4495 : :
4496 : 247552 : program_state old_state (next_state);
4497 : :
4498 : : /* Process the stmt. */
4499 : 247552 : exploded_node::on_stmt_flags flags
4500 : 247552 : = node->on_stmt (*this, snode, stmt, &next_state, &uncertainty,
4501 : : &could_have_done_work, &path_ctxt);
4502 : 247552 : node->m_num_processed_stmts++;
4503 : :
4504 : : /* If flags.m_terminate_path, stop analyzing; any nodes/edges
4505 : : will have been added by on_stmt (e.g. for handling longjmp). */
4506 : 247552 : if (flags.m_terminate_path)
4507 : : return;
4508 : :
4509 : 245889 : if (next_state.m_region_model)
4510 : : {
4511 : 245889 : impl_region_model_context ctxt (*this, node,
4512 : : &old_state, &next_state,
4513 : 245889 : &uncertainty, nullptr, stmt);
4514 : 245889 : program_state::detect_leaks (old_state, next_state, nullptr,
4515 : : get_ext_state (), &ctxt);
4516 : 245889 : }
4517 : :
4518 : 245889 : unsigned next_idx = stmt_idx + 1;
4519 : 245889 : program_point next_point
4520 : 328847 : = (next_idx < point.get_supernode ()->m_stmts.length ()
4521 : 245889 : ? program_point::before_stmt (point.get_supernode (), next_idx,
4522 : : point.get_call_string ())
4523 : 82958 : : program_point::after_supernode (point.get_supernode (),
4524 : : point.get_call_string ()));
4525 : 491778 : next_state = next_state.prune_for_point (*this, next_point, node,
4526 : 245889 : &uncertainty);
4527 : :
4528 : 245889 : if (flag_analyzer_fine_grained
4529 : 245170 : || state_change_requires_new_enode_p (old_state, next_state)
4530 : 8288 : || path_ctxt.bifurcation_p ()
4531 : 450096 : || path_ctxt.terminate_path_p ())
4532 : : {
4533 : 41708 : program_point split_point
4534 : 41708 : = program_point::before_stmt (point.get_supernode (),
4535 : : stmt_idx,
4536 : : point.get_call_string ());
4537 : 41708 : if (split_point != node->get_point ())
4538 : : {
4539 : : /* If we're not at the start of NODE, split the enode at
4540 : : this stmt, so we have:
4541 : : node -> split_enode
4542 : : so that when split_enode is processed the next edge
4543 : : we add will be:
4544 : : split_enode -> next
4545 : : and any state change will effectively occur on that
4546 : : latter edge, and split_enode will contain just stmt. */
4547 : 7379 : if (logger)
4548 : 5 : logger->log ("getting split_enode");
4549 : 7379 : exploded_node *split_enode
4550 : 7379 : = get_or_create_node (split_point, old_state, node);
4551 : 7379 : if (!split_enode)
4552 : 7379 : return;
4553 : : /* "stmt" will be reprocessed when split_enode is
4554 : : processed. */
4555 : 7379 : node->m_num_processed_stmts--;
4556 : 7379 : if (logger)
4557 : 5 : logger->log ("creating edge to split_enode");
4558 : 7379 : add_edge (node, split_enode, nullptr, could_have_done_work);
4559 : 7379 : return;
4560 : : }
4561 : : else
4562 : : /* If we're at the start of NODE, stop iterating,
4563 : : so that an edge will be created from NODE to
4564 : : (next_point, next_state) below. */
4565 : : break;
4566 : : }
4567 : 247552 : }
4568 : 118011 : unsigned next_idx = stmt_idx + 1;
4569 : 118011 : program_point next_point
4570 : 199078 : = (next_idx < point.get_supernode ()->m_stmts.length ()
4571 : 118011 : ? program_point::before_stmt (point.get_supernode (), next_idx,
4572 : : point.get_call_string ())
4573 : 81067 : : program_point::after_supernode (point.get_supernode (),
4574 : : point.get_call_string ()));
4575 : 118011 : if (path_ctxt.terminate_path_p ())
4576 : : {
4577 : 1786 : if (logger)
4578 : 0 : logger->log ("not adding node: terminating path");
4579 : : }
4580 : : else
4581 : : {
4582 : 116225 : exploded_node *next
4583 : 116225 : = get_or_create_node (next_point, next_state, node);
4584 : 116225 : if (next)
4585 : 116048 : add_edge (node, next, nullptr, could_have_done_work);
4586 : : }
4587 : :
4588 : : /* If we have custom edge infos, "bifurcate" the state
4589 : : accordingly, potentially creating a new state/enode/eedge
4590 : : instances. For example, to handle a "realloc" call, we
4591 : : might split into 3 states, for the "failure",
4592 : : "resizing in place", and "moving to a new buffer" cases. */
4593 : 142072 : for (auto edge_info_iter : path_ctxt.get_custom_eedge_infos ())
4594 : : {
4595 : : /* Take ownership of the edge infos from the path_ctxt. */
4596 : 9661 : std::unique_ptr<custom_edge_info> edge_info (edge_info_iter);
4597 : 9661 : if (logger)
4598 : : {
4599 : 0 : logger->start_log_line ();
4600 : 0 : logger->log_partial ("bifurcating for edge: ");
4601 : 0 : edge_info->print (logger->get_printer ());
4602 : 0 : logger->end_log_line ();
4603 : : }
4604 : 9661 : program_state bifurcated_new_state
4605 : 9661 : (path_ctxt.get_state_at_bifurcation ());
4606 : :
4607 : : /* Apply edge_info to state. */
4608 : 9661 : impl_region_model_context
4609 : : bifurcation_ctxt (*this,
4610 : : node, // enode_for_diag
4611 : 9661 : &path_ctxt.get_state_at_bifurcation (),
4612 : : &bifurcated_new_state,
4613 : : nullptr, // uncertainty_t *uncertainty
4614 : : nullptr, // path_context *path_ctxt
4615 : 9661 : stmt);
4616 : 9661 : if (edge_info->update_state (&bifurcated_new_state,
4617 : : nullptr, /* no exploded_edge yet. */
4618 : : &bifurcation_ctxt))
4619 : : {
4620 : 9228 : if (exploded_node *next2
4621 : 9228 : = edge_info->create_enode
4622 : 9228 : (*this,
4623 : : next_point,
4624 : : std::move (bifurcated_new_state),
4625 : : node,
4626 : : &bifurcation_ctxt))
4627 : : {
4628 : 8457 : add_edge (node, next2, nullptr,
4629 : : true /* assume that work could be done */,
4630 : : std::move (edge_info));
4631 : : }
4632 : : }
4633 : : else
4634 : : {
4635 : 433 : if (logger)
4636 : 0 : logger->log ("infeasible state, not adding node");
4637 : : }
4638 : 9661 : }
4639 : 127053 : }
4640 : 118011 : break;
4641 : 116872 : case PK_AFTER_SUPERNODE:
4642 : 116872 : {
4643 : 116872 : bool found_a_superedge = false;
4644 : 116872 : bool is_an_exit_block = false;
4645 : : /* If this is an EXIT BB, detect leaks, and potentially
4646 : : create a function summary. */
4647 : 116872 : if (point.get_supernode ()->return_p ())
4648 : : {
4649 : 17091 : is_an_exit_block = true;
4650 : 17091 : node->detect_leaks (*this);
4651 : 17091 : if (flag_analyzer_call_summaries
4652 : 17091 : && point.get_call_string ().empty_p ())
4653 : : {
4654 : : /* TODO: create function summary
4655 : : There can be more than one; each corresponds to a different
4656 : : final enode in the function. */
4657 : 10270 : if (logger)
4658 : : {
4659 : 1 : pretty_printer *pp = logger->get_printer ();
4660 : 1 : logger->start_log_line ();
4661 : 1 : logger->log_partial
4662 : 1 : ("would create function summary for %qE; state: ",
4663 : : point.get_fndecl ());
4664 : 1 : state.dump_to_pp (m_ext_state, true, false, pp);
4665 : 1 : logger->end_log_line ();
4666 : : }
4667 : 10270 : per_function_data *per_fn_data
4668 : 10270 : = get_or_create_per_function_data (point.get_function ());
4669 : 10270 : per_fn_data->add_call_summary (node);
4670 : : }
4671 : : }
4672 : : /* Traverse into successors of the supernode. */
4673 : 116872 : int i;
4674 : 116872 : superedge *succ;
4675 : 270254 : FOR_EACH_VEC_ELT (point.get_supernode ()->m_succs, i, succ)
4676 : : {
4677 : 153382 : found_a_superedge = true;
4678 : 153382 : if (logger)
4679 : : {
4680 : 41 : label_text succ_desc (succ->get_description (false));
4681 : 41 : logger->log ("considering SN: %i -> SN: %i (%s)",
4682 : 41 : succ->m_src->m_index, succ->m_dest->m_index,
4683 : : succ_desc.get ());
4684 : 41 : }
4685 : :
4686 : 153382 : program_point next_point
4687 : 153382 : = program_point::before_supernode (succ->m_dest, succ,
4688 : : point.get_call_string ());
4689 : 153382 : program_state next_state (state);
4690 : 153382 : uncertainty_t uncertainty;
4691 : :
4692 : : /* Make use the current state and try to discover and analyse
4693 : : indirect function calls (a call that doesn't have an underlying
4694 : : cgraph edge representing call).
4695 : :
4696 : : Some examples of such calls are virtual function calls
4697 : : and calls that happen via a function pointer. */
4698 : 153382 : if (succ->m_kind == SUPEREDGE_INTRAPROCEDURAL_CALL
4699 : 153382 : && !(succ->get_any_callgraph_edge ()))
4700 : : {
4701 : 3833 : const gcall &call
4702 : 3833 : = *point.get_supernode ()->get_final_call ();
4703 : :
4704 : 3833 : impl_region_model_context ctxt (*this,
4705 : : node,
4706 : : &state,
4707 : : &next_state,
4708 : : &uncertainty,
4709 : : nullptr,
4710 : 3833 : point.get_stmt());
4711 : :
4712 : 3833 : region_model *model = state.m_region_model;
4713 : 3833 : bool call_discovered = false;
4714 : :
4715 : 3833 : if (tree fn_decl = model->get_fndecl_for_call (call, &ctxt))
4716 : 154 : call_discovered = maybe_create_dynamic_call (call,
4717 : : fn_decl,
4718 : : node,
4719 : : next_state,
4720 : : next_point,
4721 : : &uncertainty,
4722 : : logger);
4723 : 154 : if (!call_discovered)
4724 : : {
4725 : : /* Check for jump through nullptr. */
4726 : 3756 : if (tree fn_ptr = gimple_call_fn (&call))
4727 : : {
4728 : 405 : const svalue *fn_ptr_sval
4729 : 405 : = model->get_rvalue (fn_ptr, &ctxt);
4730 : 405 : if (fn_ptr_sval->all_zeroes_p ())
4731 : 16 : ctxt.warn
4732 : 16 : (std::make_unique<jump_through_null> (call));
4733 : : }
4734 : :
4735 : : /* An unknown function or a special function was called
4736 : : at this point, in such case, don't terminate the
4737 : : analysis of the current function.
4738 : :
4739 : : The analyzer handles calls to such functions while
4740 : : analysing the stmt itself, so the function call
4741 : : must have been handled by the anlyzer till now. */
4742 : 3756 : exploded_node *next
4743 : 3756 : = get_or_create_node (next_point,
4744 : : next_state,
4745 : : node);
4746 : 3756 : if (next)
4747 : 3756 : add_edge (node, next, succ,
4748 : : true /* assume that work is done */);
4749 : : }
4750 : 3833 : }
4751 : :
4752 : : /* Ignore CFG edges in the sgraph flagged with EH whilst
4753 : : we're exploring the egraph.
4754 : : We only use these sedges in special-case logic for
4755 : : dealing with exception-handling. */
4756 : 153382 : if (auto cfg_sedge = succ->dyn_cast_cfg_superedge ())
4757 : 115705 : if (cfg_sedge->get_flags () & EDGE_EH)
4758 : : {
4759 : 2320 : if (logger)
4760 : 0 : logger->log ("rejecting EH edge");
4761 : 2320 : continue;
4762 : : }
4763 : :
4764 : 151062 : if (!node->on_edge (*this, succ, &next_point, &next_state,
4765 : : &uncertainty))
4766 : : {
4767 : 30933 : if (logger)
4768 : 0 : logger->log ("skipping impossible edge to SN: %i",
4769 : 0 : succ->m_dest->m_index);
4770 : 30933 : continue;
4771 : : }
4772 : 120129 : exploded_node *next = get_or_create_node (next_point, next_state,
4773 : : node);
4774 : 120129 : if (next)
4775 : : {
4776 : 120125 : add_edge (node, next, succ, false);
4777 : :
4778 : : /* We might have a function entrypoint. */
4779 : 120125 : detect_infinite_recursion (next);
4780 : : }
4781 : 153382 : }
4782 : :
4783 : : /* Return from the calls which doesn't have a return superedge.
4784 : : Such case occurs when GCC's middle end didn't knew which function to
4785 : : call but analyzer did. */
4786 : 116872 : if ((is_an_exit_block && !found_a_superedge)
4787 : 330072 : && (!point.get_call_string ().empty_p ()))
4788 : : {
4789 : 67 : const call_string &cs = point.get_call_string ();
4790 : 67 : program_point next_point
4791 : 67 : = program_point::before_supernode (cs.get_caller_node (),
4792 : : nullptr,
4793 : : cs);
4794 : 67 : program_state next_state (state);
4795 : 67 : uncertainty_t uncertainty;
4796 : :
4797 : 67 : const gcall *call
4798 : 67 : = next_point.get_supernode ()->get_returning_call ();
4799 : :
4800 : 67 : if (call)
4801 : 67 : next_state.returning_call (*this, node, *call, &uncertainty);
4802 : :
4803 : 67 : if (next_state.m_valid)
4804 : : {
4805 : 67 : next_point.pop_from_call_stack ();
4806 : 67 : exploded_node *enode = get_or_create_node (next_point,
4807 : : next_state,
4808 : : node);
4809 : 67 : if (enode)
4810 : 67 : add_edge (node, enode, nullptr, false,
4811 : 67 : std::make_unique<dynamic_call_info_t> (*call, true));
4812 : : }
4813 : 67 : }
4814 : : }
4815 : : break;
4816 : : }
4817 : 339047 : }
4818 : :
4819 : : /* Ensure that this graph has a stats instance for FN, return it.
4820 : : FN can be nullptr, in which case a stats instances is returned covering
4821 : : "functionless" parts of the graph (the origin node). */
4822 : :
4823 : : stats *
4824 : 394925 : exploded_graph::get_or_create_function_stats (function *fn)
4825 : : {
4826 : 394925 : if (!fn)
4827 : 3323 : return &m_functionless_stats;
4828 : :
4829 : 391602 : if (stats **slot = m_per_function_stats.get (fn))
4830 : 381486 : return *slot;
4831 : : else
4832 : : {
4833 : 10116 : int num_supernodes = fn ? n_basic_blocks_for_fn (fn) : 0;
4834 : : /* not quite the num supernodes, but nearly. */
4835 : 10116 : stats *new_stats = new stats (num_supernodes);
4836 : 10116 : m_per_function_stats.put (fn, new_stats);
4837 : 10116 : return new_stats;
4838 : : }
4839 : : }
4840 : :
4841 : : /* Print bar charts to PP showing:
4842 : : - the number of enodes per function, and
4843 : : - for each function:
4844 : : - the number of enodes per supernode/BB
4845 : : - the number of excess enodes per supernode/BB beyond the
4846 : : per-program-point limit, if there were any. */
4847 : :
4848 : : void
4849 : 2 : exploded_graph::print_bar_charts (pretty_printer *pp) const
4850 : : {
4851 : 2 : cgraph_node *cgnode;
4852 : :
4853 : 2 : pp_string (pp, "enodes per function:");
4854 : 2 : pp_newline (pp);
4855 : 2 : bar_chart enodes_per_function;
4856 : 4 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode)
4857 : : {
4858 : 2 : function *fn = cgnode->get_fun ();
4859 : 2 : const stats * const *s_ptr
4860 : 2 : = const_cast <function_stat_map_t &> (m_per_function_stats).get (fn);
4861 : 4 : enodes_per_function.add_item (function_name (fn),
4862 : 2 : s_ptr ? (*s_ptr)->get_total_enodes () : 0);
4863 : : }
4864 : 2 : enodes_per_function.print (pp);
4865 : :
4866 : : /* Accumulate number of enodes per supernode. */
4867 : 4 : auto_vec<unsigned> enodes_per_supernode (m_sg.num_nodes ());
4868 : 30 : for (int i = 0; i < m_sg.num_nodes (); i++)
4869 : 13 : enodes_per_supernode.quick_push (0);
4870 : : int i;
4871 : : exploded_node *enode;
4872 : 117 : FOR_EACH_VEC_ELT (m_nodes, i, enode)
4873 : : {
4874 : 115 : const supernode *iter_snode = enode->get_supernode ();
4875 : 115 : if (!iter_snode)
4876 : 2 : continue;
4877 : 113 : enodes_per_supernode[iter_snode->m_index]++;
4878 : : }
4879 : :
4880 : : /* Accumulate excess enodes per supernode. */
4881 : 4 : auto_vec<unsigned> excess_enodes_per_supernode (m_sg.num_nodes ());
4882 : 30 : for (int i = 0; i < m_sg.num_nodes (); i++)
4883 : 13 : excess_enodes_per_supernode.quick_push (0);
4884 : 46 : for (point_map_t::iterator iter = m_per_point_data.begin ();
4885 : 90 : iter != m_per_point_data.end (); ++iter)
4886 : : {
4887 : 44 : const program_point *point = (*iter).first;
4888 : 44 : const supernode *iter_snode = point->get_supernode ();
4889 : 44 : if (!iter_snode)
4890 : 2 : continue;
4891 : 42 : const per_program_point_data *point_data = (*iter).second;
4892 : 42 : excess_enodes_per_supernode[iter_snode->m_index]
4893 : 42 : += point_data->m_excess_enodes;
4894 : : }
4895 : :
4896 : : /* Show per-function bar_charts of enodes per supernode/BB. */
4897 : 2 : pp_string (pp, "per-function enodes per supernode/BB:");
4898 : 2 : pp_newline (pp);
4899 : 4 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode)
4900 : : {
4901 : 2 : function *fn = cgnode->get_fun ();
4902 : 2 : pp_printf (pp, "function: %qs", function_name (fn));
4903 : 2 : pp_newline (pp);
4904 : :
4905 : 2 : bar_chart enodes_per_snode;
4906 : 2 : bar_chart excess_enodes_per_snode;
4907 : 2 : bool have_excess_enodes = false;
4908 : 30 : for (int i = 0; i < m_sg.num_nodes (); i++)
4909 : : {
4910 : 13 : const supernode *iter_snode = m_sg.get_node_by_index (i);
4911 : 13 : if (iter_snode->get_function () != fn)
4912 : 0 : continue;
4913 : 13 : pretty_printer tmp_pp;
4914 : 13 : pp_printf (&tmp_pp, "sn %i (bb %i)",
4915 : 13 : iter_snode->m_index, iter_snode->m_bb->index);
4916 : 13 : enodes_per_snode.add_item (pp_formatted_text (&tmp_pp),
4917 : 13 : enodes_per_supernode[iter_snode->m_index]);
4918 : 13 : const int num_excess
4919 : 13 : = excess_enodes_per_supernode[iter_snode->m_index];
4920 : 13 : excess_enodes_per_snode.add_item (pp_formatted_text (&tmp_pp),
4921 : : num_excess);
4922 : 13 : if (num_excess)
4923 : 0 : have_excess_enodes = true;
4924 : 13 : }
4925 : 2 : enodes_per_snode.print (pp);
4926 : 2 : if (have_excess_enodes)
4927 : : {
4928 : 0 : pp_printf (pp, "EXCESS ENODES:");
4929 : 0 : pp_newline (pp);
4930 : 0 : excess_enodes_per_snode.print (pp);
4931 : : }
4932 : 2 : }
4933 : 2 : }
4934 : :
4935 : : /* Write all stats information to this graph's logger, if any. */
4936 : :
4937 : : void
4938 : 3323 : exploded_graph::log_stats () const
4939 : : {
4940 : 3323 : logger * const logger = get_logger ();
4941 : 3323 : if (!logger)
4942 : 3321 : return;
4943 : :
4944 : 2 : LOG_SCOPE (logger);
4945 : :
4946 : 2 : m_ext_state.get_engine ()->log_stats (logger);
4947 : :
4948 : 4 : logger->log ("m_sg.num_nodes (): %i", m_sg.num_nodes ());
4949 : 4 : logger->log ("m_nodes.length (): %i", m_nodes.length ());
4950 : 4 : logger->log ("m_edges.length (): %i", m_edges.length ());
4951 : 2 : logger->log ("remaining enodes in worklist: %i", m_worklist.length ());
4952 : :
4953 : 2 : logger->log ("global stats:");
4954 : 2 : m_global_stats.log (logger);
4955 : :
4956 : 2 : for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
4957 : 8 : iter != m_per_function_stats.end ();
4958 : 2 : ++iter)
4959 : : {
4960 : 2 : function *fn = (*iter).first;
4961 : 2 : log_scope s (logger, function_name (fn));
4962 : 2 : (*iter).second->log (logger);
4963 : 2 : }
4964 : :
4965 : 2 : print_bar_charts (logger->get_printer ());
4966 : 2 : }
4967 : :
4968 : : /* Dump all stats information to OUT. */
4969 : :
4970 : : void
4971 : 0 : exploded_graph::dump_stats (FILE *out) const
4972 : : {
4973 : 0 : fprintf (out, "m_sg.num_nodes (): %i\n", m_sg.num_nodes ());
4974 : 0 : fprintf (out, "m_nodes.length (): %i\n", m_nodes.length ());
4975 : 0 : fprintf (out, "m_edges.length (): %i\n", m_edges.length ());
4976 : 0 : fprintf (out, "remaining enodes in worklist: %i", m_worklist.length ());
4977 : :
4978 : 0 : fprintf (out, "global stats:\n");
4979 : 0 : m_global_stats.dump (out);
4980 : :
4981 : 0 : for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
4982 : 0 : iter != m_per_function_stats.end ();
4983 : 0 : ++iter)
4984 : : {
4985 : 0 : function *fn = (*iter).first;
4986 : 0 : fprintf (out, "function: %s\n", function_name (fn));
4987 : 0 : (*iter).second->dump (out);
4988 : : }
4989 : :
4990 : 0 : fprintf (out, "PK_AFTER_SUPERNODE per supernode:\n");
4991 : 0 : for (unsigned i = 0; i < m_PK_AFTER_SUPERNODE_per_snode.length (); i++)
4992 : 0 : fprintf (out, " SN %i: %3i\n", i, m_PK_AFTER_SUPERNODE_per_snode[i]);
4993 : 0 : }
4994 : :
4995 : : void
4996 : 0 : exploded_graph::dump_states_for_supernode (FILE *out,
4997 : : const supernode *snode) const
4998 : : {
4999 : 0 : fprintf (out, "PK_AFTER_SUPERNODE nodes for SN: %i\n", snode->m_index);
5000 : 0 : int i;
5001 : 0 : exploded_node *enode;
5002 : 0 : int state_idx = 0;
5003 : 0 : FOR_EACH_VEC_ELT (m_nodes, i, enode)
5004 : : {
5005 : 0 : const supernode *iter_snode = enode->get_supernode ();
5006 : 0 : if (enode->get_point ().get_kind () == PK_AFTER_SUPERNODE
5007 : 0 : && iter_snode == snode)
5008 : : {
5009 : 0 : pretty_printer pp;
5010 : 0 : pp_format_decoder (&pp) = default_tree_printer;
5011 : 0 : enode->get_state ().dump_to_pp (m_ext_state, true, false, &pp);
5012 : 0 : fprintf (out, "state %i: EN: %i\n %s\n",
5013 : 0 : state_idx++, enode->m_index,
5014 : : pp_formatted_text (&pp));
5015 : 0 : }
5016 : : }
5017 : 0 : fprintf (out, "#exploded_node for PK_AFTER_SUPERNODE for SN: %i = %i\n",
5018 : 0 : snode->m_index, state_idx);
5019 : 0 : }
5020 : :
5021 : : /* Return a new json::object of the form
5022 : : {"nodes" : [objs for enodes],
5023 : : "edges" : [objs for eedges],
5024 : : "ext_state": object for extrinsic_state,
5025 : : "diagnostic_manager": object for diagnostic_manager}. */
5026 : :
5027 : : std::unique_ptr<json::object>
5028 : 0 : exploded_graph::to_json () const
5029 : : {
5030 : 0 : auto egraph_obj = std::make_unique<json::object> ();
5031 : :
5032 : : /* Nodes. */
5033 : 0 : {
5034 : 0 : auto nodes_arr = std::make_unique<json::array> ();
5035 : 0 : unsigned i;
5036 : 0 : exploded_node *n;
5037 : 0 : FOR_EACH_VEC_ELT (m_nodes, i, n)
5038 : 0 : nodes_arr->append (n->to_json (m_ext_state));
5039 : 0 : egraph_obj->set ("nodes", std::move (nodes_arr));
5040 : 0 : }
5041 : :
5042 : : /* Edges. */
5043 : 0 : {
5044 : 0 : auto edges_arr = std::make_unique<json::array> ();
5045 : 0 : unsigned i;
5046 : 0 : exploded_edge *n;
5047 : 0 : FOR_EACH_VEC_ELT (m_edges, i, n)
5048 : 0 : edges_arr->append (n->to_json ());
5049 : 0 : egraph_obj->set ("edges", std::move (edges_arr));
5050 : 0 : }
5051 : :
5052 : : /* m_sg is JSONified at the top-level. */
5053 : :
5054 : 0 : egraph_obj->set ("ext_state", m_ext_state.to_json ());
5055 : 0 : egraph_obj->set ("worklist", m_worklist.to_json ());
5056 : 0 : egraph_obj->set ("diagnostic_manager", m_diagnostic_manager.to_json ());
5057 : :
5058 : : /* The following fields aren't yet being JSONified:
5059 : : const state_purge_map *const m_purge_map;
5060 : : const analysis_plan &m_plan;
5061 : : stats m_global_stats;
5062 : : function_stat_map_t m_per_function_stats;
5063 : : stats m_functionless_stats;
5064 : : call_string_data_map_t m_per_call_string_data;
5065 : : auto_vec<int> m_PK_AFTER_SUPERNODE_per_snode; */
5066 : :
5067 : 0 : return egraph_obj;
5068 : : }
5069 : :
5070 : : /* class exploded_path. */
5071 : :
5072 : : /* Copy ctor. */
5073 : :
5074 : 4 : exploded_path::exploded_path (const exploded_path &other)
5075 : 8 : : m_edges (other.m_edges.length ())
5076 : : {
5077 : 4 : int i;
5078 : 4 : const exploded_edge *eedge;
5079 : 75 : FOR_EACH_VEC_ELT (other.m_edges, i, eedge)
5080 : 67 : m_edges.quick_push (eedge);
5081 : 4 : }
5082 : :
5083 : : /* Look for the last use of SEARCH_STMT within this path.
5084 : : If found write the edge's index to *OUT_IDX and return true, otherwise
5085 : : return false. */
5086 : :
5087 : : bool
5088 : 796 : exploded_path::find_stmt_backwards (const gimple *search_stmt,
5089 : : int *out_idx) const
5090 : : {
5091 : 796 : int i;
5092 : 796 : const exploded_edge *eedge;
5093 : 19498 : FOR_EACH_VEC_ELT_REVERSE (m_edges, i, eedge)
5094 : : {
5095 : 18640 : const exploded_node *dst_node = eedge->m_dest;
5096 : 18640 : const program_point &dst_point = dst_node->get_point ();
5097 : 18640 : const gimple *stmt = dst_point.get_stmt ();
5098 : 18640 : if (stmt == search_stmt)
5099 : : {
5100 : 734 : *out_idx = i;
5101 : 734 : return true;
5102 : : }
5103 : : }
5104 : : return false;
5105 : : }
5106 : :
5107 : : /* Get the final exploded_node in this path, which must be non-empty. */
5108 : :
5109 : : exploded_node *
5110 : 11916 : exploded_path::get_final_enode () const
5111 : : {
5112 : 11916 : gcc_assert (m_edges.length () > 0);
5113 : 11916 : return m_edges[m_edges.length () - 1]->m_dest;
5114 : : }
5115 : :
5116 : : /* Check state along this path, returning true if it is feasible.
5117 : : If OUT is non-NULL, and the path is infeasible, write a new
5118 : : feasibility_problem to *OUT. */
5119 : :
5120 : : bool
5121 : 4 : exploded_path::feasible_p (logger *logger,
5122 : : std::unique_ptr<feasibility_problem> *out,
5123 : : engine *eng, const exploded_graph *eg) const
5124 : : {
5125 : 4 : LOG_SCOPE (logger);
5126 : :
5127 : 4 : feasibility_state state (eng->get_model_manager (),
5128 : 4 : eg->get_supergraph ());
5129 : :
5130 : : /* Traverse the path, updating this state. */
5131 : 63 : for (unsigned edge_idx = 0; edge_idx < m_edges.length (); edge_idx++)
5132 : : {
5133 : 63 : const exploded_edge *eedge = m_edges[edge_idx];
5134 : 63 : if (logger)
5135 : 0 : logger->log ("considering edge %i: EN:%i -> EN:%i",
5136 : : edge_idx,
5137 : 0 : eedge->m_src->m_index,
5138 : 0 : eedge->m_dest->m_index);
5139 : :
5140 : 63 : std::unique_ptr <rejected_constraint> rc;
5141 : 63 : if (!state.maybe_update_for_edge (logger, eedge, nullptr, &rc))
5142 : : {
5143 : 4 : gcc_assert (rc);
5144 : 4 : if (out)
5145 : : {
5146 : 4 : const exploded_node &src_enode = *eedge->m_src;
5147 : 4 : const program_point &src_point = src_enode.get_point ();
5148 : 4 : const gimple *last_stmt
5149 : 4 : = src_point.get_supernode ()->get_last_stmt ();
5150 : 8 : *out = std::make_unique<feasibility_problem> (edge_idx, *eedge,
5151 : : last_stmt,
5152 : 4 : std::move (rc));
5153 : : }
5154 : 4 : return false;
5155 : : }
5156 : :
5157 : 59 : if (logger)
5158 : : {
5159 : 0 : logger->log ("state after edge %i: EN:%i -> EN:%i",
5160 : : edge_idx,
5161 : 0 : eedge->m_src->m_index,
5162 : 0 : eedge->m_dest->m_index);
5163 : 0 : logger->start_log_line ();
5164 : 0 : state.get_model ().dump_to_pp (logger->get_printer (), true, false);
5165 : 0 : logger->end_log_line ();
5166 : : }
5167 : 63 : }
5168 : :
5169 : 0 : return true;
5170 : 4 : }
5171 : :
5172 : : /* Dump this path in multiline form to PP.
5173 : : If EXT_STATE is non-NULL, then show the nodes. */
5174 : :
5175 : : void
5176 : 0 : exploded_path::dump_to_pp (pretty_printer *pp,
5177 : : const extrinsic_state *ext_state) const
5178 : : {
5179 : 0 : for (unsigned i = 0; i < m_edges.length (); i++)
5180 : : {
5181 : 0 : const exploded_edge *eedge = m_edges[i];
5182 : 0 : pp_printf (pp, "m_edges[%i]: EN %i -> EN %i",
5183 : : i,
5184 : 0 : eedge->m_src->m_index,
5185 : 0 : eedge->m_dest->m_index);
5186 : 0 : pp_newline (pp);
5187 : :
5188 : 0 : if (ext_state)
5189 : 0 : eedge->m_dest->dump_to_pp (pp, *ext_state);
5190 : : }
5191 : 0 : }
5192 : :
5193 : : /* Dump this path in multiline form to FP. */
5194 : :
5195 : : void
5196 : 0 : exploded_path::dump (FILE *fp, const extrinsic_state *ext_state) const
5197 : : {
5198 : 0 : tree_dump_pretty_printer pp (fp);
5199 : 0 : dump_to_pp (&pp, ext_state);
5200 : 0 : }
5201 : :
5202 : : /* Dump this path in multiline form to stderr. */
5203 : :
5204 : : DEBUG_FUNCTION void
5205 : 0 : exploded_path::dump (const extrinsic_state *ext_state) const
5206 : : {
5207 : 0 : dump (stderr, ext_state);
5208 : 0 : }
5209 : :
5210 : : /* Dump this path verbosely to FILENAME. */
5211 : :
5212 : : void
5213 : 0 : exploded_path::dump_to_file (const char *filename,
5214 : : const extrinsic_state &ext_state) const
5215 : : {
5216 : 0 : FILE *fp = fopen (filename, "w");
5217 : 0 : if (!fp)
5218 : 0 : return;
5219 : 0 : pretty_printer pp;
5220 : 0 : pp_format_decoder (&pp) = default_tree_printer;
5221 : 0 : pp.set_output_stream (fp);
5222 : 0 : dump_to_pp (&pp, &ext_state);
5223 : 0 : pp_flush (&pp);
5224 : 0 : fclose (fp);
5225 : 0 : }
5226 : :
5227 : : /* class feasibility_problem. */
5228 : :
5229 : : void
5230 : 4 : feasibility_problem::dump_to_pp (pretty_printer *pp) const
5231 : : {
5232 : 4 : pp_printf (pp, "edge from EN: %i to EN: %i",
5233 : 4 : m_eedge.m_src->m_index, m_eedge.m_dest->m_index);
5234 : 4 : if (m_rc)
5235 : : {
5236 : 4 : pp_string (pp, "; rejected constraint: ");
5237 : 4 : m_rc->dump_to_pp (pp);
5238 : 4 : pp_string (pp, "; rmodel: ");
5239 : 4 : m_rc->get_model ().dump_to_pp (pp, true, false);
5240 : : }
5241 : 4 : }
5242 : :
5243 : : /* class feasibility_state. */
5244 : :
5245 : : /* Ctor for feasibility_state, at the beginning of a path. */
5246 : :
5247 : 6965 : feasibility_state::feasibility_state (region_model_manager *manager,
5248 : 6965 : const supergraph &sg)
5249 : 6965 : : m_model (manager),
5250 : 13930 : m_snodes_visited (sg.m_nodes.length ())
5251 : : {
5252 : 6965 : bitmap_clear (m_snodes_visited);
5253 : 6965 : }
5254 : :
5255 : : /* Copy ctor for feasibility_state, for extending a path. */
5256 : :
5257 : 410232 : feasibility_state::feasibility_state (const feasibility_state &other)
5258 : 410232 : : m_model (other.m_model),
5259 : 410232 : m_snodes_visited (const_sbitmap (other.m_snodes_visited)->n_bits)
5260 : : {
5261 : 410232 : bitmap_copy (m_snodes_visited, other.m_snodes_visited);
5262 : 410232 : }
5263 : :
5264 : 5093 : feasibility_state::feasibility_state (const region_model &model,
5265 : 5093 : const supergraph &sg)
5266 : 5093 : : m_model (model),
5267 : 10186 : m_snodes_visited (sg.m_nodes.length ())
5268 : : {
5269 : 5093 : bitmap_clear (m_snodes_visited);
5270 : 5093 : }
5271 : :
5272 : : feasibility_state &
5273 : 16663 : feasibility_state::operator= (const feasibility_state &other)
5274 : : {
5275 : 16663 : m_model = other.m_model;
5276 : 16663 : bitmap_copy (m_snodes_visited, other.m_snodes_visited);
5277 : 16663 : return *this;
5278 : : }
5279 : :
5280 : : /* The heart of feasibility-checking.
5281 : :
5282 : : Attempt to update this state in-place based on traversing EEDGE
5283 : : in a path.
5284 : : Update the model for the stmts in the src enode.
5285 : : Attempt to add constraints for EEDGE.
5286 : :
5287 : : If feasible, return true.
5288 : : Otherwise, return false and write to *OUT_RC. */
5289 : :
5290 : : bool
5291 : 194828 : feasibility_state::
5292 : : maybe_update_for_edge (logger *logger,
5293 : : const exploded_edge *eedge,
5294 : : region_model_context *ctxt,
5295 : : std::unique_ptr<rejected_constraint> *out_rc)
5296 : : {
5297 : 194828 : const exploded_node &src_enode = *eedge->m_src;
5298 : 194828 : const program_point &src_point = src_enode.get_point ();
5299 : 194828 : if (logger)
5300 : : {
5301 : 27 : logger->start_log_line ();
5302 : 27 : src_point.print (logger->get_printer (), format (false));
5303 : 27 : logger->end_log_line ();
5304 : : }
5305 : :
5306 : : /* Update state for the stmts that were processed in each enode. */
5307 : 309886 : for (unsigned stmt_idx = 0; stmt_idx < src_enode.m_num_processed_stmts;
5308 : : stmt_idx++)
5309 : : {
5310 : 115058 : const gimple *stmt = src_enode.get_processed_stmt (stmt_idx);
5311 : :
5312 : : /* Update cfun and input_location in case of ICE: make it easier to
5313 : : track down which source construct we're failing to handle. */
5314 : 115058 : auto_cfun sentinel (src_point.get_function ());
5315 : 115058 : input_location = stmt->location;
5316 : :
5317 : 115058 : update_for_stmt (stmt);
5318 : : }
5319 : :
5320 : 194828 : const superedge *sedge = eedge->m_sedge;
5321 : 194828 : if (sedge)
5322 : : {
5323 : 60074 : if (logger)
5324 : : {
5325 : 6 : label_text desc (sedge->get_description (false));
5326 : 6 : logger->log (" sedge: SN:%i -> SN:%i %s",
5327 : 6 : sedge->m_src->m_index,
5328 : 6 : sedge->m_dest->m_index,
5329 : : desc.get ());
5330 : 6 : }
5331 : :
5332 : 60074 : const gimple *last_stmt = src_point.get_supernode ()->get_last_stmt ();
5333 : 60074 : if (!m_model.maybe_update_for_edge (*sedge, last_stmt, ctxt, out_rc))
5334 : : {
5335 : 5853 : if (logger)
5336 : : {
5337 : 6 : logger->start_log_line ();
5338 : 6 : logger->log_partial ("rejecting due to region model: ");
5339 : 6 : m_model.dump_to_pp (logger->get_printer (), true, false);
5340 : 6 : logger->end_log_line ();
5341 : : }
5342 : 5853 : return false;
5343 : : }
5344 : : }
5345 : : else
5346 : : {
5347 : : /* Special-case the initial eedge from the origin node to the
5348 : : initial function by pushing a frame for it. */
5349 : 134754 : if (src_point.get_kind () == PK_ORIGIN)
5350 : : {
5351 : 6965 : gcc_assert (eedge->m_src->m_index == 0);
5352 : 6965 : gcc_assert (eedge->m_dest->get_point ().get_kind ()
5353 : : == PK_BEFORE_SUPERNODE);
5354 : 6965 : function *fun = eedge->m_dest->get_function ();
5355 : 6965 : gcc_assert (fun);
5356 : 6965 : m_model.push_frame (*fun, nullptr, nullptr, ctxt);
5357 : 6965 : if (logger)
5358 : 0 : logger->log (" pushing frame for %qD", fun->decl);
5359 : : }
5360 : 127789 : else if (eedge->m_custom_info)
5361 : : {
5362 : 2877 : eedge->m_custom_info->update_model (&m_model, eedge, ctxt);
5363 : : }
5364 : : }
5365 : :
5366 : : /* Handle phi nodes on an edge leaving a PK_BEFORE_SUPERNODE (to
5367 : : a PK_BEFORE_STMT, or a PK_AFTER_SUPERNODE if no stmts).
5368 : : This will typically not be associated with a superedge. */
5369 : 188975 : if (src_point.get_from_edge ())
5370 : : {
5371 : 47505 : const cfg_superedge *last_cfg_superedge
5372 : 47505 : = src_point.get_from_edge ()->dyn_cast_cfg_superedge ();
5373 : 47505 : const exploded_node &dst_enode = *eedge->m_dest;
5374 : 47505 : const unsigned dst_snode_idx = dst_enode.get_supernode ()->m_index;
5375 : 47505 : if (last_cfg_superedge)
5376 : : {
5377 : 47505 : if (logger)
5378 : 9 : logger->log (" update for phis");
5379 : 47505 : m_model.update_for_phis (src_enode.get_supernode (),
5380 : : last_cfg_superedge,
5381 : : ctxt);
5382 : : /* If we've entering an snode that we've already visited on this
5383 : : epath, then we need do fix things up for loops; see the
5384 : : comment for store::loop_replay_fixup.
5385 : : Perhaps we should probably also verify the callstring,
5386 : : and track program_points, but hopefully doing it by supernode
5387 : : is good enough. */
5388 : 47505 : if (bitmap_bit_p (m_snodes_visited, dst_snode_idx))
5389 : 8754 : m_model.loop_replay_fixup (dst_enode.get_state ().m_region_model);
5390 : : }
5391 : 47505 : bitmap_set_bit (m_snodes_visited, dst_snode_idx);
5392 : : }
5393 : : return true;
5394 : : }
5395 : :
5396 : : /* Update this object for the effects of STMT. */
5397 : :
5398 : : void
5399 : 116954 : feasibility_state::update_for_stmt (const gimple *stmt)
5400 : : {
5401 : 116954 : if (const gassign *assign = dyn_cast <const gassign *> (stmt))
5402 : 59000 : m_model.on_assignment (assign, nullptr);
5403 : 57954 : else if (const gasm *asm_stmt = dyn_cast <const gasm *> (stmt))
5404 : 42 : m_model.on_asm_stmt (asm_stmt, nullptr);
5405 : 57912 : else if (const gcall *call = dyn_cast <const gcall *> (stmt))
5406 : : {
5407 : 29126 : bool unknown_side_effects = m_model.on_call_pre (*call, nullptr);
5408 : 29126 : m_model.on_call_post (*call, unknown_side_effects, nullptr);
5409 : : }
5410 : 28786 : else if (const greturn *return_ = dyn_cast <const greturn *> (stmt))
5411 : 3029 : m_model.on_return (return_, nullptr);
5412 : 116954 : }
5413 : :
5414 : : /* Dump this object to PP. */
5415 : :
5416 : : void
5417 : 128 : feasibility_state::dump_to_pp (pretty_printer *pp,
5418 : : bool simple, bool multiline) const
5419 : : {
5420 : 128 : m_model.dump_to_pp (pp, simple, multiline);
5421 : 128 : }
5422 : :
5423 : : /* A family of cluster subclasses for use when generating .dot output for
5424 : : exploded graphs (-fdump-analyzer-exploded-graph), for grouping the
5425 : : enodes into hierarchical boxes.
5426 : :
5427 : : All functionless enodes appear in the top-level graph.
5428 : : Every (function, call_string) pair gets its own cluster. Within that
5429 : : cluster, each supernode gets its own cluster.
5430 : :
5431 : : Hence all enodes relating to a particular function with a particular
5432 : : callstring will be in a cluster together; all enodes for the same
5433 : : function but with a different callstring will be in a different
5434 : : cluster. */
5435 : :
5436 : : /* Base class of cluster for clustering exploded_node instances in .dot
5437 : : output, based on various subclass-specific criteria. */
5438 : :
5439 : 1456 : class exploded_cluster : public cluster<eg_traits>
5440 : : {
5441 : : };
5442 : :
5443 : : /* Cluster containing all exploded_node instances for one supernode. */
5444 : :
5445 : : class supernode_cluster : public exploded_cluster
5446 : : {
5447 : : public:
5448 : 484 : supernode_cluster (const supernode *supernode) : m_supernode (supernode) {}
5449 : :
5450 : : // TODO: dtor?
5451 : :
5452 : 484 : void dump_dot (graphviz_out *gv, const dump_args_t &args) const final override
5453 : : {
5454 : 484 : gv->println ("subgraph \"cluster_supernode_%i\" {", m_supernode->m_index);
5455 : 484 : gv->indent ();
5456 : 484 : gv->println ("style=\"dashed\";");
5457 : 968 : gv->println ("label=\"SN: %i (bb: %i; scc: %i)\";",
5458 : 484 : m_supernode->m_index, m_supernode->m_bb->index,
5459 : 484 : args.m_eg.get_scc_id (*m_supernode));
5460 : :
5461 : 484 : int i;
5462 : 484 : exploded_node *enode;
5463 : 1452 : FOR_EACH_VEC_ELT (m_enodes, i, enode)
5464 : 484 : enode->dump_dot (gv, args);
5465 : :
5466 : : /* Terminate subgraph. */
5467 : 484 : gv->outdent ();
5468 : 484 : gv->println ("}");
5469 : 484 : }
5470 : :
5471 : 484 : void add_node (exploded_node *en) final override
5472 : : {
5473 : 0 : m_enodes.safe_push (en);
5474 : 0 : }
5475 : :
5476 : : /* Comparator for use by auto_vec<supernode_cluster *>::qsort. */
5477 : :
5478 : 0 : static int cmp_ptr_ptr (const void *p1, const void *p2)
5479 : : {
5480 : 0 : const supernode_cluster *c1
5481 : : = *(const supernode_cluster * const *)p1;
5482 : 0 : const supernode_cluster *c2
5483 : : = *(const supernode_cluster * const *)p2;
5484 : 0 : return c1->m_supernode->m_index - c2->m_supernode->m_index;
5485 : : }
5486 : :
5487 : : private:
5488 : : const supernode *m_supernode;
5489 : : auto_vec <exploded_node *> m_enodes;
5490 : : };
5491 : :
5492 : : /* Cluster containing all supernode_cluster instances for one
5493 : : (function, call_string) pair. */
5494 : :
5495 : : class function_call_string_cluster : public exploded_cluster
5496 : : {
5497 : : public:
5498 : 484 : function_call_string_cluster (function *fun, const call_string &cs)
5499 : 484 : : m_fun (fun), m_cs (cs) {}
5500 : :
5501 : 968 : ~function_call_string_cluster ()
5502 : 484 : {
5503 : 484 : for (map_t::iterator iter = m_map.begin ();
5504 : 1936 : iter != m_map.end ();
5505 : 484 : ++iter)
5506 : 484 : delete (*iter).second;
5507 : 968 : }
5508 : :
5509 : 484 : void dump_dot (graphviz_out *gv, const dump_args_t &args) const final override
5510 : : {
5511 : 484 : const char *funcname = function_name (m_fun);
5512 : :
5513 : 968 : gv->println ("subgraph \"cluster_function_%s\" {",
5514 : 484 : IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (m_fun->decl)));
5515 : 484 : gv->indent ();
5516 : 484 : gv->write_indent ();
5517 : 484 : gv->print ("label=\"call string: ");
5518 : 484 : m_cs.print (gv->get_pp ());
5519 : 484 : gv->print (" function: %s \";", funcname);
5520 : 484 : gv->print ("\n");
5521 : :
5522 : : /* Dump m_map, sorting it to avoid churn when comparing dumps. */
5523 : 484 : auto_vec<supernode_cluster *> child_clusters (m_map.elements ());
5524 : 484 : for (map_t::iterator iter = m_map.begin ();
5525 : 1936 : iter != m_map.end ();
5526 : 484 : ++iter)
5527 : 484 : child_clusters.quick_push ((*iter).second);
5528 : :
5529 : 484 : child_clusters.qsort (supernode_cluster::cmp_ptr_ptr);
5530 : :
5531 : : unsigned i;
5532 : : supernode_cluster *child_cluster;
5533 : 968 : FOR_EACH_VEC_ELT (child_clusters, i, child_cluster)
5534 : 484 : child_cluster->dump_dot (gv, args);
5535 : :
5536 : : /* Terminate subgraph. */
5537 : 484 : gv->outdent ();
5538 : 484 : gv->println ("}");
5539 : 484 : }
5540 : :
5541 : 484 : void add_node (exploded_node *en) final override
5542 : : {
5543 : 484 : const supernode *supernode = en->get_supernode ();
5544 : 484 : gcc_assert (supernode);
5545 : 484 : supernode_cluster **slot = m_map.get (supernode);
5546 : 484 : if (slot)
5547 : 0 : (*slot)->add_node (en);
5548 : : else
5549 : : {
5550 : 484 : supernode_cluster *child = new supernode_cluster (supernode);
5551 : 484 : m_map.put (supernode, child);
5552 : 484 : child->add_node (en);
5553 : : }
5554 : 484 : }
5555 : :
5556 : : /* Comparator for use by auto_vec<function_call_string_cluster *>. */
5557 : :
5558 : 20530 : static int cmp_ptr_ptr (const void *p1, const void *p2)
5559 : : {
5560 : 20530 : const function_call_string_cluster *c1
5561 : : = *(const function_call_string_cluster * const *)p1;
5562 : 20530 : const function_call_string_cluster *c2
5563 : : = *(const function_call_string_cluster * const *)p2;
5564 : 41060 : if (int cmp_names
5565 : 20530 : = strcmp (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (c1->m_fun->decl)),
5566 : 20530 : IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (c2->m_fun->decl))))
5567 : : return cmp_names;
5568 : 7391 : return call_string::cmp (c1->m_cs, c2->m_cs);
5569 : : }
5570 : :
5571 : : private:
5572 : : function *m_fun;
5573 : : const call_string &m_cs;
5574 : : typedef ordered_hash_map<const supernode *, supernode_cluster *> map_t;
5575 : : map_t m_map;
5576 : : };
5577 : :
5578 : : /* Keys for root_cluster. */
5579 : :
5580 : : struct function_call_string
5581 : : {
5582 : 484 : function_call_string (function *fun, const call_string *cs)
5583 : 484 : : m_fun (fun), m_cs (cs)
5584 : : {
5585 : 484 : gcc_assert (fun);
5586 : 484 : gcc_assert (cs);
5587 : 484 : }
5588 : :
5589 : : function *m_fun;
5590 : : const call_string *m_cs;
5591 : : };
5592 : :
5593 : : } // namespace ana
5594 : :
5595 : : template <> struct default_hash_traits<function_call_string>
5596 : : : public pod_hash_traits<function_call_string>
5597 : : {
5598 : : static const bool empty_zero_p = false;
5599 : : };
5600 : :
5601 : : template <>
5602 : : inline hashval_t
5603 : 6795 : pod_hash_traits<function_call_string>::hash (value_type v)
5604 : : {
5605 : 6795 : return (pointer_hash <function>::hash (v.m_fun)
5606 : 6795 : ^ pointer_hash <const call_string>::hash (v.m_cs));
5607 : : }
5608 : :
5609 : : template <>
5610 : : inline bool
5611 : 29625 : pod_hash_traits<function_call_string>::equal (const value_type &existing,
5612 : : const value_type &candidate)
5613 : : {
5614 : 29625 : return existing.m_fun == candidate.m_fun && &existing.m_cs == &candidate.m_cs;
5615 : : }
5616 : : template <>
5617 : : inline void
5618 : : pod_hash_traits<function_call_string>::mark_deleted (value_type &v)
5619 : : {
5620 : : v.m_fun = reinterpret_cast<function *> (1);
5621 : : }
5622 : : template <>
5623 : : inline void
5624 : 1932 : pod_hash_traits<function_call_string>::mark_empty (value_type &v)
5625 : : {
5626 : 1932 : v.m_fun = nullptr;
5627 : : }
5628 : : template <>
5629 : : inline bool
5630 : 46714 : pod_hash_traits<function_call_string>::is_deleted (value_type v)
5631 : : {
5632 : 46714 : return v.m_fun == reinterpret_cast<function *> (1);
5633 : : }
5634 : : template <>
5635 : : inline bool
5636 : 103861 : pod_hash_traits<function_call_string>::is_empty (value_type v)
5637 : : {
5638 : 103377 : return v.m_fun == nullptr;
5639 : : }
5640 : :
5641 : : namespace ana {
5642 : :
5643 : : /* Top-level cluster for generating .dot output for exploded graphs,
5644 : : handling the functionless nodes, and grouping the remaining nodes by
5645 : : callstring. */
5646 : :
5647 : : class root_cluster : public exploded_cluster
5648 : : {
5649 : : public:
5650 : 4 : ~root_cluster ()
5651 : 4 : {
5652 : 488 : for (map_t::iterator iter = m_map.begin ();
5653 : 488 : iter != m_map.end ();
5654 : 484 : ++iter)
5655 : 484 : delete (*iter).second;
5656 : 4 : }
5657 : :
5658 : 4 : void dump_dot (graphviz_out *gv, const dump_args_t &args) const final override
5659 : : {
5660 : 4 : int i;
5661 : 4 : exploded_node *enode;
5662 : 8 : FOR_EACH_VEC_ELT (m_functionless_enodes, i, enode)
5663 : 4 : enode->dump_dot (gv, args);
5664 : :
5665 : : /* Dump m_map, sorting it to avoid churn when comparing dumps. */
5666 : 4 : auto_vec<function_call_string_cluster *> child_clusters (m_map.elements ());
5667 : 4 : for (map_t::iterator iter = m_map.begin ();
5668 : 488 : iter != m_map.end ();
5669 : 484 : ++iter)
5670 : 484 : child_clusters.quick_push ((*iter).second);
5671 : :
5672 : 4 : child_clusters.qsort (function_call_string_cluster::cmp_ptr_ptr);
5673 : :
5674 : : function_call_string_cluster *child_cluster;
5675 : 492 : FOR_EACH_VEC_ELT (child_clusters, i, child_cluster)
5676 : 484 : child_cluster->dump_dot (gv, args);
5677 : 4 : }
5678 : :
5679 : 488 : void add_node (exploded_node *en) final override
5680 : : {
5681 : 488 : function *fun = en->get_function ();
5682 : 488 : if (!fun)
5683 : : {
5684 : 4 : m_functionless_enodes.safe_push (en);
5685 : 4 : return;
5686 : : }
5687 : :
5688 : 484 : const call_string &cs = en->get_point ().get_call_string ();
5689 : 484 : function_call_string key (fun, &cs);
5690 : 484 : function_call_string_cluster **slot = m_map.get (key);
5691 : 484 : if (slot)
5692 : 0 : (*slot)->add_node (en);
5693 : : else
5694 : : {
5695 : 484 : function_call_string_cluster *child
5696 : 484 : = new function_call_string_cluster (fun, cs);
5697 : 484 : m_map.put (key, child);
5698 : 484 : child->add_node (en);
5699 : : }
5700 : : }
5701 : :
5702 : : private:
5703 : : typedef hash_map<function_call_string, function_call_string_cluster *> map_t;
5704 : : map_t m_map;
5705 : :
5706 : : /* This should just be the origin exploded_node. */
5707 : : auto_vec <exploded_node *> m_functionless_enodes;
5708 : : };
5709 : :
5710 : : /* Subclass of range_label for use within
5711 : : exploded_graph::dump_exploded_nodes for implementing
5712 : : -fdump-analyzer-exploded-nodes: a label for a specific
5713 : : exploded_node. */
5714 : :
5715 : : class enode_label : public range_label
5716 : : {
5717 : : public:
5718 : 0 : enode_label (const extrinsic_state &ext_state,
5719 : : exploded_node *enode)
5720 : 0 : : m_ext_state (ext_state), m_enode (enode) {}
5721 : :
5722 : 0 : label_text get_text (unsigned) const final override
5723 : : {
5724 : 0 : pretty_printer pp;
5725 : 0 : pp_format_decoder (&pp) = default_tree_printer;
5726 : 0 : m_enode->get_state ().dump_to_pp (m_ext_state, true, false, &pp);
5727 : 0 : return make_label_text (false, "EN: %i: %s",
5728 : 0 : m_enode->m_index, pp_formatted_text (&pp));
5729 : 0 : }
5730 : :
5731 : : private:
5732 : : const extrinsic_state &m_ext_state;
5733 : : exploded_node *m_enode;
5734 : : };
5735 : :
5736 : : /* Postprocessing support for dumping the exploded nodes.
5737 : : Handle -fdump-analyzer-exploded-nodes,
5738 : : -fdump-analyzer-exploded-nodes-2, and the
5739 : : "__analyzer_dump_exploded_nodes" builtin. */
5740 : :
5741 : : void
5742 : 3323 : exploded_graph::dump_exploded_nodes () const
5743 : : {
5744 : : // TODO
5745 : : /* Locate calls to __analyzer_dump_exploded_nodes. */
5746 : : // Print how many egs there are for them?
5747 : : /* Better: log them as we go, and record the exploded nodes
5748 : : in question. */
5749 : :
5750 : : /* Show every enode. */
5751 : :
5752 : : /* Gather them by stmt, so that we can more clearly see the
5753 : : "hotspots" requiring numerous exploded nodes. */
5754 : :
5755 : : /* Alternatively, simply throw them all into one big rich_location
5756 : : and see if the label-printing will sort it out...
5757 : : This requires them all to be in the same source file. */
5758 : :
5759 : 3323 : if (flag_dump_analyzer_exploded_nodes)
5760 : : {
5761 : 0 : auto_timevar tv (TV_ANALYZER_DUMP);
5762 : 0 : gcc_rich_location richloc (UNKNOWN_LOCATION);
5763 : 0 : unsigned i;
5764 : 0 : exploded_node *enode;
5765 : 0 : FOR_EACH_VEC_ELT (m_nodes, i, enode)
5766 : : {
5767 : 0 : if (const gimple *stmt = enode->get_stmt ())
5768 : : {
5769 : 0 : if (get_pure_location (richloc.get_loc ()) == UNKNOWN_LOCATION)
5770 : 0 : richloc.set_range (0, stmt->location, SHOW_RANGE_WITH_CARET);
5771 : : else
5772 : 0 : richloc.add_range (stmt->location,
5773 : : SHOW_RANGE_WITHOUT_CARET,
5774 : 0 : new enode_label (m_ext_state, enode));
5775 : : }
5776 : : }
5777 : 0 : warning_at (&richloc, 0, "%i exploded nodes", m_nodes.length ());
5778 : :
5779 : : /* Repeat the warning without all the labels, so that message is visible
5780 : : (the other one may well have scrolled past the terminal limit). */
5781 : 0 : warning_at (richloc.get_loc (), 0,
5782 : : "%i exploded nodes", m_nodes.length ());
5783 : :
5784 : 0 : if (m_worklist.length () > 0)
5785 : 0 : warning_at (richloc.get_loc (), 0,
5786 : : "worklist still contains %i nodes", m_worklist.length ());
5787 : 0 : }
5788 : :
5789 : : /* Dump the egraph in textual form to a dump file. */
5790 : 3323 : if (flag_dump_analyzer_exploded_nodes_2)
5791 : : {
5792 : 0 : auto_timevar tv (TV_ANALYZER_DUMP);
5793 : 0 : char *filename
5794 : 0 : = concat (dump_base_name, ".eg.txt", nullptr);
5795 : 0 : FILE *outf = fopen (filename, "w");
5796 : 0 : if (!outf)
5797 : 0 : error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename);
5798 : 0 : free (filename);
5799 : :
5800 : 0 : fprintf (outf, "exploded graph for %s\n", dump_base_name);
5801 : 0 : fprintf (outf, " nodes: %i\n", m_nodes.length ());
5802 : 0 : fprintf (outf, " edges: %i\n", m_edges.length ());
5803 : :
5804 : 0 : unsigned i;
5805 : 0 : exploded_node *enode;
5806 : 0 : FOR_EACH_VEC_ELT (m_nodes, i, enode)
5807 : : {
5808 : 0 : fprintf (outf, "\nEN %i:\n", enode->m_index);
5809 : 0 : enode->dump_succs_and_preds (outf);
5810 : 0 : pretty_printer pp;
5811 : 0 : enode->get_point ().print (&pp, format (true));
5812 : 0 : fprintf (outf, "%s\n", pp_formatted_text (&pp));
5813 : 0 : text_art::dump_to_file (enode->get_state (), outf);
5814 : 0 : }
5815 : :
5816 : 0 : fclose (outf);
5817 : 0 : }
5818 : :
5819 : : /* Dump the egraph in textual form to multiple dump files, one per enode. */
5820 : 3323 : if (flag_dump_analyzer_exploded_nodes_3)
5821 : : {
5822 : 0 : auto_timevar tv (TV_ANALYZER_DUMP);
5823 : :
5824 : 0 : unsigned i;
5825 : 0 : exploded_node *enode;
5826 : 0 : FOR_EACH_VEC_ELT (m_nodes, i, enode)
5827 : : {
5828 : 0 : char *filename
5829 : 0 : = xasprintf ("%s.en-%i.txt", dump_base_name, i);
5830 : 0 : FILE *outf = fopen (filename, "w");
5831 : 0 : if (!outf)
5832 : 0 : error_at (UNKNOWN_LOCATION, "unable to open %qs for writing",
5833 : : filename);
5834 : 0 : free (filename);
5835 : :
5836 : 0 : fprintf (outf, "EN %i:\n", enode->m_index);
5837 : 0 : enode->dump_succs_and_preds (outf);
5838 : 0 : pretty_printer pp;
5839 : 0 : enode->get_point ().print (&pp, format (true));
5840 : 0 : fprintf (outf, "%s\n", pp_formatted_text (&pp));
5841 : 0 : text_art::dump_to_file (enode->get_state (), outf);
5842 : :
5843 : 0 : fclose (outf);
5844 : 0 : }
5845 : 0 : }
5846 : :
5847 : : /* Emit a warning at any call to "__analyzer_dump_exploded_nodes",
5848 : : giving the number of processed exploded nodes for "before-stmt",
5849 : : and the IDs of processed, merger, and worklist enodes.
5850 : :
5851 : : We highlight the count of *processed* enodes since this is of most
5852 : : interest in DejaGnu tests for ensuring that state merger has
5853 : : happened.
5854 : :
5855 : : We don't show the count of merger and worklist enodes, as this is
5856 : : more of an implementation detail of the merging/worklist that we
5857 : : don't want to bake into our expected DejaGnu messages. */
5858 : :
5859 : 3323 : unsigned i;
5860 : 3323 : exploded_node *enode;
5861 : 3323 : hash_set<const gimple *> seen;
5862 : 394943 : FOR_EACH_VEC_ELT (m_nodes, i, enode)
5863 : : {
5864 : 388301 : if (enode->get_point ().get_kind () != PK_BEFORE_STMT)
5865 : 256604 : continue;
5866 : :
5867 : 131697 : if (const gimple *stmt = enode->get_stmt ())
5868 : 176932 : if (const gcall *call = dyn_cast <const gcall *> (stmt))
5869 : 45621 : if (is_special_named_call_p (*call, "__analyzer_dump_exploded_nodes",
5870 : : 1))
5871 : : {
5872 : 1081 : if (seen.contains (stmt))
5873 : 386 : continue;
5874 : :
5875 : 695 : auto_vec<exploded_node *> processed_enodes;
5876 : 695 : auto_vec<exploded_node *> merger_enodes;
5877 : 695 : auto_vec<exploded_node *> worklist_enodes;
5878 : : /* This is O(N^2). */
5879 : 695 : unsigned j;
5880 : 695 : exploded_node *other_enode;
5881 : 124676 : FOR_EACH_VEC_ELT (m_nodes, j, other_enode)
5882 : : {
5883 : 123981 : if (other_enode->get_point ().get_kind () != PK_BEFORE_STMT)
5884 : 83465 : continue;
5885 : 40516 : if (other_enode->get_stmt () == stmt)
5886 : 1081 : switch (other_enode->get_status ())
5887 : : {
5888 : 0 : default:
5889 : 0 : gcc_unreachable ();
5890 : 0 : case exploded_node::status::worklist:
5891 : 0 : worklist_enodes.safe_push (other_enode);
5892 : 0 : break;
5893 : 1068 : case exploded_node::status::processed:
5894 : 1068 : processed_enodes.safe_push (other_enode);
5895 : 1068 : break;
5896 : 13 : case exploded_node::status::merger:
5897 : 13 : merger_enodes.safe_push (other_enode);
5898 : 13 : break;
5899 : : }
5900 : : }
5901 : :
5902 : 1390 : pretty_printer pp;
5903 : 695 : pp_character (&pp, '[');
5904 : 695 : print_enode_indices (&pp, processed_enodes);
5905 : 695 : if (merger_enodes.length () > 0)
5906 : : {
5907 : 13 : pp_string (&pp, "] merger(s): [");
5908 : 13 : print_enode_indices (&pp, merger_enodes);
5909 : : }
5910 : 695 : if (worklist_enodes.length () > 0)
5911 : : {
5912 : 0 : pp_string (&pp, "] worklist: [");
5913 : 0 : print_enode_indices (&pp, worklist_enodes);
5914 : : }
5915 : 695 : pp_character (&pp, ']');
5916 : :
5917 : 1390 : warning_n (stmt->location, 0, processed_enodes.length (),
5918 : : "%i processed enode: %s",
5919 : : "%i processed enodes: %s",
5920 : : processed_enodes.length (), pp_formatted_text (&pp));
5921 : 695 : seen.add (stmt);
5922 : :
5923 : : /* If the argument is non-zero, then print all of the states
5924 : : of the various enodes. */
5925 : 695 : tree t_arg = fold (gimple_call_arg (call, 0));
5926 : 695 : if (TREE_CODE (t_arg) != INTEGER_CST)
5927 : : {
5928 : 0 : error_at (call->location,
5929 : : "integer constant required for arg 1");
5930 : 0 : return;
5931 : : }
5932 : 695 : int i_arg = TREE_INT_CST_LOW (t_arg);
5933 : 695 : if (i_arg)
5934 : : {
5935 : : exploded_node *other_enode;
5936 : 695 : FOR_EACH_VEC_ELT (processed_enodes, j, other_enode)
5937 : : {
5938 : 0 : fprintf (stderr, "%i of %i: EN %i:\n",
5939 : : j + 1, processed_enodes.length (),
5940 : 0 : other_enode->m_index);
5941 : 0 : other_enode->dump_succs_and_preds (stderr);
5942 : : /* Dump state. */
5943 : 0 : other_enode->get_state ().dump (m_ext_state, false);
5944 : : }
5945 : : }
5946 : 695 : }
5947 : : }
5948 : 3323 : }
5949 : :
5950 : : DEBUG_FUNCTION exploded_node *
5951 : 0 : exploded_graph::get_node_by_index (int idx) const
5952 : : {
5953 : 0 : exploded_node *enode = m_nodes[idx];
5954 : 0 : gcc_assert (enode->m_index == idx);
5955 : 0 : return enode;
5956 : : }
5957 : :
5958 : : /* Ensure that there is an exploded_node for a top-level call to FNDECL. */
5959 : :
5960 : : void
5961 : 380 : exploded_graph::on_escaped_function (tree fndecl)
5962 : : {
5963 : 380 : logger * const logger = get_logger ();
5964 : 380 : LOG_FUNC_1 (logger, "%qE", fndecl);
5965 : :
5966 : 380 : cgraph_node *cgnode = cgraph_node::get (fndecl);
5967 : 380 : if (!cgnode)
5968 : : return;
5969 : :
5970 : 380 : function *fun = cgnode->get_fun ();
5971 : 380 : if (!fun)
5972 : : return;
5973 : :
5974 : 373 : if (!gimple_has_body_p (fndecl))
5975 : : return;
5976 : :
5977 : 373 : exploded_node *enode = add_function_entry (*fun);
5978 : 373 : if (logger)
5979 : : {
5980 : 0 : if (enode)
5981 : 0 : logger->log ("created EN %i for %qE entrypoint",
5982 : 0 : enode->m_index, fun->decl);
5983 : : else
5984 : 0 : logger->log ("did not create enode for %qE entrypoint", fun->decl);
5985 : : }
5986 : 380 : }
5987 : :
5988 : : /* A collection of classes for visualizing the callgraph in .dot form
5989 : : (as represented in the supergraph). */
5990 : :
5991 : : /* Forward decls. */
5992 : : class viz_callgraph_node;
5993 : : class viz_callgraph_edge;
5994 : : class viz_callgraph;
5995 : : class viz_callgraph_cluster;
5996 : :
5997 : : /* Traits for using "digraph.h" to visualize the callgraph. */
5998 : :
5999 : : struct viz_callgraph_traits
6000 : : {
6001 : : typedef viz_callgraph_node node_t;
6002 : : typedef viz_callgraph_edge edge_t;
6003 : : typedef viz_callgraph graph_t;
6004 : : struct dump_args_t
6005 : : {
6006 : 4 : dump_args_t (const exploded_graph *eg) : m_eg (eg) {}
6007 : : const exploded_graph *m_eg;
6008 : : };
6009 : : typedef viz_callgraph_cluster cluster_t;
6010 : : };
6011 : :
6012 : : /* Subclass of dnode representing a function within the callgraph. */
6013 : :
6014 : : class viz_callgraph_node : public dnode<viz_callgraph_traits>
6015 : : {
6016 : : friend class viz_callgraph;
6017 : :
6018 : : public:
6019 : 12 : viz_callgraph_node (function *fun, int index)
6020 : 12 : : m_fun (fun), m_index (index), m_num_supernodes (0), m_num_superedges (0)
6021 : : {
6022 : 12 : gcc_assert (fun);
6023 : 12 : }
6024 : :
6025 : 12 : void dump_dot (graphviz_out *gv, const dump_args_t &args) const final override
6026 : : {
6027 : 12 : pretty_printer *pp = gv->get_pp ();
6028 : :
6029 : 24 : dump_dot_id (pp);
6030 : 12 : pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
6031 : : "lightgrey");
6032 : 12 : pp_write_text_to_stream (pp);
6033 : :
6034 : 12 : pp_printf (pp, "VCG: %i: %s", m_index, function_name (m_fun));
6035 : 12 : pp_newline (pp);
6036 : :
6037 : 12 : pp_printf (pp, "supernodes: %i\n", m_num_supernodes);
6038 : 12 : pp_newline (pp);
6039 : :
6040 : 12 : pp_printf (pp, "superedges: %i\n", m_num_superedges);
6041 : 12 : pp_newline (pp);
6042 : :
6043 : 12 : if (args.m_eg)
6044 : : {
6045 : : unsigned i;
6046 : : exploded_node *enode;
6047 : : unsigned num_enodes = 0;
6048 : 1476 : FOR_EACH_VEC_ELT (args.m_eg->m_nodes, i, enode)
6049 : : {
6050 : 1464 : if (enode->get_point ().get_function () == m_fun)
6051 : 484 : num_enodes++;
6052 : : }
6053 : 12 : pp_printf (pp, "enodes: %i\n", num_enodes);
6054 : 12 : pp_newline (pp);
6055 : :
6056 : : // TODO: also show the per-callstring breakdown
6057 : 12 : const exploded_graph::call_string_data_map_t *per_cs_data
6058 : 12 : = args.m_eg->get_per_call_string_data ();
6059 : 36 : for (exploded_graph::call_string_data_map_t::iterator iter
6060 : 12 : = per_cs_data->begin ();
6061 : 36 : iter != per_cs_data->end ();
6062 : 24 : ++iter)
6063 : : {
6064 : 24 : const call_string *cs = (*iter).first;
6065 : : //per_call_string_data *data = (*iter).second;
6066 : 24 : num_enodes = 0;
6067 : 2952 : FOR_EACH_VEC_ELT (args.m_eg->m_nodes, i, enode)
6068 : : {
6069 : 2928 : if (enode->get_point ().get_function () == m_fun
6070 : 2928 : && &enode->get_point ().get_call_string () == cs)
6071 : 484 : num_enodes++;
6072 : : }
6073 : 24 : if (num_enodes > 0)
6074 : : {
6075 : 16 : cs->print (pp);
6076 : 16 : pp_printf (pp, ": %i\n", num_enodes);
6077 : : }
6078 : : }
6079 : :
6080 : : /* Show any summaries. */
6081 : 12 : per_function_data *data = args.m_eg->get_per_function_data (m_fun);
6082 : 12 : if (data)
6083 : : {
6084 : 12 : pp_newline (pp);
6085 : 24 : pp_printf (pp, "summaries: %i\n", data->m_summaries.length ());
6086 : 52 : for (auto summary : data->m_summaries)
6087 : : {
6088 : 16 : pp_printf (pp, "\nsummary: %s:\n", summary->get_desc ().get ());
6089 : 16 : const extrinsic_state &ext_state = args.m_eg->get_ext_state ();
6090 : 16 : const program_state &state = summary->get_state ();
6091 : 16 : state.dump_to_pp (ext_state, false, true, pp);
6092 : 16 : pp_newline (pp);
6093 : : }
6094 : : }
6095 : : }
6096 : :
6097 : 12 : pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
6098 : 12 : pp_string (pp, "\"];\n\n");
6099 : 12 : pp_flush (pp);
6100 : 12 : }
6101 : :
6102 : 20 : void dump_dot_id (pretty_printer *pp) const
6103 : : {
6104 : 12 : pp_printf (pp, "vcg_%i", m_index);
6105 : : }
6106 : :
6107 : : private:
6108 : : function *m_fun;
6109 : : int m_index;
6110 : : int m_num_supernodes;
6111 : : int m_num_superedges;
6112 : : };
6113 : :
6114 : : /* Subclass of dedge representing a callgraph edge. */
6115 : :
6116 : : class viz_callgraph_edge : public dedge<viz_callgraph_traits>
6117 : : {
6118 : : public:
6119 : 4 : viz_callgraph_edge (viz_callgraph_node *src, viz_callgraph_node *dest)
6120 : 4 : : dedge<viz_callgraph_traits> (src, dest)
6121 : : {}
6122 : :
6123 : 4 : void dump_dot (graphviz_out *gv, const dump_args_t &) const
6124 : : final override
6125 : : {
6126 : 4 : pretty_printer *pp = gv->get_pp ();
6127 : :
6128 : 4 : const char *style = "\"solid,bold\"";
6129 : 4 : const char *color = "black";
6130 : 4 : int weight = 10;
6131 : 4 : const char *constraint = "true";
6132 : :
6133 : 4 : m_src->dump_dot_id (pp);
6134 : 4 : pp_string (pp, " -> ");
6135 : 4 : m_dest->dump_dot_id (pp);
6136 : 4 : pp_printf (pp,
6137 : : (" [style=%s, color=%s, weight=%d, constraint=%s,"
6138 : : " headlabel=\""),
6139 : : style, color, weight, constraint);
6140 : 4 : pp_printf (pp, "\"];\n");
6141 : 4 : }
6142 : : };
6143 : :
6144 : : /* Subclass of digraph representing the callgraph. */
6145 : :
6146 : : class viz_callgraph : public digraph<viz_callgraph_traits>
6147 : : {
6148 : : public:
6149 : : viz_callgraph (const supergraph &sg);
6150 : :
6151 : 260 : viz_callgraph_node *get_vcg_node_for_function (function *fun)
6152 : : {
6153 : 520 : return *m_map.get (fun);
6154 : : }
6155 : :
6156 : 92 : viz_callgraph_node *get_vcg_node_for_snode (supernode *snode)
6157 : : {
6158 : 184 : return get_vcg_node_for_function (snode->m_fun);
6159 : : }
6160 : :
6161 : : private:
6162 : : hash_map<function *, viz_callgraph_node *> m_map;
6163 : : };
6164 : :
6165 : : /* Placeholder subclass of cluster. */
6166 : :
6167 : : class viz_callgraph_cluster : public cluster<viz_callgraph_traits>
6168 : : {
6169 : : };
6170 : :
6171 : : /* viz_callgraph's ctor. */
6172 : :
6173 : 4 : viz_callgraph::viz_callgraph (const supergraph &sg)
6174 : : {
6175 : 4 : cgraph_node *node;
6176 : 16 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
6177 : : {
6178 : 12 : function *fun = node->get_fun ();
6179 : 12 : viz_callgraph_node *vcg_node
6180 : 20 : = new viz_callgraph_node (fun, m_nodes.length ());
6181 : 12 : m_map.put (fun, vcg_node);
6182 : 12 : add_node (vcg_node);
6183 : : }
6184 : :
6185 : : unsigned i;
6186 : : superedge *sedge;
6187 : 92 : FOR_EACH_VEC_ELT (sg.m_edges, i, sedge)
6188 : : {
6189 : 88 : viz_callgraph_node *vcg_src = get_vcg_node_for_snode (sedge->m_src);
6190 : 88 : if (vcg_src->m_fun)
6191 : 88 : get_vcg_node_for_function (vcg_src->m_fun)->m_num_superedges++;
6192 : 88 : if (sedge->dyn_cast_call_superedge ())
6193 : : {
6194 : 4 : viz_callgraph_node *vcg_dest = get_vcg_node_for_snode (sedge->m_dest);
6195 : 4 : viz_callgraph_edge *vcg_edge
6196 : 4 : = new viz_callgraph_edge (vcg_src, vcg_dest);
6197 : 4 : add_edge (vcg_edge);
6198 : : }
6199 : : }
6200 : :
6201 : : supernode *snode;
6202 : 84 : FOR_EACH_VEC_ELT (sg.m_nodes, i, snode)
6203 : : {
6204 : 80 : if (snode->m_fun)
6205 : 80 : get_vcg_node_for_function (snode->m_fun)->m_num_supernodes++;
6206 : : }
6207 : 4 : }
6208 : :
6209 : : /* Dump the callgraph to FILENAME. */
6210 : :
6211 : : static void
6212 : 4 : dump_callgraph (const supergraph &sg, const char *filename,
6213 : : const exploded_graph *eg)
6214 : : {
6215 : 4 : FILE *outf = fopen (filename, "w");
6216 : 4 : if (!outf)
6217 : 0 : return;
6218 : :
6219 : : // TODO
6220 : 4 : viz_callgraph vcg (sg);
6221 : 4 : vcg.dump_dot (filename, nullptr, viz_callgraph_traits::dump_args_t (eg));
6222 : :
6223 : 4 : fclose (outf);
6224 : 4 : }
6225 : :
6226 : : /* Dump the callgraph to "<srcfile>.callgraph.dot". */
6227 : :
6228 : : static void
6229 : 4 : dump_callgraph (const supergraph &sg, const exploded_graph *eg)
6230 : : {
6231 : 4 : auto_timevar tv (TV_ANALYZER_DUMP);
6232 : 4 : char *filename = concat (dump_base_name, ".callgraph.dot", nullptr);
6233 : 4 : dump_callgraph (sg, filename, eg);
6234 : 4 : free (filename);
6235 : 4 : }
6236 : :
6237 : : /* Subclass of dot_annotator for implementing
6238 : : DUMP_BASE_NAME.supergraph-eg.dot, a post-analysis dump of the supergraph.
6239 : :
6240 : : Annotate the supergraph nodes by printing the exploded nodes in concise
6241 : : form within them, next to their pertinent statements where appropriate,
6242 : : colorizing the exploded nodes based on sm-state.
6243 : : Also show saved diagnostics within the exploded nodes, giving information
6244 : : on whether they were feasible, and, if infeasible, where the problem
6245 : : was. */
6246 : :
6247 : 4 : class exploded_graph_annotator : public dot_annotator
6248 : : {
6249 : : public:
6250 : 4 : exploded_graph_annotator (const exploded_graph &eg)
6251 : 4 : : m_eg (eg)
6252 : : {
6253 : : /* Avoid O(N^2) by prepopulating m_enodes_per_snodes. */
6254 : 4 : unsigned i;
6255 : 4 : supernode *snode;
6256 : 84 : FOR_EACH_VEC_ELT (eg.get_supergraph ().m_nodes, i, snode)
6257 : 80 : m_enodes_per_snodes.safe_push (new auto_vec <exploded_node *> ());
6258 : : exploded_node *enode;
6259 : 492 : FOR_EACH_VEC_ELT (m_eg.m_nodes, i, enode)
6260 : 488 : if (enode->get_supernode ())
6261 : 484 : m_enodes_per_snodes[enode->get_supernode ()->m_index]->safe_push (enode);
6262 : 4 : }
6263 : :
6264 : : /* Show exploded nodes for BEFORE_SUPERNODE points before N. */
6265 : 160 : bool add_node_annotations (graphviz_out *gv, const supernode &n,
6266 : : bool within_table)
6267 : : const final override
6268 : : {
6269 : 160 : if (!within_table)
6270 : : return false;
6271 : 80 : gv->begin_tr ();
6272 : 80 : pretty_printer *pp = gv->get_pp ();
6273 : :
6274 : 80 : gv->begin_td ();
6275 : 80 : pp_string (pp, "BEFORE");
6276 : 80 : pp_printf (pp, " (scc: %i)", m_eg.get_scc_id (n));
6277 : 80 : gv->end_td ();
6278 : :
6279 : 80 : unsigned i;
6280 : 80 : exploded_node *enode;
6281 : 80 : bool had_enode = false;
6282 : 564 : FOR_EACH_VEC_ELT (*m_enodes_per_snodes[n.m_index], i, enode)
6283 : : {
6284 : 484 : gcc_assert (enode->get_supernode () == &n);
6285 : 484 : const program_point &point = enode->get_point ();
6286 : 484 : if (point.get_kind () != PK_BEFORE_SUPERNODE)
6287 : 320 : continue;
6288 : 164 : print_enode (gv, enode);
6289 : 164 : had_enode = true;
6290 : : }
6291 : 80 : if (!had_enode)
6292 : 0 : pp_string (pp, "<TD BGCOLOR=\"red\">UNREACHED</TD>");
6293 : 80 : pp_flush (pp);
6294 : 80 : gv->end_tr ();
6295 : 80 : return true;
6296 : : }
6297 : :
6298 : : /* Show exploded nodes for STMT. */
6299 : 282 : void add_stmt_annotations (graphviz_out *gv, const gimple *stmt,
6300 : : bool within_row)
6301 : : const final override
6302 : : {
6303 : 282 : if (!within_row)
6304 : 282 : return;
6305 : 141 : pretty_printer *pp = gv->get_pp ();
6306 : :
6307 : 141 : const supernode *snode
6308 : 141 : = m_eg.get_supergraph ().get_supernode_for_stmt (stmt);
6309 : 141 : unsigned i;
6310 : 141 : exploded_node *enode;
6311 : 141 : bool had_td = false;
6312 : 1474 : FOR_EACH_VEC_ELT (*m_enodes_per_snodes[snode->m_index], i, enode)
6313 : : {
6314 : 1333 : const program_point &point = enode->get_point ();
6315 : 1333 : if (point.get_kind () != PK_BEFORE_STMT)
6316 : 681 : continue;
6317 : 652 : if (point.get_stmt () != stmt)
6318 : 469 : continue;
6319 : 183 : print_enode (gv, enode);
6320 : 183 : had_td = true;
6321 : : }
6322 : 141 : pp_flush (pp);
6323 : 141 : if (!had_td)
6324 : : {
6325 : 40 : gv->begin_td ();
6326 : 40 : gv->end_td ();
6327 : : }
6328 : : }
6329 : :
6330 : : /* Show exploded nodes for AFTER_SUPERNODE points after N. */
6331 : 80 : bool add_after_node_annotations (graphviz_out *gv, const supernode &n)
6332 : : const final override
6333 : : {
6334 : 80 : gv->begin_tr ();
6335 : 80 : pretty_printer *pp = gv->get_pp ();
6336 : :
6337 : 80 : gv->begin_td ();
6338 : 80 : pp_string (pp, "AFTER");
6339 : 80 : gv->end_td ();
6340 : :
6341 : 80 : unsigned i;
6342 : 80 : exploded_node *enode;
6343 : 564 : FOR_EACH_VEC_ELT (*m_enodes_per_snodes[n.m_index], i, enode)
6344 : : {
6345 : 484 : gcc_assert (enode->get_supernode () == &n);
6346 : 484 : const program_point &point = enode->get_point ();
6347 : 484 : if (point.get_kind () != PK_AFTER_SUPERNODE)
6348 : 336 : continue;
6349 : 148 : print_enode (gv, enode);
6350 : : }
6351 : 80 : pp_flush (pp);
6352 : 80 : gv->end_tr ();
6353 : 80 : return true;
6354 : : }
6355 : :
6356 : : private:
6357 : : /* Concisely print a TD element for ENODE, showing the index, status,
6358 : : and any saved_diagnostics at the enode. Colorize it to show sm-state.
6359 : :
6360 : : Ideally we'd dump ENODE's state here, hidden behind some kind of
6361 : : interactive disclosure method like a tooltip, so that the states
6362 : : can be explored without overwhelming the graph.
6363 : : However, I wasn't able to get graphviz/xdot to show tooltips on
6364 : : individual elements within a HTML-like label. */
6365 : 495 : void print_enode (graphviz_out *gv, const exploded_node *enode) const
6366 : : {
6367 : 495 : pretty_printer *pp = gv->get_pp ();
6368 : 495 : pp_printf (pp, "<TD BGCOLOR=\"%s\">",
6369 : : enode->get_dot_fillcolor ());
6370 : 495 : pp_printf (pp, "<TABLE BORDER=\"0\">");
6371 : 495 : gv->begin_trtd ();
6372 : 495 : pp_printf (pp, "EN: %i", enode->m_index);
6373 : 495 : switch (enode->get_status ())
6374 : : {
6375 : 0 : default:
6376 : 0 : gcc_unreachable ();
6377 : 0 : case exploded_node::status::worklist:
6378 : 0 : pp_string (pp, "(W)");
6379 : 0 : break;
6380 : : case exploded_node::status::processed:
6381 : : break;
6382 : 6 : case exploded_node::status::special:
6383 : 6 : pp_string (pp, "(S)");
6384 : 6 : break;
6385 : 0 : case exploded_node::status::merger:
6386 : 0 : pp_string (pp, "(M)");
6387 : 0 : break;
6388 : 40 : case exploded_node::status::bulk_merged:
6389 : 40 : pp_string (pp, "(BM)");
6390 : 40 : break;
6391 : : }
6392 : 495 : gv->end_tdtr ();
6393 : :
6394 : : /* Dump any saved_diagnostics at this enode. */
6395 : 503 : for (unsigned i = 0; i < enode->get_num_diagnostics (); i++)
6396 : : {
6397 : 8 : const saved_diagnostic *sd = enode->get_saved_diagnostic (i);
6398 : 8 : print_saved_diagnostic (gv, sd);
6399 : : }
6400 : 495 : pp_printf (pp, "</TABLE>");
6401 : 495 : pp_printf (pp, "</TD>");
6402 : 495 : }
6403 : :
6404 : : /* Print a TABLE element for SD, showing the kind, the length of the
6405 : : exploded_path, whether the path was feasible, and if infeasible,
6406 : : what the problem was. */
6407 : 8 : void print_saved_diagnostic (graphviz_out *gv,
6408 : : const saved_diagnostic *sd) const
6409 : : {
6410 : 8 : pretty_printer *pp = gv->get_pp ();
6411 : 8 : gv->begin_trtd ();
6412 : 8 : pp_printf (pp, "<TABLE BORDER=\"0\">");
6413 : 8 : gv->begin_tr ();
6414 : 8 : pp_string (pp, "<TD BGCOLOR=\"green\">");
6415 : 8 : pp_printf (pp, "DIAGNOSTIC: %s", sd->m_d->get_kind ());
6416 : 8 : gv->end_tdtr ();
6417 : 8 : gv->begin_trtd ();
6418 : 8 : if (sd->get_best_epath ())
6419 : 8 : pp_printf (pp, "epath length: %i", sd->get_epath_length ());
6420 : : else
6421 : 0 : pp_printf (pp, "no best epath");
6422 : 8 : gv->end_tdtr ();
6423 : 8 : if (const feasibility_problem *p = sd->get_feasibility_problem ())
6424 : : {
6425 : 0 : gv->begin_trtd ();
6426 : 0 : pp_printf (pp, "INFEASIBLE at eedge %i: EN:%i -> EN:%i",
6427 : 0 : p->m_eedge_idx,
6428 : 0 : p->m_eedge.m_src->m_index,
6429 : 0 : p->m_eedge.m_dest->m_index);
6430 : 0 : pp_write_text_as_html_like_dot_to_stream (pp);
6431 : 0 : gv->end_tdtr ();
6432 : 0 : gv->begin_trtd ();
6433 : 0 : p->m_eedge.m_sedge->dump (pp);
6434 : 0 : pp_write_text_as_html_like_dot_to_stream (pp);
6435 : 0 : gv->end_tdtr ();
6436 : 0 : gv->begin_trtd ();
6437 : 0 : pp_gimple_stmt_1 (pp, p->m_last_stmt, 0, (dump_flags_t)0);
6438 : 0 : pp_write_text_as_html_like_dot_to_stream (pp);
6439 : 0 : gv->end_tdtr ();
6440 : : /* Ideally we'd print p->m_model here; see the notes above about
6441 : : tooltips. */
6442 : : }
6443 : 8 : pp_printf (pp, "</TABLE>");
6444 : 8 : gv->end_tdtr ();
6445 : 8 : }
6446 : :
6447 : : const exploded_graph &m_eg;
6448 : : auto_delete_vec<auto_vec <exploded_node *> > m_enodes_per_snodes;
6449 : : };
6450 : :
6451 : : /* Implement -fdump-analyzer-json. */
6452 : :
6453 : : static void
6454 : 0 : dump_analyzer_json (const supergraph &sg,
6455 : : const exploded_graph &eg)
6456 : : {
6457 : 0 : auto_timevar tv (TV_ANALYZER_DUMP);
6458 : 0 : char *filename = concat (dump_base_name, ".analyzer.json.gz", nullptr);
6459 : 0 : gzFile output = gzopen (filename, "w");
6460 : 0 : if (!output)
6461 : : {
6462 : 0 : error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename);
6463 : 0 : free (filename);
6464 : 0 : return;
6465 : : }
6466 : :
6467 : 0 : auto toplev_obj = std::make_unique<json::object> ();
6468 : 0 : toplev_obj->set ("sgraph", sg.to_json ());
6469 : 0 : toplev_obj->set ("egraph", eg.to_json ());
6470 : :
6471 : 0 : pretty_printer pp;
6472 : 0 : toplev_obj->print (&pp, flag_diagnostics_json_formatting);
6473 : 0 : pp_formatted_text (&pp);
6474 : :
6475 : 0 : if (gzputs (output, pp_formatted_text (&pp)) == EOF
6476 : 0 : || gzclose (output))
6477 : 0 : error_at (UNKNOWN_LOCATION, "error writing %qs", filename);
6478 : :
6479 : 0 : free (filename);
6480 : 0 : }
6481 : :
6482 : : /* Concrete subclass of plugin_analyzer_init_iface, allowing plugins
6483 : : to register new state machines. */
6484 : :
6485 : : class plugin_analyzer_init_impl : public plugin_analyzer_init_iface
6486 : : {
6487 : : public:
6488 : 3323 : plugin_analyzer_init_impl (std::vector<std::unique_ptr<state_machine>> &checkers,
6489 : : known_function_manager &known_fn_mgr,
6490 : : logger *logger)
6491 : 3323 : : m_checkers (checkers),
6492 : 3323 : m_known_fn_mgr (known_fn_mgr),
6493 : 3323 : m_logger (logger)
6494 : : {}
6495 : :
6496 : 1 : void register_state_machine (std::unique_ptr<state_machine> sm) final override
6497 : : {
6498 : 1 : LOG_SCOPE (m_logger);
6499 : 1 : m_checkers.push_back (std::move (sm));
6500 : 1 : }
6501 : :
6502 : 107 : void register_known_function (const char *name,
6503 : : std::unique_ptr<known_function> kf) final override
6504 : : {
6505 : 107 : LOG_SCOPE (m_logger);
6506 : 107 : m_known_fn_mgr.add (name, std::move (kf));
6507 : 107 : }
6508 : :
6509 : 39 : logger *get_logger () const final override
6510 : : {
6511 : 39 : return m_logger;
6512 : : }
6513 : :
6514 : : private:
6515 : : std::vector<std::unique_ptr<state_machine>> &m_checkers;
6516 : : known_function_manager &m_known_fn_mgr;
6517 : : logger *m_logger;
6518 : : };
6519 : :
6520 : : /* Run the analysis "engine". */
6521 : :
6522 : : void
6523 : 3323 : impl_run_checkers (logger *logger)
6524 : : {
6525 : 3323 : LOG_SCOPE (logger);
6526 : :
6527 : 3323 : if (logger)
6528 : : {
6529 : 2 : logger->log ("BITS_BIG_ENDIAN: %i", BITS_BIG_ENDIAN ? 1 : 0);
6530 : 2 : logger->log ("BYTES_BIG_ENDIAN: %i", BYTES_BIG_ENDIAN ? 1 : 0);
6531 : 2 : logger->log ("WORDS_BIG_ENDIAN: %i", WORDS_BIG_ENDIAN ? 1 : 0);
6532 : 2 : log_stashed_constants (logger);
6533 : : }
6534 : :
6535 : : /* If using LTO, ensure that the cgraph nodes have function bodies. */
6536 : 3323 : cgraph_node *node;
6537 : 13445 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
6538 : 10122 : node->get_untransformed_body ();
6539 : :
6540 : : /* Create the supergraph. */
6541 : 3323 : supergraph sg (logger);
6542 : :
6543 : 3323 : engine eng (&sg, logger);
6544 : :
6545 : 3323 : state_purge_map *purge_map = nullptr;
6546 : :
6547 : 3323 : if (flag_analyzer_state_purge)
6548 : 3315 : purge_map = new state_purge_map (sg, eng.get_model_manager (), logger);
6549 : :
6550 : 3323 : if (flag_dump_analyzer_supergraph)
6551 : : {
6552 : : /* Dump supergraph pre-analysis. */
6553 : 4 : auto_timevar tv (TV_ANALYZER_DUMP);
6554 : 4 : char *filename = concat (dump_base_name, ".supergraph.dot", nullptr);
6555 : 4 : supergraph::dump_args_t args ((enum supergraph_dot_flags)0, nullptr);
6556 : 4 : sg.dump_dot (filename, args);
6557 : 4 : free (filename);
6558 : 4 : }
6559 : :
6560 : 3323 : if (flag_dump_analyzer_state_purge)
6561 : : {
6562 : 4 : auto_timevar tv (TV_ANALYZER_DUMP);
6563 : 4 : state_purge_annotator a (purge_map);
6564 : 4 : char *filename = concat (dump_base_name, ".state-purge.dot", nullptr);
6565 : 4 : supergraph::dump_args_t args ((enum supergraph_dot_flags)0, &a);
6566 : 4 : sg.dump_dot (filename, args);
6567 : 4 : free (filename);
6568 : 4 : }
6569 : :
6570 : 3323 : auto checkers = make_checkers (logger);
6571 : :
6572 : 3323 : register_known_functions (*eng.get_known_function_manager (),
6573 : 3323 : *eng.get_model_manager ());
6574 : :
6575 : 3323 : plugin_analyzer_init_impl data (checkers,
6576 : 3323 : *eng.get_known_function_manager (),
6577 : 3323 : logger);
6578 : 3323 : invoke_plugin_callbacks (PLUGIN_ANALYZER_INIT, &data);
6579 : :
6580 : 3323 : if (logger)
6581 : : {
6582 : 2 : int i = 0;
6583 : 16 : for (auto &sm : checkers)
6584 : 14 : logger->log ("checkers[%i]: %s", ++i, sm->get_name ());
6585 : : }
6586 : :
6587 : : /* Extrinsic state shared by nodes in the graph. */
6588 : 3323 : const extrinsic_state ext_state (std::move (checkers), &eng, logger);
6589 : :
6590 : 3323 : const analysis_plan plan (sg, logger);
6591 : :
6592 : : /* The exploded graph. */
6593 : 3323 : exploded_graph eg (sg, logger, ext_state, purge_map, plan,
6594 : 3323 : analyzer_verbosity);
6595 : :
6596 : : /* Add entrypoints to the graph for externally-callable functions. */
6597 : 3323 : eg.build_initial_worklist ();
6598 : :
6599 : : /* Now process the worklist, exploring the <point, state> graph. */
6600 : 3323 : eg.process_worklist ();
6601 : :
6602 : 3323 : if (warn_analyzer_infinite_loop)
6603 : 3323 : eg.detect_infinite_loops ();
6604 : :
6605 : 3323 : if (flag_dump_analyzer_exploded_graph)
6606 : : {
6607 : 4 : auto_timevar tv (TV_ANALYZER_DUMP);
6608 : 4 : char *filename
6609 : 4 : = concat (dump_base_name, ".eg.dot", nullptr);
6610 : 4 : exploded_graph::dump_args_t args (eg);
6611 : 4 : root_cluster c;
6612 : 4 : eg.dump_dot (filename, &c, args);
6613 : 4 : free (filename);
6614 : 4 : }
6615 : :
6616 : : /* Now emit any saved diagnostics. */
6617 : 3323 : eg.get_diagnostic_manager ().emit_saved_diagnostics (eg);
6618 : :
6619 : 3323 : eg.dump_exploded_nodes ();
6620 : :
6621 : 3323 : eg.log_stats ();
6622 : :
6623 : 3323 : if (flag_dump_analyzer_callgraph)
6624 : 4 : dump_callgraph (sg, &eg);
6625 : :
6626 : 3323 : if (flag_dump_analyzer_supergraph)
6627 : : {
6628 : : /* Dump post-analysis form of supergraph. */
6629 : 4 : auto_timevar tv (TV_ANALYZER_DUMP);
6630 : 4 : char *filename = concat (dump_base_name, ".supergraph-eg.dot", nullptr);
6631 : 4 : exploded_graph_annotator a (eg);
6632 : 4 : supergraph::dump_args_t args ((enum supergraph_dot_flags)0, &a);
6633 : 4 : sg.dump_dot (filename, args);
6634 : 4 : free (filename);
6635 : 4 : }
6636 : :
6637 : 3323 : if (flag_dump_analyzer_json)
6638 : 0 : dump_analyzer_json (sg, eg);
6639 : :
6640 : 3323 : if (flag_dump_analyzer_untracked)
6641 : 23 : eng.get_model_manager ()->dump_untracked_regions ();
6642 : :
6643 : 3323 : delete purge_map;
6644 : :
6645 : : /* Free up any dominance info that we may have created. */
6646 : 13445 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
6647 : : {
6648 : 10122 : function *fun = node->get_fun ();
6649 : 10122 : free_dominance_info (fun, CDI_DOMINATORS);
6650 : : }
6651 : 3323 : }
6652 : :
6653 : : /* Handle -fdump-analyzer and -fdump-analyzer-stderr. */
6654 : : static FILE *dump_fout = nullptr;
6655 : :
6656 : : /* Track if we're responsible for closing dump_fout. */
6657 : : static bool owns_dump_fout = false;
6658 : :
6659 : : /* If dumping is enabled, attempt to create dump_fout if it hasn't already
6660 : : been opened. Return it. */
6661 : :
6662 : : FILE *
6663 : 6688 : get_or_create_any_logfile ()
6664 : : {
6665 : 6688 : if (!dump_fout)
6666 : : {
6667 : 6686 : if (flag_dump_analyzer_stderr)
6668 : 0 : dump_fout = stderr;
6669 : 6686 : else if (flag_dump_analyzer)
6670 : : {
6671 : 2 : char *dump_filename = concat (dump_base_name, ".analyzer.txt", nullptr);
6672 : 2 : dump_fout = fopen (dump_filename, "w");
6673 : 2 : free (dump_filename);
6674 : 2 : if (dump_fout)
6675 : 2 : owns_dump_fout = true;
6676 : : }
6677 : : }
6678 : 6688 : return dump_fout;
6679 : : }
6680 : :
6681 : : /* External entrypoint to the analysis "engine".
6682 : : Set up any dumps, then call impl_run_checkers. */
6683 : :
6684 : : void
6685 : 3323 : run_checkers ()
6686 : : {
6687 : : /* Save input_location. */
6688 : 3323 : location_t saved_input_location = input_location;
6689 : :
6690 : 3323 : {
6691 : 3323 : log_user the_logger (nullptr);
6692 : 3323 : get_or_create_any_logfile ();
6693 : 3323 : if (dump_fout)
6694 : 2 : the_logger.set_logger (new logger (dump_fout, 0, 0,
6695 : 2 : *global_dc->get_reference_printer ()));
6696 : 3323 : LOG_SCOPE (the_logger.get_logger ());
6697 : :
6698 : 3323 : impl_run_checkers (the_logger.get_logger ());
6699 : :
6700 : : /* end of lifetime of the_logger (so that dump file is closed after the
6701 : : various dtors run). */
6702 : 3323 : }
6703 : :
6704 : 3323 : if (owns_dump_fout)
6705 : : {
6706 : 2 : fclose (dump_fout);
6707 : 2 : owns_dump_fout = false;
6708 : 2 : dump_fout = nullptr;
6709 : : }
6710 : :
6711 : : /* Restore input_location. Subsequent passes may assume that input_location
6712 : : is some arbitrary value *not* in the block tree, which might be violated
6713 : : if we didn't restore it. */
6714 : 3323 : input_location = saved_input_location;
6715 : 3323 : }
6716 : :
6717 : : } // namespace ana
6718 : :
6719 : : #endif /* #if ENABLE_ANALYZER */
|