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