Line data Source code
1 : /* The analysis "engine".
2 : Copyright (C) 2019-2026 Free Software Foundation, Inc.
3 : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 :
5 : This file is part of GCC.
6 :
7 : GCC is free software; you can redistribute it and/or modify it
8 : under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3, or (at your option)
10 : any later version.
11 :
12 : GCC is distributed in the hope that it will be useful, but
13 : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with GCC; see the file COPYING3. If not see
19 : <http://www.gnu.org/licenses/>. */
20 :
21 : #include "analyzer/common.h"
22 :
23 : #include <zlib.h>
24 :
25 : #include "cfg.h"
26 : #include "gcc-rich-location.h"
27 : #include "gimple-iterator.h"
28 : #include "gimple-pretty-print.h"
29 : #include "cgraph.h"
30 : #include "fold-const.h"
31 : #include "digraph.h"
32 : #include "plugin.h"
33 : #include "target.h"
34 : #include "stringpool.h"
35 : #include "attribs.h"
36 : #include "tree-dfa.h"
37 : #include "gimple-predict.h"
38 : #include "context.h"
39 : #include "channels.h"
40 : #include "pretty-print-markup.h"
41 :
42 : #include "text-art/dump.h"
43 :
44 : #include "analyzer/analyzer-logging.h"
45 : #include "analyzer/call-string.h"
46 : #include "analyzer/program-point.h"
47 : #include "analyzer/store.h"
48 : #include "analyzer/region-model.h"
49 : #include "analyzer/constraint-manager.h"
50 : #include "analyzer/sm.h"
51 : #include "analyzer/pending-diagnostic.h"
52 : #include "analyzer/diagnostic-manager.h"
53 : #include "analyzer/supergraph.h"
54 : #include "analyzer/program-state.h"
55 : #include "analyzer/exploded-graph.h"
56 : #include "analyzer/exploded-path.h"
57 : #include "analyzer/analysis-plan.h"
58 : #include "analyzer/checker-path.h"
59 : #include "analyzer/state-purge.h"
60 : #include "analyzer/bar-chart.h"
61 : #include "analyzer/call-info.h"
62 : #include "analyzer/known-function-manager.h"
63 : #include "analyzer/call-summary.h"
64 : #include "analyzer/impl-sm-context.h"
65 :
66 : /* For an overview, see gcc/doc/analyzer.texi. */
67 :
68 : #if ENABLE_ANALYZER
69 :
70 : namespace ana {
71 :
72 : /* class impl_region_model_context : public region_model_context. */
73 :
74 1401863 : impl_region_model_context::
75 : impl_region_model_context (exploded_graph &eg,
76 : exploded_node *enode_for_diag,
77 : const program_state *old_state,
78 : program_state *new_state,
79 : uncertainty_t *uncertainty,
80 : path_context *path_ctxt,
81 : const gimple *stmt,
82 1401863 : bool *out_could_have_done_work)
83 1401863 : : m_eg (&eg), m_logger (eg.get_logger ()),
84 1401863 : m_enode_for_diag (enode_for_diag),
85 1401863 : m_old_state (old_state),
86 1401863 : m_new_state (new_state),
87 1401863 : m_stmt (stmt),
88 1401863 : m_ext_state (eg.get_ext_state ()),
89 1401863 : m_uncertainty (uncertainty),
90 1401863 : m_path_ctxt (path_ctxt),
91 1401863 : m_out_could_have_done_work (out_could_have_done_work)
92 : {
93 1401863 : }
94 :
95 4 : impl_region_model_context::
96 : impl_region_model_context (program_state *state,
97 : const extrinsic_state &ext_state,
98 : uncertainty_t *uncertainty,
99 4 : logger *logger)
100 4 : : m_eg (nullptr), m_logger (logger), m_enode_for_diag (nullptr),
101 4 : m_old_state (nullptr),
102 4 : m_new_state (state),
103 4 : m_stmt (nullptr),
104 4 : m_ext_state (ext_state),
105 4 : m_uncertainty (uncertainty),
106 4 : m_path_ctxt (nullptr),
107 4 : m_out_could_have_done_work (nullptr)
108 : {
109 4 : }
110 :
111 : bool
112 3209 : impl_region_model_context::warn_at (std::unique_ptr<pending_diagnostic> d,
113 : pending_location &&ploc)
114 : {
115 3209 : LOG_FUNC (get_logger ());
116 3209 : if (m_eg)
117 : {
118 3209 : bool terminate_path = d->terminate_path_p ();
119 3209 : if (m_eg->get_diagnostic_manager ().add_diagnostic (std::move (ploc),
120 : std::move (d)))
121 : {
122 3170 : if (m_path_ctxt
123 2256 : && terminate_path
124 871 : && flag_analyzer_suppress_followups)
125 666 : m_path_ctxt->terminate_path ();
126 3170 : return true;
127 : }
128 : }
129 : return false;
130 3209 : }
131 :
132 : void
133 199 : impl_region_model_context::add_note (std::unique_ptr<pending_note> pn)
134 : {
135 199 : LOG_FUNC (get_logger ());
136 199 : if (m_eg)
137 199 : m_eg->get_diagnostic_manager ().add_note (std::move (pn));
138 199 : }
139 :
140 : void
141 164 : impl_region_model_context::add_event (std::unique_ptr<checker_event> event)
142 : {
143 164 : LOG_FUNC (get_logger ());
144 164 : if (m_eg)
145 164 : m_eg->get_diagnostic_manager ().add_event (std::move (event));
146 164 : }
147 :
148 : void
149 88715 : impl_region_model_context::on_svalue_leak (const svalue *sval)
150 :
151 : {
152 886825 : for (sm_state_map *smap : m_new_state->m_checker_states)
153 620680 : smap->on_svalue_leak (sval, this);
154 88715 : }
155 :
156 : void
157 506248 : impl_region_model_context::
158 : on_liveness_change (const svalue_set &live_svalues,
159 : const region_model *model)
160 : {
161 5060319 : for (sm_state_map *smap : m_new_state->m_checker_states)
162 3541575 : smap->on_liveness_change (live_svalues, model, m_ext_state, this);
163 506248 : }
164 :
165 : void
166 52343 : impl_region_model_context::on_unknown_change (const svalue *sval,
167 : bool is_mutable)
168 : {
169 52343 : if (!sval->can_have_associated_state_p ())
170 : return;
171 424805 : for (sm_state_map *smap : m_new_state->m_checker_states)
172 297338 : smap->on_unknown_change (sval, is_mutable, m_ext_state);
173 : }
174 :
175 : void
176 298 : impl_region_model_context::on_escaped_function (tree fndecl)
177 : {
178 298 : m_eg->on_escaped_function (fndecl);
179 298 : }
180 :
181 : uncertainty_t *
182 4295211 : impl_region_model_context::get_uncertainty ()
183 : {
184 4295211 : return m_uncertainty;
185 : }
186 :
187 : /* Purge state involving SVAL. The region_model has already been purged,
188 : so we only need to purge other state in the program_state:
189 : the sm-state. */
190 :
191 : void
192 15923 : impl_region_model_context::purge_state_involving (const svalue *sval)
193 : {
194 15923 : int i;
195 15923 : sm_state_map *smap;
196 127384 : FOR_EACH_VEC_ELT (m_new_state->m_checker_states, i, smap)
197 111461 : smap->purge_state_involving (sval, m_ext_state);
198 15923 : }
199 :
200 : void
201 8602 : impl_region_model_context::bifurcate (std::unique_ptr<custom_edge_info> info)
202 : {
203 8602 : if (m_path_ctxt)
204 8602 : m_path_ctxt->bifurcate (std::move (info));
205 8602 : }
206 :
207 : void
208 1360 : impl_region_model_context::terminate_path ()
209 : {
210 1360 : if (m_path_ctxt)
211 1323 : return m_path_ctxt->terminate_path ();
212 : }
213 :
214 : bool
215 756424 : impl_region_model_context::
216 : get_state_map_by_name (const char *name,
217 : sm_state_map **out_smap,
218 : const state_machine **out_sm,
219 : unsigned *out_sm_idx,
220 : std::unique_ptr<sm_context> *out_sm_context)
221 : {
222 756424 : if (!m_new_state)
223 : return false;
224 :
225 753413 : unsigned sm_idx;
226 753413 : if (!m_ext_state.get_sm_idx_by_name (name, &sm_idx))
227 : return false;
228 :
229 752946 : const state_machine *sm = &m_ext_state.get_sm (sm_idx);
230 752946 : sm_state_map *new_smap = m_new_state->m_checker_states[sm_idx];
231 :
232 752946 : *out_smap = new_smap;
233 752946 : *out_sm = sm;
234 752946 : if (out_sm_idx)
235 752128 : *out_sm_idx = sm_idx;
236 752946 : if (out_sm_context)
237 : {
238 748 : const sm_state_map *old_smap = m_old_state->m_checker_states[sm_idx];
239 748 : *out_sm_context
240 748 : = std::make_unique<impl_sm_context> (*m_eg,
241 : sm_idx,
242 : *sm,
243 748 : m_enode_for_diag,
244 748 : m_old_state,
245 748 : m_new_state,
246 : old_smap,
247 : new_smap,
248 748 : m_path_ctxt,
249 1496 : false);
250 : }
251 : return true;
252 : }
253 :
254 : /* Subclass of pending_location::fixer_for_epath for finding the best stmt
255 : to report the leak at, given the emission path. */
256 :
257 : class leak_ploc_fixer_for_epath : public pending_location::fixer_for_epath
258 : {
259 : public:
260 1310 : leak_ploc_fixer_for_epath (const exploded_graph &eg, tree var)
261 1310 : : m_eg (eg), m_var (var)
262 : {}
263 :
264 : void
265 1194 : fixup_for_epath (const exploded_path &epath,
266 : pending_location &ploc) const final override
267 : {
268 1194 : logger * const logger = m_eg.get_logger ();
269 1194 : LOG_FUNC (logger);
270 :
271 : /* Handle the interprocedural case where we leak the retval at a return
272 : because the caller discards the return value. */
273 1194 : if (m_var
274 974 : && TREE_CODE (m_var) == RESULT_DECL)
275 : {
276 6 : auto &point = ploc.m_enode->get_point ();
277 12 : if (point.get_stack_depth () > 1)
278 6 : if (point.get_supernode ()->exit_p ())
279 : {
280 : /* Get the program_point for the call within the caller. */
281 6 : auto &cs = point.get_call_string ();
282 6 : auto caller_snode = cs.get_return_node_in_caller ();
283 6 : gcc_assert (caller_snode);
284 6 : program_point caller_point (caller_snode, *cs.get_parent ());
285 6 : ploc.m_event_loc_info = event_loc_info (caller_point);
286 6 : return;
287 : }
288 : }
289 :
290 : /* Handle the case where we have e.g.:
291 : | var = malloc (N);
292 : | var = NULL;
293 : which with SSA becomes e.g.:
294 : | var_0 = malloc (N);
295 : | var_1 = nullptr;
296 : and thus leads to the leak being found at the enode where "var_0" goes
297 : out of scope.
298 : Fix up the location of the leak to report it at the write of NULL. */
299 1188 : if (m_var && TREE_CODE (m_var) == SSA_NAME)
300 : {
301 776 : log_scope sentinel (logger, "looking for write to sibling SSA name");
302 : /* Locate the final write to this SSA name in the path. */
303 776 : const gimple *def_stmt = SSA_NAME_DEF_STMT (m_var);
304 :
305 776 : int idx_of_def_stmt;
306 776 : if (epath.find_stmt_backwards (def_stmt, &idx_of_def_stmt))
307 : {
308 : /* What was the next write to the underlying var
309 : after the SSA name was set? (if any). */
310 :
311 668 : for (unsigned idx = idx_of_def_stmt + 1;
312 7560 : idx < epath.m_elements.size ();
313 : ++idx)
314 : {
315 6894 : const exploded_edge *eedge = epath.m_elements[idx].m_eedge;
316 6894 : if (logger)
317 0 : logger->log ("eedge[%i]: EN %i -> EN %i",
318 : idx,
319 0 : eedge->m_src->m_index,
320 0 : eedge->m_dest->m_index);
321 6894 : const gimple *stmt = eedge->maybe_get_stmt ();
322 6894 : if (!stmt)
323 2142 : continue;
324 8660 : if (const gassign *assign = dyn_cast <const gassign *> (stmt))
325 : {
326 1768 : tree lhs = gimple_assign_lhs (assign);
327 1768 : if (TREE_CODE (lhs) == SSA_NAME
328 2938 : && SSA_NAME_VAR (lhs) == SSA_NAME_VAR (m_var))
329 : {
330 2 : if (logger)
331 0 : logger->log ("using location 0%lx from gassign",
332 0 : assign->location);
333 2 : ploc.m_event_loc_info.m_loc = assign->location;
334 2 : return;
335 : }
336 : }
337 : }
338 : }
339 776 : }
340 :
341 : /* If the epath ends at a function exit node, the location is at
342 : the final "}". Try walking backward along EPATH, looking for a
343 : the first suitable stmt with a better location. */
344 1186 : gcc_assert (ploc.m_enode->get_supernode ());
345 1186 : const greturn *return_stmt = nullptr;
346 1186 : if (ploc.m_enode->get_supernode ()->exit_p ()
347 1186 : && has_return_stmt_p (epath, return_stmt, logger))
348 : {
349 : /* If we have "return SSA_NAME;" on EPATH, keep track of the
350 : pertinent SSA name as we walk backwards through EPATH. */
351 728 : tree retval = NULL_TREE;
352 728 : if (return_stmt)
353 728 : retval = gimple_return_retval (return_stmt);
354 :
355 728 : log_scope sentinel (logger, "walking backward along epath");
356 3022 : for (int idx = epath.m_elements.size () - 1; idx >= 0; --idx)
357 : {
358 2950 : const exploded_path::element_t &element = epath.m_elements[idx];
359 2950 : const exploded_edge *eedge = element.m_eedge;
360 2950 : if (logger)
361 : {
362 0 : logger->log ("eedge[%i]: EN %i -> EN %i",
363 : idx,
364 0 : eedge->m_src->m_index,
365 0 : eedge->m_dest->m_index);
366 0 : if (retval)
367 0 : logger->log (" retval: %qE", retval);
368 : }
369 2950 : if (auto op = eedge->maybe_get_op ())
370 : {
371 1904 : if (retval)
372 396 : if (auto phis = op->dyn_cast_phis_for_edge_op ())
373 : {
374 176 : for (auto iter : phis->get_pairs ())
375 88 : if (retval == iter.m_dst)
376 : {
377 : /* We have "PHI(RETVAL = SRC);"
378 : Track SRC instead */
379 88 : retval = iter.m_src;
380 88 : if (logger)
381 0 : logger->log ("updating retval to %qE", retval);
382 : }
383 : }
384 1904 : if (const gimple *stmt = op->maybe_get_stmt ())
385 1802 : if (consider_stmt_location_p (*stmt, retval))
386 866 : if (useful_location_p (stmt->location))
387 : {
388 656 : if (logger)
389 0 : logger->log ("using location 0x%lx from stmt",
390 0 : stmt->location);
391 656 : ploc.m_event_loc_info.m_loc = stmt->location;
392 656 : return;
393 : }
394 : }
395 : }
396 728 : }
397 1194 : }
398 :
399 : private:
400 : static bool
401 728 : has_return_stmt_p (const exploded_path &epath,
402 : const greturn *&out_greturn,
403 : logger *logger)
404 : {
405 728 : LOG_SCOPE (logger);
406 :
407 1252 : for (int idx = epath.m_elements.size () - 1; idx >= 0; --idx)
408 : {
409 1252 : const exploded_path::element_t &element = epath.m_elements[idx];
410 1252 : const exploded_edge *eedge = element.m_eedge;
411 1252 : if (eedge->m_src->get_stack_depth ()
412 2504 : != eedge->m_dest->get_stack_depth ())
413 : {
414 : /* We have interprocedural activity, and
415 : presumably are no longer in the function where
416 : EPATH terminates.
417 : Give up. */
418 : return false;
419 : }
420 1252 : if (auto op = eedge->maybe_get_op ())
421 : {
422 728 : switch (op->get_kind ())
423 : {
424 : default:
425 : break;
426 728 : case operation::kind::return_stmt:
427 728 : if (logger)
428 0 : logger->log ("found return_stmt");
429 728 : out_greturn = &((const greturn_op *)op)->get_greturn ();
430 728 : return true;
431 0 : case operation::kind::predict_stmt:
432 0 : {
433 0 : auto &stmt = ((const gimple_stmt_op *)op)->get_stmt ();
434 0 : switch (gimple_predict_predictor (&stmt))
435 : {
436 0 : case PRED_TREE_EARLY_RETURN:
437 : /* Assume this is due to a "return;" in the user's
438 : code. */
439 0 : if (logger)
440 0 : logger->log ("assuming a return: PRED_TREE_EARLY_RETURN");
441 0 : return true;
442 : default:
443 : break;
444 : }
445 : }
446 : break;
447 : }
448 : }
449 : }
450 : return false;
451 728 : }
452 :
453 : /* When certain statements show up on the epath of a leak
454 : at an exit node, if they have locations, these locations
455 : tend to be better locations for the leak.
456 : Return true for such statements (but without checking their
457 : locations). */
458 : static bool
459 1802 : consider_stmt_location_p (const gimple &stmt,
460 : tree retval)
461 : {
462 1802 : if (retval && TREE_CODE (retval) == SSA_NAME)
463 306 : if (&stmt == SSA_NAME_DEF_STMT (retval))
464 : return true;
465 :
466 1778 : switch (stmt.code)
467 : {
468 : default:
469 : break;
470 526 : case GIMPLE_CALL:
471 526 : {
472 526 : const gcall &call = *as_a <const gcall *> (&stmt);
473 526 : if (is_cxa_end_catch_p (call))
474 : return true;
475 : }
476 : break;
477 : case GIMPLE_PREDICT:
478 : case GIMPLE_RETURN:
479 : return true;
480 : }
481 : return false;
482 : }
483 :
484 : const exploded_graph &m_eg;
485 : tree m_var;
486 : };
487 :
488 : std::unique_ptr<pending_location::fixer_for_epath>
489 0 : make_ploc_fixer_for_epath_for_leak_diagnostic (const exploded_graph &eg,
490 : tree var)
491 : {
492 0 : return std::make_unique<leak_ploc_fixer_for_epath> (eg, var);
493 : }
494 :
495 : /* A measurement of how good EXPR is for presenting to the user, so
496 : that e.g. we can say prefer printing
497 : "leak of 'tmp.m_ptr'"
498 : over:
499 : "leak of '<unknown>'". */
500 :
501 : static int
502 14764 : readability (const_tree expr)
503 : {
504 : /* Arbitrarily-chosen "high readability" value. */
505 30522 : const int HIGH_READABILITY = 65536;
506 :
507 30522 : gcc_assert (expr);
508 30522 : switch (TREE_CODE (expr))
509 : {
510 5238 : case COMPONENT_REF:
511 5238 : case MEM_REF:
512 : /* Impose a slight readability penalty relative to that of
513 : operand 0. */
514 5238 : return readability (TREE_OPERAND (expr, 0)) - 16;
515 :
516 10162 : case SSA_NAME:
517 10162 : {
518 10162 : if (tree var = SSA_NAME_VAR (expr))
519 : {
520 6835 : if (DECL_ARTIFICIAL (var))
521 : {
522 : /* If we have an SSA name for an artificial var,
523 : only use it if it has a debug expr associated with
524 : it that fixup_tree_for_diagnostic can use. */
525 132 : if (VAR_P (var) && DECL_HAS_DEBUG_EXPR_P (var))
526 0 : return readability (DECL_DEBUG_EXPR (var)) - 1;
527 : }
528 : else
529 : {
530 : /* Slightly favor the underlying var over the SSA name to
531 : avoid having them compare equal. */
532 6703 : return readability (var) - 1;
533 : }
534 : }
535 : /* Avoid printing '<unknown>' for SSA names for temporaries. */
536 : return -1;
537 : }
538 9145 : break;
539 :
540 9145 : case PARM_DECL:
541 9145 : case VAR_DECL:
542 9145 : if (DECL_NAME (expr))
543 : return HIGH_READABILITY;
544 : else
545 : /* We don't want to print temporaries. For example, the C FE
546 : prints them as e.g. "<Uxxxx>" where "xxxx" is the low 16 bits
547 : of the tree pointer (see pp_c_tree_decl_identifier). */
548 : return -1;
549 :
550 : case RESULT_DECL:
551 : /* Printing "<return-value>" isn't ideal, but is less awful than
552 : trying to print a temporary. */
553 : return HIGH_READABILITY / 2;
554 :
555 3817 : case NOP_EXPR:
556 3817 : {
557 : /* Impose a moderate readability penalty for casts. */
558 3817 : const int CAST_PENALTY = 32;
559 3817 : return readability (TREE_OPERAND (expr, 0)) - CAST_PENALTY;
560 : }
561 :
562 : case INTEGER_CST:
563 : return HIGH_READABILITY;
564 :
565 184 : default:
566 184 : return 0;
567 : }
568 :
569 : return 0;
570 : }
571 :
572 : /* A qsort comparator for trees to sort them into most user-readable to
573 : least user-readable. */
574 :
575 : int
576 7382 : readability_comparator (const void *p1, const void *p2)
577 : {
578 7382 : path_var pv1 = *(path_var const *)p1;
579 7382 : path_var pv2 = *(path_var const *)p2;
580 :
581 7382 : const int tree_r1 = readability (pv1.m_tree);
582 7382 : const int tree_r2 = readability (pv2.m_tree);
583 :
584 : /* Favor items that are deeper on the stack and hence more recent;
585 : this also favors locals over globals. */
586 7382 : const int COST_PER_FRAME = 64;
587 7382 : const int depth_r1 = pv1.m_stack_depth * COST_PER_FRAME;
588 7382 : const int depth_r2 = pv2.m_stack_depth * COST_PER_FRAME;
589 :
590 : /* Combine the scores from the tree and from the stack depth.
591 : This e.g. lets us have a slightly penalized cast in the most
592 : recent stack frame "beat" an uncast value in an older stack frame. */
593 7382 : const int sum_r1 = tree_r1 + depth_r1;
594 7382 : const int sum_r2 = tree_r2 + depth_r2;
595 7382 : if (int cmp = sum_r2 - sum_r1)
596 : return cmp;
597 :
598 : /* Otherwise, more readable trees win. */
599 1022 : if (int cmp = tree_r2 - tree_r1)
600 : return cmp;
601 :
602 : /* Otherwise, if they have the same readability, then impose an
603 : arbitrary deterministic ordering on them. */
604 :
605 1022 : if (int cmp = TREE_CODE (pv1.m_tree) - TREE_CODE (pv2.m_tree))
606 : return cmp;
607 :
608 962 : switch (TREE_CODE (pv1.m_tree))
609 : {
610 : default:
611 : break;
612 858 : case SSA_NAME:
613 858 : if (int cmp = (SSA_NAME_VERSION (pv1.m_tree)
614 858 : - SSA_NAME_VERSION (pv2.m_tree)))
615 : return cmp;
616 : break;
617 0 : case PARM_DECL:
618 0 : case VAR_DECL:
619 0 : case RESULT_DECL:
620 0 : if (int cmp = DECL_UID (pv1.m_tree) - DECL_UID (pv2.m_tree))
621 : return cmp;
622 : break;
623 : }
624 :
625 : /* TODO: We ought to find ways of sorting such cases. */
626 : return 0;
627 : }
628 :
629 : /* Return true is SNODE is the EXIT node of a function, or is one
630 : of the final snodes within its function.
631 :
632 : Specifically, handle the final supernodes before the EXIT node,
633 : for the case of clobbers that happen immediately before exiting.
634 : We need a run of snodes leading to the return_p snode, where all edges are
635 : intraprocedural, and every snode has just one successor.
636 :
637 : We use this when suppressing leak reports at the end of "main". */
638 :
639 : static bool
640 1625 : returning_from_function_p (const supernode *snode)
641 : {
642 1625 : if (!snode)
643 : return false;
644 :
645 : unsigned count = 0;
646 : const supernode *iter = snode;
647 2626 : while (true)
648 : {
649 2626 : if (iter->exit_p ())
650 : return true;
651 1737 : if (iter->m_succs.length () != 1)
652 : return false;
653 1570 : const superedge *sedge = iter->m_succs[0];
654 :
655 1570 : if (auto op = sedge->get_op ())
656 1279 : if (op->get_kind () == operation::kind::return_stmt)
657 : return true;
658 :
659 1035 : iter = sedge->m_dest;
660 :
661 : /* Impose a limit to ensure we terminate for pathological cases.
662 :
663 : We only care about the final 3 nodes, due to cases like:
664 : BB:
665 : (clobber causing leak)
666 :
667 : BB:
668 : <label>:
669 : return _val;
670 :
671 : EXIT BB.*/
672 1035 : if (++count > 4)
673 : return false;
674 : }
675 : }
676 :
677 : /* Find the best tree for SVAL and call SM's on_leak vfunc with it.
678 : If on_leak returns a pending_diagnostic, queue it up to be reported,
679 : so that we potentially complain about a leak of SVAL in the given STATE. */
680 :
681 : void
682 1625 : impl_region_model_context::on_state_leak (const state_machine &sm,
683 : const svalue *sval,
684 : state_machine::state_t state)
685 : {
686 1625 : logger * const logger = get_logger ();
687 1625 : LOG_SCOPE (logger);
688 1625 : if (logger)
689 : {
690 0 : logger->start_log_line ();
691 0 : logger->log_partial ("considering leak of ");
692 0 : sval->dump_to_pp (logger->get_printer (), true);
693 0 : logger->end_log_line ();
694 : }
695 :
696 1625 : if (!m_eg)
697 : return;
698 :
699 : /* m_old_state also needs to be non-NULL so that the sm_ctxt can look
700 : up the old state of SVAL. */
701 1625 : gcc_assert (m_old_state);
702 :
703 : /* SVAL has leaked within the new state: it is not used by any reachable
704 : regions.
705 : We need to convert it back to a tree, but since it's likely no regions
706 : use it, we have to find the "best" tree for it in the old_state. */
707 1625 : svalue_set visited;
708 1625 : path_var leaked_pv
709 1625 : = m_old_state->m_region_model->get_representative_path_var (sval,
710 : &visited,
711 : nullptr);
712 :
713 : /* Strip off top-level casts */
714 1625 : if (leaked_pv.m_tree && TREE_CODE (leaked_pv.m_tree) == NOP_EXPR)
715 94 : leaked_pv.m_tree = TREE_OPERAND (leaked_pv.m_tree, 0);
716 :
717 : /* This might be NULL; the pending_diagnostic subclasses need to cope
718 : with this. */
719 1625 : tree leaked_tree = leaked_pv.m_tree;
720 1625 : if (logger)
721 : {
722 0 : if (leaked_tree)
723 0 : logger->log ("best leaked_tree: %qE", leaked_tree);
724 : else
725 0 : logger->log ("best leaked_tree: NULL");
726 : }
727 :
728 1625 : gcc_assert (m_enode_for_diag);
729 :
730 : /* Don't complain about leaks when returning from "main". */
731 1625 : if (returning_from_function_p (m_enode_for_diag->get_supernode ()))
732 : {
733 1458 : tree fndecl = m_enode_for_diag->get_function ()->decl;
734 1458 : if (id_equal (DECL_NAME (fndecl), "main"))
735 : {
736 66 : if (logger)
737 0 : logger->log ("not reporting leak from main");
738 66 : return;
739 : }
740 : }
741 :
742 1559 : tree leaked_tree_for_diag = fixup_tree_for_diagnostic (leaked_tree);
743 1559 : std::unique_ptr<pending_diagnostic> pd = sm.on_leak (leaked_tree_for_diag,
744 : m_old_state,
745 1559 : m_new_state);
746 1559 : if (pd)
747 : {
748 1310 : pending_location ploc (get_pending_location_for_diag ());
749 1310 : ploc.m_fixer_for_epath
750 1310 : = std::make_unique<leak_ploc_fixer_for_epath> (*m_eg, leaked_tree);
751 1310 : m_eg->get_diagnostic_manager ().add_diagnostic
752 1310 : (&sm, std::move (ploc),
753 : leaked_tree_for_diag, sval, state, std::move (pd));
754 1310 : }
755 1625 : }
756 :
757 : /* Implementation of region_model_context::on_condition vfunc.
758 : Notify all state machines about the condition, which could lead to
759 : state transitions. */
760 :
761 : void
762 35788 : impl_region_model_context::on_condition (const svalue *lhs,
763 : enum tree_code op,
764 : const svalue *rhs)
765 : {
766 35788 : int sm_idx;
767 35788 : sm_state_map *smap;
768 286094 : FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
769 : {
770 250306 : const state_machine &sm = m_ext_state.get_sm (sm_idx);
771 500612 : impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
772 : m_old_state, m_new_state,
773 500612 : m_old_state->m_checker_states[sm_idx],
774 500612 : m_new_state->m_checker_states[sm_idx],
775 250306 : m_path_ctxt);
776 250306 : sm.on_condition (sm_ctxt, lhs, op, rhs);
777 250306 : }
778 35788 : }
779 :
780 : /* Implementation of region_model_context::on_bounded_ranges vfunc.
781 : Notify all state machines about the ranges, which could lead to
782 : state transitions. */
783 :
784 : void
785 6100 : impl_region_model_context::on_bounded_ranges (const svalue &sval,
786 : const bounded_ranges &ranges)
787 : {
788 6100 : int sm_idx;
789 6100 : sm_state_map *smap;
790 48800 : FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
791 : {
792 42700 : const state_machine &sm = m_ext_state.get_sm (sm_idx);
793 85400 : impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
794 : m_old_state, m_new_state,
795 85400 : m_old_state->m_checker_states[sm_idx],
796 85400 : m_new_state->m_checker_states[sm_idx],
797 42700 : m_path_ctxt);
798 42700 : sm.on_bounded_ranges (sm_ctxt, sval, ranges);
799 42700 : }
800 6100 : }
801 :
802 : /* Implementation of region_model_context::on_pop_frame vfunc.
803 : Notify all state machines about the frame being popped, which
804 : could lead to states being discarded. */
805 :
806 : void
807 25930 : impl_region_model_context::on_pop_frame (const frame_region *frame_reg)
808 : {
809 25930 : int sm_idx;
810 25930 : sm_state_map *smap;
811 207072 : FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
812 : {
813 181142 : const state_machine &sm = m_ext_state.get_sm (sm_idx);
814 181142 : sm.on_pop_frame (smap, frame_reg);
815 : }
816 25930 : }
817 :
818 : /* Implementation of region_model_context::on_phi vfunc.
819 : Notify all state machines about the phi, which could lead to
820 : state transitions. */
821 :
822 : void
823 0 : impl_region_model_context::on_phi (const gphi *phi, tree rhs)
824 : {
825 0 : int sm_idx;
826 0 : sm_state_map *smap;
827 0 : FOR_EACH_VEC_ELT (m_new_state->m_checker_states, sm_idx, smap)
828 : {
829 0 : const state_machine &sm = m_ext_state.get_sm (sm_idx);
830 0 : impl_sm_context sm_ctxt (*m_eg, sm_idx, sm, m_enode_for_diag,
831 : m_old_state, m_new_state,
832 0 : m_old_state->m_checker_states[sm_idx],
833 0 : m_new_state->m_checker_states[sm_idx],
834 0 : m_path_ctxt);
835 0 : sm.on_phi (sm_ctxt, phi, rhs);
836 0 : }
837 0 : }
838 :
839 : /* Implementation of region_model_context::on_unexpected_tree_code vfunc.
840 : Mark the new state as being invalid for further exploration.
841 : TODO(stage1): introduce a warning for when this occurs. */
842 :
843 : void
844 52 : impl_region_model_context::on_unexpected_tree_code (tree t,
845 : const dump_location_t &loc)
846 : {
847 52 : logger * const logger = get_logger ();
848 52 : if (logger)
849 : {
850 2 : const dump_impl_location_t &impl_loc = loc.get_impl_location ();
851 2 : const char *unknown = "<unknown>";
852 2 : logger->log ("unhandled tree code: %qs in %qs at %s:%i",
853 2 : get_tree_code_name (TREE_CODE (t)),
854 2 : impl_loc.m_function ? impl_loc.m_function : unknown,
855 2 : impl_loc.m_file ? impl_loc.m_file : unknown,
856 2 : impl_loc.m_line);
857 : }
858 52 : if (m_new_state)
859 52 : m_new_state->m_valid = false;
860 52 : }
861 :
862 : /* Implementation of region_model_context::maybe_did_work vfunc.
863 : Mark that "externally visible work" has occurred, and thus we
864 : shouldn't report an infinite loop here. */
865 :
866 : void
867 24979 : impl_region_model_context::maybe_did_work ()
868 : {
869 24979 : if (m_out_could_have_done_work)
870 23440 : *m_out_could_have_done_work = true;
871 24979 : }
872 :
873 : pending_location
874 4519 : impl_region_model_context::get_pending_location_for_diag () const
875 : {
876 4519 : if (m_stmt && useful_location_p (m_stmt->location))
877 3193 : return pending_location (m_enode_for_diag, m_stmt->location);
878 : else
879 1326 : return pending_location (m_enode_for_diag);
880 : }
881 :
882 : /* struct point_and_state. */
883 :
884 : /* Assert that this object is sane. */
885 :
886 : void
887 832671 : point_and_state::validate (const extrinsic_state &ext_state) const
888 : {
889 : /* Skip this in a release build. */
890 : #if !CHECKING_P
891 : return;
892 : #endif
893 :
894 832671 : m_point.validate ();
895 :
896 832671 : m_state.validate (ext_state);
897 :
898 : /* Verify that the callstring's model of the stack corresponds to that
899 : of the region_model. */
900 : /* They should have the same depth. */
901 1658398 : gcc_assert (m_point.get_stack_depth ()
902 : == m_state.m_region_model->get_stack_depth ());
903 : /* Check the functions in the callstring vs those in the frames
904 : at each depth. */
905 1251331 : for (const frame_region *iter_frame
906 832671 : = m_state.m_region_model->get_current_frame ();
907 2084002 : iter_frame; iter_frame = iter_frame->get_calling_frame ())
908 : {
909 1251331 : int index = iter_frame->get_index ();
910 1251331 : gcc_assert (m_point.get_function_at_depth (index)
911 : == &iter_frame->get_function ());
912 : }
913 832671 : }
914 :
915 : /* Subroutine of print_enode_indices: print a run of indices from START_IDX
916 : to END_IDX to PP, using and updating *FIRST_RUN. */
917 :
918 : static void
919 9879 : print_run (pretty_printer *pp, int start_idx, int end_idx,
920 : bool *first_run)
921 : {
922 9879 : if (!(*first_run))
923 6963 : pp_string (pp, ", ");
924 9879 : *first_run = false;
925 9879 : if (start_idx == end_idx)
926 6892 : pp_printf (pp, "EN: %i", start_idx);
927 : else
928 2987 : pp_printf (pp, "EN: %i-%i", start_idx, end_idx);
929 9879 : }
930 :
931 : /* Print the indices within ENODES to PP, collecting them as
932 : runs/singletons e.g. "EN: 4-7, EN: 20-23, EN: 42". */
933 :
934 : static void
935 2928 : print_enode_indices (pretty_printer *pp,
936 : const auto_vec<exploded_node *> &enodes)
937 : {
938 2928 : int cur_start_idx = -1;
939 2928 : int cur_finish_idx = -1;
940 2928 : bool first_run = true;
941 2928 : unsigned i;
942 2928 : exploded_node *enode;
943 21553 : FOR_EACH_VEC_ELT (enodes, i, enode)
944 : {
945 18625 : if (cur_start_idx == -1)
946 : {
947 2916 : gcc_assert (cur_finish_idx == -1);
948 2916 : cur_start_idx = cur_finish_idx = enode->m_index;
949 : }
950 : else
951 : {
952 15709 : if (enode->m_index == cur_finish_idx + 1)
953 : /* Continuation of a run. */
954 : cur_finish_idx = enode->m_index;
955 : else
956 : {
957 : /* Finish existing run, start a new one. */
958 6963 : gcc_assert (cur_start_idx >= 0);
959 6963 : gcc_assert (cur_finish_idx >= 0);
960 6963 : print_run (pp, cur_start_idx, cur_finish_idx,
961 : &first_run);
962 6963 : cur_start_idx = cur_finish_idx = enode->m_index;
963 : }
964 : }
965 : }
966 : /* Finish any existing run. */
967 2928 : if (cur_start_idx >= 0)
968 : {
969 2916 : gcc_assert (cur_finish_idx >= 0);
970 2916 : print_run (pp, cur_start_idx, cur_finish_idx,
971 : &first_run);
972 : }
973 2928 : }
974 :
975 : /* struct eg_traits::dump_args_t. */
976 :
977 : /* The <FILENAME>.eg.dot output can quickly become unwieldy if we show
978 : full details for all enodes (both in terms of CPU time to render it,
979 : and in terms of being meaningful to a human viewing it).
980 :
981 : If we show just the IDs then the resulting graph is usually viewable,
982 : but then we have to keep switching back and forth between the .dot
983 : view and other dumps.
984 :
985 : This function implements a heuristic for showing detail at the enodes
986 : that (we hope) matter, and just the ID at other enodes, fixing the CPU
987 : usage of the .dot viewer, and drawing the attention of the viewer
988 : to these enodes.
989 :
990 : Return true if ENODE should be shown in detail in .dot output.
991 : Return false if no detail should be shown for ENODE. */
992 :
993 : bool
994 507 : eg_traits::dump_args_t::show_enode_details_p (const exploded_node &enode) const
995 : {
996 : /* If the number of exploded nodes isn't too large, we may as well show
997 : all enodes in full detail in the .dot output. */
998 507 : if (m_eg.m_nodes.length ()
999 507 : <= (unsigned) param_analyzer_max_enodes_for_full_dump)
1000 : return true;
1001 :
1002 : /* Otherwise, assume that what's most interesting are state explosions,
1003 : and thus the places where this happened.
1004 : Expand enodes at program points where we hit the per-enode limit, so we
1005 : can investigate what exploded. */
1006 0 : const per_program_point_data *per_point_data
1007 0 : = m_eg.get_per_program_point_data (enode.get_point ());
1008 0 : return per_point_data->m_excess_enodes > 0;
1009 : }
1010 :
1011 : /* class exploded_node : public dnode<eg_traits>. */
1012 :
1013 : const char *
1014 0 : exploded_node::status_to_str (enum status s)
1015 : {
1016 0 : switch (s)
1017 : {
1018 0 : default: gcc_unreachable ();
1019 : case status::worklist: return "worklist";
1020 0 : case status::processed: return "processed";
1021 0 : case status::special: return "special";
1022 0 : case status::merger: return "merger";
1023 0 : case status::bulk_merged: return "bulk_merged";
1024 : }
1025 : }
1026 :
1027 : /* exploded_node's ctor. */
1028 :
1029 412650 : exploded_node::exploded_node (const point_and_state &ps,
1030 412650 : int index)
1031 412650 : : m_ps (ps), m_status (status::worklist), m_index (index)
1032 : {
1033 412650 : gcc_checking_assert (ps.get_state ().m_region_model->canonicalized_p ());
1034 412650 : }
1035 :
1036 : /* For use by dump_dot, get a value for the .dot "fillcolor" attribute.
1037 : Colorize by sm-state, to make it easier to see how sm-state propagates
1038 : through the exploded_graph. */
1039 :
1040 : const char *
1041 1014 : exploded_node::get_dot_fillcolor () const
1042 : {
1043 1014 : const program_state &state = get_state ();
1044 :
1045 : /* We want to be able to easily distinguish the no-sm-state case,
1046 : and to be able to distinguish cases where there's a single state
1047 : from each other.
1048 :
1049 : Sum the sm_states, and use the result to choose from a table,
1050 : modulo table-size, special-casing the "no sm-state" case. */
1051 1014 : int total_sm_state = 0;
1052 1014 : int i;
1053 1014 : sm_state_map *smap;
1054 8112 : FOR_EACH_VEC_ELT (state.m_checker_states, i, smap)
1055 : {
1056 8450 : for (sm_state_map::iterator_t iter = smap->begin ();
1057 8450 : iter != smap->end ();
1058 1352 : ++iter)
1059 1352 : total_sm_state += (*iter).second.m_state->get_id ();
1060 7098 : total_sm_state += smap->get_global_state ()->get_id ();
1061 : }
1062 :
1063 1014 : if (total_sm_state > 0)
1064 : {
1065 : /* An arbitrarily-picked collection of light colors. */
1066 752 : const char * const colors[]
1067 : = {"azure", "coral", "cornsilk", "lightblue", "yellow",
1068 : "honeydew", "lightpink", "lightsalmon", "palegreen1",
1069 : "wheat", "seashell"};
1070 752 : const int num_colors = ARRAY_SIZE (colors);
1071 752 : return colors[total_sm_state % num_colors];
1072 : }
1073 : else
1074 : /* No sm-state. */
1075 : return "lightgrey";
1076 : }
1077 :
1078 : /* Implementation of dnode::dump_dot vfunc for exploded_node. */
1079 :
1080 : void
1081 507 : exploded_node::dump_dot (graphviz_out *gv, const dump_args_t &args) const
1082 : {
1083 507 : pretty_printer *pp = gv->get_pp ();
1084 :
1085 507 : dump_dot_id (pp);
1086 507 : pp_printf (pp, " [shape=none,margin=0,style=filled,fillcolor=%s,label=\"",
1087 : get_dot_fillcolor ());
1088 507 : pp_write_text_to_stream (pp);
1089 :
1090 507 : pp_printf (pp, "EN: %i", m_index);
1091 507 : if (m_status == status::merger)
1092 12 : pp_string (pp, " (merger)");
1093 495 : else if (m_status == status::bulk_merged)
1094 0 : pp_string (pp, " (bulk merged)");
1095 507 : pp_newline (pp);
1096 :
1097 507 : if (args.show_enode_details_p (*this))
1098 : {
1099 507 : format f (true);
1100 507 : m_ps.get_point ().print (pp, f);
1101 507 : pp_newline (pp);
1102 :
1103 507 : bool show_state = true;
1104 :
1105 : /* Don't show the state if we have a single predecessor
1106 : and the state hasn't changed. */
1107 507 : if (m_preds.length () == 1
1108 499 : && get_state () == m_preds[0]->m_src->get_state ())
1109 : show_state = false;
1110 :
1111 393 : if (show_state)
1112 : {
1113 393 : const extrinsic_state &ext_state = args.m_eg.get_ext_state ();
1114 393 : const program_state &state = m_ps.get_state ();
1115 393 : state.dump_to_pp (ext_state, false, true, pp);
1116 393 : pp_newline (pp);
1117 : }
1118 : }
1119 :
1120 507 : dump_saved_diagnostics (pp);
1121 :
1122 507 : args.dump_extra_info (this, pp);
1123 :
1124 507 : pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/true);
1125 :
1126 507 : pp_string (pp, "\"];\n\n");
1127 :
1128 : /* It can be hard to locate the saved diagnostics as text within the
1129 : enode nodes, so add extra nodes to the graph for each saved_diagnostic,
1130 : highlighted in red.
1131 : Compare with dump_saved_diagnostics. */
1132 507 : {
1133 507 : unsigned i;
1134 507 : const saved_diagnostic *sd;
1135 1022 : FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
1136 : {
1137 8 : sd->dump_as_dot_node (pp);
1138 :
1139 : /* Add edge connecting this enode to the saved_diagnostic. */
1140 8 : dump_dot_id (pp);
1141 8 : pp_string (pp, " -> ");
1142 8 : sd->dump_dot_id (pp);
1143 8 : pp_string (pp, " [style=\"dotted\" arrowhead=\"none\"];");
1144 8 : pp_newline (pp);
1145 : }
1146 : }
1147 :
1148 507 : pp_flush (pp);
1149 507 : }
1150 :
1151 : /* Dump any saved_diagnostics at this enode to PP. */
1152 :
1153 : void
1154 581 : exploded_node::dump_saved_diagnostics (pretty_printer *pp) const
1155 : {
1156 581 : unsigned i;
1157 581 : const saved_diagnostic *sd;
1158 593 : FOR_EACH_VEC_ELT (m_saved_diagnostics, i, sd)
1159 : {
1160 12 : pp_printf (pp, "DIAGNOSTIC: %s (sd: %i)",
1161 12 : sd->m_d->get_kind (), sd->get_index ());
1162 12 : pp_newline (pp);
1163 : }
1164 581 : }
1165 :
1166 : /* Dump this to PP in a form suitable for use as an id in .dot output. */
1167 :
1168 : void
1169 1545 : exploded_node::dump_dot_id (pretty_printer *pp) const
1170 : {
1171 1545 : pp_printf (pp, "exploded_node_%i", m_index);
1172 1545 : }
1173 :
1174 : /* Dump a multiline representation of this node to PP. */
1175 :
1176 : void
1177 0 : exploded_node::dump_to_pp (pretty_printer *pp,
1178 : const extrinsic_state &ext_state) const
1179 : {
1180 0 : pp_printf (pp, "EN: %i", m_index);
1181 0 : pp_newline (pp);
1182 :
1183 0 : format f (true);
1184 0 : m_ps.get_point ().print (pp, f);
1185 0 : pp_newline (pp);
1186 :
1187 0 : m_ps.get_state ().dump_to_pp (ext_state, false, true, pp);
1188 0 : pp_newline (pp);
1189 0 : }
1190 :
1191 : /* Dump a multiline representation of this node to FILE. */
1192 :
1193 : void
1194 0 : exploded_node::dump (FILE *fp,
1195 : const extrinsic_state &ext_state) const
1196 : {
1197 0 : tree_dump_pretty_printer pp (fp);
1198 0 : dump_to_pp (&pp, ext_state);
1199 0 : }
1200 :
1201 : /* Dump a multiline representation of this node to stderr. */
1202 :
1203 : DEBUG_FUNCTION void
1204 0 : exploded_node::dump (const extrinsic_state &ext_state) const
1205 : {
1206 0 : dump (stderr, ext_state);
1207 0 : }
1208 :
1209 : /* Return a new json::object of the form
1210 : {"point" : object for program_point,
1211 : "state" : object for program_state,
1212 : "status" : str,
1213 : "idx" : int}. */
1214 :
1215 : std::unique_ptr<json::object>
1216 0 : exploded_node::to_json (const extrinsic_state &ext_state) const
1217 : {
1218 0 : auto enode_obj = std::make_unique<json::object> ();
1219 :
1220 0 : enode_obj->set ("point", get_point ().to_json ());
1221 0 : enode_obj->set ("state", get_state ().to_json (ext_state));
1222 0 : enode_obj->set_string ("status", status_to_str (m_status));
1223 0 : enode_obj->set_integer ("idx", m_index);
1224 :
1225 0 : return enode_obj;
1226 : }
1227 :
1228 : } // namespace ana
1229 :
1230 : /* Return true if FNDECL has a gimple body. */
1231 : // TODO: is there a pre-canned way to do this?
1232 :
1233 : bool
1234 18451 : fndecl_has_gimple_body_p (tree fndecl)
1235 : {
1236 18451 : if (fndecl == NULL_TREE)
1237 : return false;
1238 :
1239 18451 : cgraph_node *n = cgraph_node::get (fndecl);
1240 18451 : if (!n)
1241 : return false;
1242 :
1243 18451 : return n->has_gimple_body_p ();
1244 : }
1245 :
1246 : namespace ana {
1247 :
1248 : /* Subclass of call_info for exploded edges that express
1249 : a throw or rethrow of an exception (actually a call
1250 : to __cxa_throw or __cxa_rethrow). */
1251 :
1252 : class throw_custom_edge : public call_info
1253 : {
1254 : public:
1255 187 : throw_custom_edge (const call_details &cd,
1256 : tree type,
1257 : bool is_rethrow)
1258 187 : : call_info (cd),
1259 187 : m_type (type),
1260 187 : m_is_rethrow (is_rethrow)
1261 : {
1262 : }
1263 :
1264 0 : void print (pretty_printer *pp) const final override
1265 : {
1266 0 : if (m_is_rethrow)
1267 : {
1268 0 : if (m_type)
1269 0 : pp_printf (pp, "rethrowing %qT", m_type);
1270 : else
1271 0 : pp_printf (pp, "rethrowing");
1272 : }
1273 : else
1274 : {
1275 0 : if (m_type)
1276 0 : pp_printf (pp, "throwing %qT", m_type);
1277 : else
1278 0 : pp_printf (pp, "throwing");
1279 : }
1280 0 : }
1281 :
1282 0 : void print_desc (pretty_printer &pp) const final override
1283 : {
1284 0 : print (&pp);
1285 0 : }
1286 :
1287 343 : bool update_model (region_model *model,
1288 : const exploded_edge *,
1289 : region_model_context *ctxt) const final override
1290 : {
1291 343 : if (m_is_rethrow)
1292 : {
1293 117 : if (auto eh_node = model->get_current_caught_exception ())
1294 84 : model->push_thrown_exception (*eh_node);
1295 : else
1296 : {
1297 : /* We have a rethrow of some unknown exception.
1298 : We don't have a good way of representing this;
1299 : leave the exception stack empty. */
1300 : }
1301 : }
1302 : else
1303 : {
1304 226 : call_details cd (get_call_details (model, ctxt));
1305 :
1306 226 : const svalue *exception_sval = cd.get_arg_svalue (0);
1307 226 : const svalue *tinfo_sval = cd.get_arg_svalue (1);
1308 226 : const svalue *destructor_sval = cd.get_arg_svalue (2);
1309 :
1310 : /* Push a new exception_node on the model's m_exception_stack. */
1311 226 : exception_node eh_node (exception_sval, tinfo_sval, destructor_sval);
1312 226 : model->push_thrown_exception (eh_node);
1313 : }
1314 :
1315 343 : return true;
1316 : }
1317 :
1318 69 : void add_events_to_path (checker_path *emission_path,
1319 : const exploded_edge &eedge,
1320 : pending_diagnostic &,
1321 : const state_transition *) const final override
1322 : {
1323 69 : const exploded_node *dst_node = eedge.m_dest;
1324 69 : const program_point &dst_point = dst_node->get_point ();
1325 69 : const int dst_stack_depth = dst_point.get_stack_depth ();
1326 :
1327 69 : const gcall &call = get_call_stmt ();
1328 :
1329 69 : emission_path->add_event
1330 69 : (std::make_unique<explicit_throw_event>
1331 69 : (event_loc_info (call.location,
1332 : dst_point.get_fndecl (),
1333 69 : dst_stack_depth),
1334 : dst_node,
1335 : call,
1336 69 : m_type,
1337 69 : m_is_rethrow));
1338 69 : }
1339 :
1340 : private:
1341 : tree m_type;
1342 : bool m_is_rethrow;
1343 : };
1344 :
1345 : /* Subclass of custom_edge_info for an exploded edge that expresses
1346 : unwinding one stack frame during exception handling. */
1347 :
1348 : class unwind_custom_edge : public custom_edge_info
1349 : {
1350 : public:
1351 6668 : unwind_custom_edge (location_t loc)
1352 6668 : : m_loc (loc)
1353 : {
1354 : }
1355 :
1356 0 : void print (pretty_printer *pp) const final override
1357 : {
1358 0 : pp_printf (pp, "unwinding frame");
1359 0 : }
1360 :
1361 6700 : bool update_model (region_model *model,
1362 : const exploded_edge *,
1363 : region_model_context *ctxt) const final override
1364 : {
1365 6700 : model->pop_frame (NULL_TREE, nullptr, ctxt, nullptr, false);
1366 6700 : return true;
1367 : }
1368 :
1369 16 : void add_events_to_path (checker_path *emission_path,
1370 : const exploded_edge &eedge,
1371 : pending_diagnostic &,
1372 : const state_transition *) const final override
1373 : {
1374 16 : const exploded_node *src_node = eedge.m_src;
1375 16 : const program_point &src_point = src_node->get_point ();
1376 16 : const int src_stack_depth = src_point.get_stack_depth ();
1377 16 : emission_path->add_event
1378 32 : (std::make_unique<unwind_event> (event_loc_info (m_loc,
1379 : src_point.get_fndecl (),
1380 16 : src_stack_depth)));
1381 16 : }
1382 :
1383 : private:
1384 : location_t m_loc;
1385 : };
1386 :
1387 : /* Locate an SNODE that's a CFG edge with the EH flag,
1388 : or return nullptr. */
1389 :
1390 : static const superedge *
1391 7530 : get_eh_outedge (const supernode &snode)
1392 : {
1393 27360 : for (auto out_sedge : snode.m_succs)
1394 6934 : if (::edge cfg_edge = out_sedge->get_any_cfg_edge ())
1395 1316 : if (cfg_edge->flags & EDGE_EH)
1396 : return out_sedge;
1397 :
1398 : // Not found
1399 : return nullptr;
1400 : }
1401 :
1402 : /* Given THROWN_ENODE, which expreses a throw or rethrow occurring at
1403 : THROW_STMT, unwind intraprocedurally and interprocedurally to find
1404 : the next eh_dispatch statement to handle exceptions, if any.
1405 :
1406 : Add eedges and enodes to this graph expressing the actions taken
1407 : to reach an enode containing the eh_dispatch stmt, if any.
1408 : Only the final enode is added to this graph's worklist.
1409 :
1410 : Use CTXT to warn about problems e.g. memory leaks due to stack frames
1411 : being unwound. */
1412 :
1413 : void
1414 6232 : exploded_graph::unwind_from_exception (exploded_node &thrown_enode,
1415 : const gimple *throw_stmt,
1416 : region_model_context *ctxt)
1417 : {
1418 6232 : logger * const logger = get_logger ();
1419 6232 : LOG_FUNC_1 (logger, "thrown EN: %i", thrown_enode.m_index);
1420 :
1421 : /* Iteratively unwind the stack looking for an out-cfg-edge
1422 : flagged EH. */
1423 6232 : exploded_node *iter_enode = &thrown_enode;
1424 6232 : while (iter_enode)
1425 : {
1426 : /* If we have an out-cfg-edge flagged EH, follow that,
1427 : presumably to a bb with a label and an eh_dispatch stmt.
1428 : Otherwise assume no out-cfgs-edges, and we are unwinding to the
1429 : caller. */
1430 7530 : if (auto sedge = get_eh_outedge (*iter_enode->get_supernode ()))
1431 : {
1432 : /* Intraprocedural case.
1433 : Assume we have an out-edge flagged with EH leading to
1434 : code for dispatch to catch handlers. */
1435 862 : const program_point next_point
1436 862 : (sedge->m_dest,
1437 862 : iter_enode->get_point ().get_call_string ());
1438 862 : exploded_node *next_enode
1439 862 : = get_or_create_node (next_point,
1440 : iter_enode->get_state (),
1441 : iter_enode,
1442 : /* Add this enode to the worklist. */
1443 : true);
1444 862 : if (!next_enode)
1445 : return;
1446 :
1447 862 : add_edge (iter_enode, next_enode, nullptr, false, nullptr);
1448 862 : return;
1449 : }
1450 : else
1451 : {
1452 : /* Interprocedural case.
1453 : No out-cfg-edge. Unwind one stack frame. */
1454 6668 : program_state unwound_state (iter_enode->get_state ());
1455 6668 : location_t loc = throw_stmt ? throw_stmt->location : UNKNOWN_LOCATION;
1456 6668 : auto unwind_edge_info
1457 6668 : = std::make_unique<unwind_custom_edge> (loc);
1458 6668 : unwind_edge_info->update_model (unwound_state.m_region_model, nullptr,
1459 : ctxt);
1460 :
1461 : /* Detect leaks in the new state relative to the old state.
1462 : Use an alternate ctxt that uses the original enode and the stmt
1463 : (if any) for the location of any diagnostics. */
1464 6668 : {
1465 6668 : uncertainty_t uncertainty;
1466 6668 : impl_region_model_context ctxt (*this,
1467 : &thrown_enode,
1468 6668 : &iter_enode->get_state (),
1469 : &unwound_state,
1470 : &uncertainty,
1471 : nullptr,
1472 6668 : throw_stmt);
1473 6668 : program_state::detect_leaks (iter_enode->get_state (),
1474 : unwound_state,
1475 : nullptr,
1476 : get_ext_state (), &ctxt);
1477 6668 : }
1478 6668 : const call_string &cs = iter_enode->get_point ().get_call_string ();
1479 6668 : if (cs.empty_p ())
1480 : {
1481 : /* Top-level stack frame in analysis: unwinding
1482 : to the outside world that called us. */
1483 : return;
1484 : }
1485 : else
1486 : {
1487 : /* Nested function in analysis: unwinding to
1488 : the callsite in the analysis (or beyond). */
1489 1714 : program_point unwound_point (cs.get_return_node_in_caller (), cs);
1490 1714 : unwound_point.pop_from_call_stack ();
1491 :
1492 1714 : exploded_node *after_unwind_enode
1493 1714 : = get_or_create_node (unwound_point,
1494 : std::move (unwound_state),
1495 : iter_enode,
1496 : /* Don't add this enode to the
1497 : worklist; we will process it
1498 : on the next iteration. */
1499 : false);
1500 :
1501 1714 : if (!after_unwind_enode)
1502 416 : return;
1503 :
1504 1298 : add_edge (iter_enode, after_unwind_enode, nullptr, true,
1505 1298 : std::move (unwind_edge_info));
1506 1298 : iter_enode = after_unwind_enode;
1507 : }
1508 6668 : }
1509 : }
1510 6232 : }
1511 :
1512 : /* Handle THROW_CALL, a call to __cxa_throw or __cxa_rethrow.
1513 :
1514 : Create an eedge and destination enode for the throw/rethrow, adding
1515 : them to this egraph. The new enode isn't added to the worklist, but
1516 : instead exploded_graph::unwind_from_exception is immediately called
1517 : on it, potentially creating more eedges and enodes leading to an
1518 : eh_handler stmt. */
1519 :
1520 : void
1521 190 : exploded_node::on_throw (exploded_graph &eg,
1522 : const gcall &throw_call,
1523 : const program_point &after_throw_point,
1524 : program_state *new_state,
1525 : bool is_rethrow,
1526 : region_model_context *ctxt)
1527 : {
1528 190 : region_model *model = new_state->m_region_model;
1529 190 : call_details cd (throw_call, model, ctxt);
1530 :
1531 190 : if (!cd.get_fndecl_for_call ())
1532 3 : return;
1533 :
1534 : /* Create an enode and eedge for the "throw". */
1535 187 : tree type = NULL_TREE;
1536 187 : if (is_rethrow)
1537 : {
1538 81 : const exception_node *eh_node = model->get_current_caught_exception ();
1539 66 : if (eh_node)
1540 66 : type = eh_node->maybe_get_type ();
1541 : else
1542 : {
1543 : /* We have a "throw;" but no exception to rethrow.
1544 : Presumably the top-level of the analysis is an
1545 : entrypoint for handling exceptions, so we will
1546 : simulate fully unwinding. */
1547 : }
1548 : }
1549 : else
1550 : {
1551 106 : const svalue *tinfo_sval = cd.get_arg_svalue (1);
1552 106 : type = tinfo_sval->maybe_get_type_from_typeinfo ();
1553 : }
1554 :
1555 187 : auto throw_edge_info
1556 187 : = std::make_unique<throw_custom_edge> (cd, type, is_rethrow);
1557 187 : throw_edge_info->update_model (model, nullptr, ctxt);
1558 :
1559 187 : exploded_node *after_throw_enode
1560 187 : = eg.get_or_create_node (after_throw_point, *new_state, this,
1561 : /* Don't add to worklist; we process
1562 : this immediately below. */
1563 : false);
1564 :
1565 187 : if (!after_throw_enode)
1566 0 : return;
1567 :
1568 : /* Create custom exploded_edge for a throw. */
1569 187 : eg.add_edge (this, after_throw_enode, nullptr, true,
1570 187 : std::move (throw_edge_info));
1571 :
1572 187 : eg.unwind_from_exception (*after_throw_enode, &throw_call, ctxt);
1573 187 : }
1574 :
1575 : /* Subroutine of exploded_graph::process_node for finding the successors
1576 : of the supernode for a function exit basic block.
1577 :
1578 : Ensure that pop_frame is called, potentially queuing diagnostics about
1579 : leaks. */
1580 :
1581 : void
1582 11896 : exploded_node::detect_leaks (exploded_graph &eg)
1583 : {
1584 11896 : LOG_FUNC_1 (eg.get_logger (), "EN: %i", m_index);
1585 :
1586 11896 : gcc_assert (get_point ().get_supernode ()->exit_p ());
1587 :
1588 : /* If we're not a "top-level" function, do nothing; pop_frame
1589 : will be called when handling the return superedge. */
1590 11896 : if (get_point ().get_stack_depth () > 1)
1591 0 : return;
1592 :
1593 : /* We have a "top-level" function. */
1594 11896 : gcc_assert (get_point ().get_stack_depth () == 1);
1595 :
1596 11896 : const program_state &old_state = get_state ();
1597 :
1598 : /* Work with a temporary copy of the state: pop the frame, and see
1599 : what leaks (via purge_unused_svalues). */
1600 11896 : program_state new_state (old_state);
1601 :
1602 11896 : gcc_assert (new_state.m_region_model);
1603 :
1604 11896 : uncertainty_t uncertainty;
1605 11896 : impl_region_model_context ctxt (eg, this,
1606 : &old_state, &new_state, &uncertainty, nullptr,
1607 11896 : nullptr);
1608 11896 : const svalue *result = nullptr;
1609 11896 : new_state.m_region_model->pop_frame (nullptr, &result, &ctxt, nullptr);
1610 11896 : program_state::detect_leaks (old_state, new_state, result,
1611 : eg.get_ext_state (), &ctxt);
1612 23792 : }
1613 :
1614 : /* Dump the successors and predecessors of this enode to OUTF. */
1615 :
1616 : void
1617 0 : exploded_node::dump_succs_and_preds (FILE *outf) const
1618 : {
1619 0 : unsigned i;
1620 0 : exploded_edge *e;
1621 0 : {
1622 0 : auto_vec<exploded_node *> preds (m_preds.length ());
1623 0 : FOR_EACH_VEC_ELT (m_preds, i, e)
1624 0 : preds.quick_push (e->m_src);
1625 0 : pretty_printer pp;
1626 0 : print_enode_indices (&pp, preds);
1627 0 : fprintf (outf, "preds: %s\n",
1628 : pp_formatted_text (&pp));
1629 0 : }
1630 0 : {
1631 0 : auto_vec<exploded_node *> succs (m_succs.length ());
1632 0 : FOR_EACH_VEC_ELT (m_succs, i, e)
1633 0 : succs.quick_push (e->m_dest);
1634 0 : pretty_printer pp;
1635 0 : print_enode_indices (&pp, succs);
1636 0 : fprintf (outf, "succs: %s\n",
1637 : pp_formatted_text (&pp));
1638 0 : }
1639 0 : }
1640 :
1641 : // class interprocedural_call : public custom_edge_info
1642 :
1643 : void
1644 8 : interprocedural_call::print (pretty_printer *pp) const
1645 : {
1646 8 : pp_string (pp, "call to ");
1647 8 : pp_gimple_stmt_1 (pp, &get_gcall (), 0, (dump_flags_t)0);
1648 8 : }
1649 :
1650 : void
1651 8 : interprocedural_call::get_dot_attrs (const char *&/*out_style*/,
1652 : const char *&out_color) const
1653 : {
1654 8 : out_color = "red";
1655 8 : }
1656 :
1657 : bool
1658 8276 : interprocedural_call::update_state (program_state *state,
1659 : const exploded_edge *eedge,
1660 : region_model_context *ctxt) const
1661 : {
1662 8276 : return update_model (state->m_region_model, eedge, ctxt);
1663 : }
1664 :
1665 : bool
1666 14503 : interprocedural_call::update_model (region_model *model,
1667 : const exploded_edge */*eedge*/,
1668 : region_model_context *ctxt) const
1669 : {
1670 14503 : model->update_for_gcall (get_gcall (), ctxt, &m_callee_fun);
1671 14503 : return true;
1672 : }
1673 :
1674 : void
1675 1347 : interprocedural_call::add_events_to_path (checker_path *emission_path,
1676 : const exploded_edge &eedge,
1677 : pending_diagnostic &pd,
1678 : const state_transition *state_trans) const
1679 : {
1680 1370 : pd.add_call_event (eedge, get_gcall (), *emission_path,
1681 : (state_trans
1682 23 : ? state_trans->dyn_cast_state_transition_at_call ()
1683 : : nullptr));
1684 1347 : }
1685 :
1686 : bool
1687 1193 : interprocedural_call::try_to_rewind_data_flow (rewind_context &ctxt) const
1688 : {
1689 1193 : auto logger = ctxt.m_logger;
1690 :
1691 : // Rewind from params to arguments
1692 1193 : if (ctxt.m_input.m_region_holding_value)
1693 : {
1694 57 : const region_model &dst_enode_model = ctxt.get_dst_region_model ();
1695 57 : tree dst_tree
1696 : = dst_enode_model.get_representative_tree
1697 57 : (ctxt.m_input.m_region_holding_value);
1698 57 : if (dst_tree)
1699 : {
1700 52 : callsite_expr expr;
1701 52 : tree src_tree
1702 52 : = m_op.map_expr_from_callee_to_caller (m_callee_fun.decl,
1703 : dst_tree,
1704 : &expr);
1705 52 : if (src_tree)
1706 : {
1707 23 : const region_model &src_enode_model = ctxt.get_src_region_model ();
1708 23 : ctxt.m_output.m_region_holding_value
1709 23 : = src_enode_model.get_lvalue (src_tree, nullptr);
1710 :
1711 23 : ctxt.add_state_transition
1712 23 : (std::make_unique<state_transition_at_call> (expr));
1713 :
1714 23 : if (logger)
1715 : {
1716 0 : callsite_expr_element e (expr);
1717 0 : logger->log ("updating m_region_holding_value from %qE to %qE"
1718 : " (callsite_expr: %e)",
1719 : dst_tree, src_tree, &e);
1720 0 : }
1721 : }
1722 : }
1723 : }
1724 :
1725 1193 : return true;
1726 : }
1727 :
1728 : const gcall &
1729 15858 : interprocedural_call::get_gcall () const
1730 : {
1731 15858 : return m_op.get_gcall ();
1732 : }
1733 :
1734 : // class interprocedural_return : public custom_edge_info
1735 :
1736 : void
1737 16 : interprocedural_return::print (pretty_printer *pp) const
1738 : {
1739 16 : pp_string (pp, "return from ");
1740 16 : pp_gimple_stmt_1 (pp, &m_call_stmt, 0, (dump_flags_t)0);
1741 16 : }
1742 :
1743 : void
1744 8 : interprocedural_return::get_dot_attrs (const char *&/*out_style*/,
1745 : const char *&out_color) const
1746 : {
1747 8 : out_color = "green";
1748 8 : }
1749 :
1750 : bool
1751 7344 : interprocedural_return::update_state (program_state *state,
1752 : const exploded_edge *eedge,
1753 : region_model_context *ctxt) const
1754 : {
1755 7344 : return update_model (state->m_region_model, eedge, ctxt);
1756 : }
1757 :
1758 : bool
1759 10880 : interprocedural_return::update_model (region_model *model,
1760 : const exploded_edge */*eedge*/,
1761 : region_model_context *ctxt) const
1762 : {
1763 10880 : model->update_for_return_gcall (m_call_stmt, ctxt);
1764 10880 : return true;
1765 : }
1766 :
1767 : void
1768 737 : interprocedural_return::add_events_to_path (checker_path *emission_path,
1769 : const exploded_edge &eedge,
1770 : pending_diagnostic &,
1771 : const state_transition *state_trans) const
1772 : {
1773 737 : const program_point &dst_point = eedge.m_dest->get_point ();
1774 737 : emission_path->add_event
1775 737 : (std::make_unique<return_event>
1776 737 : (eedge,
1777 1474 : event_loc_info (m_call_stmt.location,
1778 : dst_point.get_fndecl (),
1779 1474 : dst_point.get_stack_depth ()),
1780 : (state_trans
1781 737 : ? state_trans->dyn_cast_state_transition_at_return ()
1782 : : nullptr)));
1783 737 : }
1784 :
1785 : bool
1786 671 : interprocedural_return::try_to_rewind_data_flow (rewind_context &ctxt) const
1787 : {
1788 671 : auto logger = ctxt.m_logger;
1789 :
1790 671 : tree lhs = gimple_call_lhs (&m_call_stmt);
1791 671 : if (!lhs)
1792 : return true;
1793 :
1794 419 : const region_model &src_enode_model = ctxt.get_src_region_model ();
1795 419 : tree fndecl = src_enode_model.get_current_function ()->decl;
1796 419 : tree fn_result = DECL_RESULT (fndecl);
1797 :
1798 419 : ctxt.on_data_flow (DECL_RESULT (fndecl), lhs);
1799 :
1800 419 : return true;
1801 : }
1802 :
1803 : /* class exploded_edge : public dedge<eg_traits>. */
1804 :
1805 : /* exploded_edge's ctor. */
1806 :
1807 426907 : exploded_edge::exploded_edge (exploded_node *src, exploded_node *dest,
1808 : const superedge *sedge, bool could_do_work,
1809 426907 : std::unique_ptr<custom_edge_info> custom_info)
1810 426907 : : dedge<eg_traits> (src, dest), m_sedge (sedge),
1811 426907 : m_custom_info (std::move (custom_info)),
1812 426907 : m_could_do_work_p (could_do_work)
1813 : {
1814 426907 : }
1815 :
1816 : /* Implementation of dedge::dump_dot vfunc for exploded_edge.
1817 : Use the label of the underlying superedge, if any. */
1818 :
1819 : void
1820 515 : exploded_edge::dump_dot (graphviz_out *gv, const dump_args_t &) const
1821 : {
1822 515 : pretty_printer *pp = gv->get_pp ();
1823 :
1824 515 : m_src->dump_dot_id (pp);
1825 515 : pp_string (pp, " -> ");
1826 515 : m_dest->dump_dot_id (pp);
1827 515 : dump_dot_label (pp);
1828 515 : }
1829 :
1830 : /* Second half of exploded_edge::dump_dot. This is split out
1831 : for use by trimmed_graph::dump_dot and base_feasible_edge::dump_dot. */
1832 :
1833 : void
1834 585 : exploded_edge::dump_dot_label (pretty_printer *pp) const
1835 : {
1836 585 : const char *style = "\"solid,bold\"";
1837 585 : const char *color = "black";
1838 585 : int weight = 10;
1839 585 : const char *constraint = "true";
1840 :
1841 585 : if (m_sedge)
1842 : {
1843 539 : if (m_sedge->get_op ())
1844 426 : style = "\"solid\"";
1845 : else
1846 113 : style = "\"dotted\"";
1847 : }
1848 585 : if (m_custom_info)
1849 22 : m_custom_info->get_dot_attrs (style, color);
1850 :
1851 585 : pp_printf (pp,
1852 : (" [style=%s, color=%s, weight=%d, constraint=%s,"
1853 : " headlabel=\""),
1854 : style, color, weight, constraint);
1855 585 : pp_flush (pp);
1856 :
1857 585 : if (m_sedge)
1858 539 : m_sedge->dump_label_to_pp (pp, false);
1859 46 : else if (m_custom_info)
1860 14 : m_custom_info->print (pp);
1861 :
1862 1132 : pp_printf (pp, "%s",
1863 585 : could_do_work_p () ? "(could do work)" : "DOES NO WORK");
1864 :
1865 585 : pp_write_text_as_dot_label_to_stream (pp, /*for_record=*/false);
1866 :
1867 585 : pp_printf (pp, "\"];\n");
1868 585 : }
1869 :
1870 : /* Return a new json::object of the form
1871 : {"src_idx": int, the index of the source exploded edge,
1872 : "dst_idx": int, the index of the destination exploded edge,
1873 : "sedge": (optional) object for the superedge, if any,
1874 : "custom": (optional) str, a description, if this is a custom edge}. */
1875 :
1876 : std::unique_ptr<json::object>
1877 0 : exploded_edge::to_json () const
1878 : {
1879 0 : auto eedge_obj = std::make_unique<json::object> ();
1880 0 : eedge_obj->set_integer ("src_idx", m_src->m_index);
1881 0 : eedge_obj->set_integer ("dst_idx", m_dest->m_index);
1882 0 : if (m_sedge)
1883 0 : eedge_obj->set ("sedge", m_sedge->to_json ());
1884 0 : if (m_custom_info)
1885 : {
1886 0 : pretty_printer pp;
1887 0 : pp_format_decoder (&pp) = default_tree_printer;
1888 0 : m_custom_info->print (&pp);
1889 0 : eedge_obj->set_string ("custom", pp_formatted_text (&pp));
1890 0 : }
1891 0 : return eedge_obj;
1892 : }
1893 :
1894 : const gimple *
1895 17851 : exploded_edge::maybe_get_stmt () const
1896 : {
1897 17851 : auto op = maybe_get_op ();
1898 17851 : if (!op)
1899 : return nullptr;
1900 13703 : return op->maybe_get_stmt ();
1901 : }
1902 :
1903 : const operation *
1904 57913 : exploded_edge::maybe_get_op () const
1905 : {
1906 57913 : if (!m_sedge)
1907 : return nullptr;
1908 52837 : return m_sedge->get_op ();
1909 : }
1910 :
1911 : /* struct stats. */
1912 :
1913 : /* stats' ctor. */
1914 :
1915 27680 : stats::stats (int num_supernodes)
1916 27680 : : m_num_nodes (0),
1917 27680 : m_node_reuse_count (0),
1918 27680 : m_node_reuse_after_merge_count (0),
1919 27680 : m_num_supernodes (num_supernodes)
1920 : {
1921 27680 : }
1922 :
1923 : /* Log these stats in multiline form to LOGGER. */
1924 :
1925 : void
1926 11 : stats::log (logger *logger) const
1927 : {
1928 11 : gcc_assert (logger);
1929 11 : logger->log ("m_num_nodes: %i", m_num_nodes);
1930 11 : logger->log ("m_node_reuse_count: %i", m_node_reuse_count);
1931 11 : logger->log ("m_node_reuse_after_merge_count: %i",
1932 11 : m_node_reuse_after_merge_count);
1933 11 : }
1934 :
1935 : /* Dump these stats in multiline form to OUT. */
1936 :
1937 : void
1938 0 : stats::dump (FILE *out) const
1939 : {
1940 0 : fprintf (out, "m_num_nodes: %i\n", m_num_nodes);
1941 0 : fprintf (out, "m_node_reuse_count: %i\n", m_node_reuse_count);
1942 0 : fprintf (out, "m_node_reuse_after_merge_count: %i\n",
1943 0 : m_node_reuse_after_merge_count);
1944 :
1945 0 : if (m_num_supernodes > 0)
1946 0 : fprintf (out, "enodes per supernode: %.2f\n",
1947 0 : (float)m_num_nodes / (float)m_num_supernodes);
1948 0 : }
1949 :
1950 : /* Return the total number of enodes recorded within this object. */
1951 :
1952 : int
1953 6 : stats::get_total_enodes () const
1954 : {
1955 6 : return m_num_nodes;
1956 : }
1957 :
1958 : /* struct per_function_data. */
1959 :
1960 367 : per_function_data::~per_function_data ()
1961 : {
1962 1732 : for (auto iter : m_summaries)
1963 631 : delete iter;
1964 367 : }
1965 :
1966 : void
1967 631 : per_function_data::add_call_summary (exploded_node *node)
1968 : {
1969 631 : m_summaries.safe_push (new call_summary (this, node));
1970 631 : }
1971 :
1972 : /* strongly_connected_components's ctor. Tarjan's SCC algorithm. */
1973 :
1974 3474 : strongly_connected_components::
1975 3474 : strongly_connected_components (const supergraph &sg, logger *logger)
1976 6944 : : m_sg (sg), m_per_node (m_sg.m_nodes.length ())
1977 : {
1978 3474 : LOG_SCOPE (logger);
1979 3474 : auto_timevar tv (TV_ANALYZER_SCC);
1980 :
1981 194359 : for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
1982 190885 : m_per_node.quick_push (per_node_data ());
1983 :
1984 201299 : for (auto snode : m_sg.m_nodes)
1985 190885 : if (m_per_node[snode->m_id].m_id == -1)
1986 10792 : strong_connect (snode->m_id, logger);
1987 :
1988 3474 : if (0)
1989 : dump ();
1990 3474 : }
1991 :
1992 : /* Dump this object to stderr. */
1993 :
1994 : DEBUG_FUNCTION void
1995 0 : strongly_connected_components::dump () const
1996 : {
1997 0 : fprintf (stderr, "Stack: [");
1998 0 : bool first = true;
1999 0 : for (auto i : m_stack)
2000 : {
2001 0 : if (first)
2002 : first = false;
2003 : else
2004 0 : fprintf (stderr, ", ");
2005 0 : fprintf (stderr, "%i", i);
2006 : }
2007 0 : fprintf (stderr, "]\n");
2008 0 : for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
2009 : {
2010 0 : const per_node_data &v = m_per_node[i];
2011 0 : fprintf (stderr, "SN %lu: index: %i lowlink: %i on_stack: %i\n",
2012 0 : i, v.m_id, v.m_lowlink, v.m_on_stack);
2013 : }
2014 0 : }
2015 :
2016 : /* Return a new json::array of per-snode SCC ids. */
2017 :
2018 : std::unique_ptr<json::array>
2019 0 : strongly_connected_components::to_json () const
2020 : {
2021 0 : auto scc_arr = std::make_unique<json::array> ();
2022 0 : for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
2023 0 : scc_arr->append (std::make_unique<json::integer_number> (get_scc_id (i)));
2024 0 : return scc_arr;
2025 : }
2026 :
2027 : /* Subroutine of strongly_connected_components's ctor, part of Tarjan's
2028 : SCC algorithm. */
2029 :
2030 : void
2031 190885 : strongly_connected_components::strong_connect (unsigned id,
2032 : logger *logger)
2033 : {
2034 190885 : supernode *v_snode = m_sg.m_nodes[id];
2035 190885 : if (!v_snode)
2036 190885 : return;
2037 :
2038 : /* Set the depth index for v to the smallest unused index. */
2039 190885 : per_node_data *v = &m_per_node[id];
2040 190885 : v->m_id = id;
2041 190885 : v->m_lowlink = id;
2042 190885 : m_stack.safe_push (id);
2043 190885 : v->m_on_stack = true;
2044 190885 : id++;
2045 :
2046 : /* Consider successors of v. */
2047 190885 : unsigned i;
2048 190885 : superedge *sedge;
2049 381242 : FOR_EACH_VEC_ELT (v_snode->m_succs, i, sedge)
2050 : {
2051 190357 : supernode *w_snode = sedge->m_dest;
2052 190357 : per_node_data *w = &m_per_node[w_snode->m_id];
2053 190357 : if (w->m_id == -1)
2054 : {
2055 : /* Successor w has not yet been visited; recurse on it. */
2056 180093 : strong_connect (w_snode->m_id, logger);
2057 180093 : v->m_lowlink = MIN (v->m_lowlink, w->m_lowlink);
2058 : }
2059 10264 : else if (w->m_on_stack)
2060 : {
2061 : /* Successor w is in stack S and hence in the current SCC
2062 : If w is not on stack, then (v, w) is a cross-edge in the DFS
2063 : tree and must be ignored. */
2064 2173 : v->m_lowlink = MIN (v->m_lowlink, w->m_id);
2065 : }
2066 : }
2067 :
2068 : /* If v is a root node, pop the stack and generate an SCC. */
2069 :
2070 190885 : if (v->m_lowlink == v->m_id)
2071 : {
2072 174647 : if (logger)
2073 50 : logger->log ("got SCC root node: SN %i", v->m_id);
2074 190885 : per_node_data *w;
2075 190885 : do {
2076 190885 : int id = m_stack.pop ();
2077 190885 : w = &m_per_node[id];
2078 190885 : w->m_on_stack = false;
2079 190885 : if (logger)
2080 80 : logger->log (" popping SN %i", w->m_id);
2081 190885 : } while (w != v);
2082 : }
2083 : }
2084 :
2085 : /* worklist's ctor. */
2086 :
2087 3474 : worklist::worklist (const exploded_graph &eg, const analysis_plan &plan)
2088 3474 : : m_scc (eg.get_supergraph (), eg.get_logger ()),
2089 3474 : m_plan (plan),
2090 3474 : m_queue (key_t (*this, nullptr))
2091 : {
2092 3474 : }
2093 :
2094 : /* Return the number of nodes in the worklist. */
2095 :
2096 : unsigned
2097 387222 : worklist::length () const
2098 : {
2099 387222 : return m_queue.nodes ();
2100 : }
2101 :
2102 : /* Return the next node in the worklist, removing it. */
2103 :
2104 : exploded_node *
2105 404157 : worklist::take_next ()
2106 : {
2107 404157 : return m_queue.extract_min ();
2108 : }
2109 :
2110 : /* Return the next node in the worklist without removing it. */
2111 :
2112 : exploded_node *
2113 426864 : worklist::peek_next ()
2114 : {
2115 426864 : return m_queue.min ();
2116 : }
2117 :
2118 : /* Add ENODE to the worklist. */
2119 :
2120 : void
2121 405195 : worklist::add_node (exploded_node *enode)
2122 : {
2123 405195 : gcc_assert (enode->get_status () == exploded_node::status::worklist);
2124 405195 : m_queue.insert (key_t (*this, enode), enode);
2125 405195 : }
2126 :
2127 : /* Comparator for implementing worklist::key_t comparison operators.
2128 : Return negative if KA is before KB
2129 : Return positive if KA is after KB
2130 : Return 0 if they are equal.
2131 :
2132 : The ordering of the worklist is critical for performance and for
2133 : avoiding node explosions. Ideally we want all enodes at a CFG join-point
2134 : with the same callstring to be sorted next to each other in the worklist
2135 : so that a run of consecutive enodes can be merged and processed "in bulk"
2136 : rather than individually or pairwise, minimizing the number of new enodes
2137 : created. */
2138 :
2139 : int
2140 1970331 : worklist::key_t::cmp (const worklist::key_t &ka, const worklist::key_t &kb)
2141 : {
2142 1970331 : const program_point &point_a = ka.m_enode->get_point ();
2143 1970331 : const program_point &point_b = kb.m_enode->get_point ();
2144 1970331 : const call_string &call_string_a = point_a.get_call_string ();
2145 1970331 : const call_string &call_string_b = point_b.get_call_string ();
2146 :
2147 : /* Order empty-callstring points with different functions based on the
2148 : analysis_plan, so that we generate summaries before they are used. */
2149 1970331 : if (flag_analyzer_call_summaries
2150 199898 : && call_string_a.empty_p ()
2151 176060 : && call_string_b.empty_p ()
2152 176060 : && point_a.get_function () != nullptr
2153 2095904 : && point_b.get_function () != nullptr
2154 2146011 : && point_a.get_function () != point_b.get_function ())
2155 : {
2156 50107 : if (int cmp = ka.m_worklist.m_plan.cmp_function (point_a.get_function (),
2157 : point_b.get_function ()))
2158 : return cmp;
2159 : }
2160 :
2161 : /* Sort by callstring, so that nodes with deeper call strings are processed
2162 : before those with shallower call strings.
2163 : If we have
2164 : splitting BB
2165 : / \
2166 : / \
2167 : fn call no fn call
2168 : \ /
2169 : \ /
2170 : join BB
2171 : then we want the path inside the function call to be fully explored up
2172 : to the return to the join BB before we explore on the "no fn call" path,
2173 : so that both enodes at the join BB reach the front of the worklist at
2174 : the same time and thus have a chance of being merged. */
2175 1920224 : int cs_cmp = call_string::cmp (call_string_a, call_string_b);
2176 1920224 : if (cs_cmp)
2177 : return cs_cmp;
2178 :
2179 : /* Order by SCC. */
2180 1717489 : int scc_id_a = ka.get_scc_id (ka.m_enode);
2181 1717489 : int scc_id_b = kb.get_scc_id (kb.m_enode);
2182 1717489 : if (scc_id_a != scc_id_b)
2183 1321579 : return scc_id_a - scc_id_b;
2184 :
2185 : /* If in same SCC, order by supernode index (an arbitrary but stable
2186 : ordering). */
2187 395910 : const supernode *snode_a = ka.m_enode->get_supernode ();
2188 395910 : const supernode *snode_b = kb.m_enode->get_supernode ();
2189 395910 : if (snode_a == nullptr)
2190 : {
2191 0 : if (snode_b != nullptr)
2192 : /* One is nullptr. */
2193 : return -1;
2194 : else
2195 : /* Both are nullptr. */
2196 0 : return 0;
2197 : }
2198 395910 : if (snode_b == nullptr)
2199 : /* One is nullptr. */
2200 : return 1;
2201 : /* Neither are nullptr. */
2202 392446 : gcc_assert (snode_a && snode_b);
2203 392446 : if (snode_a->m_bb->index != snode_b->m_bb->index)
2204 34951 : return snode_a->m_bb->index - snode_b->m_bb->index;
2205 357495 : if (snode_a->m_id != snode_b->m_id)
2206 54513 : return snode_a->m_id - snode_b->m_id;
2207 :
2208 302982 : gcc_assert (snode_a == snode_b);
2209 :
2210 : /* Otherwise, we ought to have the same program_point. */
2211 302982 : gcc_assert (point_a == point_b);
2212 :
2213 : const program_state &state_a = ka.m_enode->get_state ();
2214 : const program_state &state_b = kb.m_enode->get_state ();
2215 :
2216 : /* Sort by sm-state, so that identical sm-states are grouped
2217 : together in the worklist. */
2218 1334057 : for (unsigned sm_idx = 0; sm_idx < state_a.m_checker_states.length ();
2219 : ++sm_idx)
2220 : {
2221 1221096 : sm_state_map *smap_a = state_a.m_checker_states[sm_idx];
2222 1221096 : sm_state_map *smap_b = state_b.m_checker_states[sm_idx];
2223 :
2224 1221096 : if (int smap_cmp = sm_state_map::cmp (*smap_a, *smap_b))
2225 : return smap_cmp;
2226 : }
2227 :
2228 : /* Otherwise, we have two enodes at the same program point but with
2229 : different states. We don't have a good total ordering on states,
2230 : so order them by enode index, so that we have at least have a
2231 : stable sort. */
2232 112961 : return ka.m_enode->m_index - kb.m_enode->m_index;
2233 : }
2234 :
2235 : /* Return a new json::object of the form
2236 : {"scc" : [per-snode-IDs]}, */
2237 :
2238 : std::unique_ptr<json::object>
2239 0 : worklist::to_json () const
2240 : {
2241 0 : auto worklist_obj = std::make_unique<json::object> ();
2242 :
2243 0 : worklist_obj->set ("scc", m_scc.to_json ());
2244 :
2245 : /* The following field isn't yet being JSONified:
2246 : queue_t m_queue; */
2247 :
2248 0 : return worklist_obj;
2249 : }
2250 :
2251 : /* exploded_graph's ctor. */
2252 :
2253 3474 : exploded_graph::exploded_graph (const supergraph &sg, logger *logger,
2254 : const extrinsic_state &ext_state,
2255 : const state_purge_map *purge_map,
2256 : const analysis_plan &plan,
2257 3474 : int verbosity)
2258 3474 : : m_sg (sg), m_logger (logger),
2259 3474 : m_worklist (*this, plan),
2260 3474 : m_ext_state (ext_state),
2261 3474 : m_purge_map (purge_map),
2262 3474 : m_plan (plan),
2263 3474 : m_diagnostic_manager (logger, ext_state.get_engine (), verbosity),
2264 6944 : m_global_stats (m_sg.m_nodes.length ()),
2265 10418 : m_functionless_stats (m_sg.m_nodes.length ())
2266 : {
2267 6948 : m_origin = get_or_create_node
2268 3474 : (program_point::origin (*ext_state.get_model_manager ()),
2269 6948 : program_state (ext_state), nullptr);
2270 3474 : }
2271 :
2272 : /* exploded_graph's dtor. */
2273 :
2274 3474 : exploded_graph::~exploded_graph ()
2275 : {
2276 506240 : for (auto iter : m_per_point_data)
2277 502766 : delete iter.second;
2278 4208 : for (auto iter : m_per_function_data)
2279 367 : delete iter.second;
2280 17732 : for (auto iter : m_per_function_stats)
2281 10788 : delete iter.second;
2282 23362 : for (auto iter : m_per_call_string_data)
2283 9944 : delete iter.second;
2284 6948 : }
2285 :
2286 : /* Subroutine for use when implementing __attribute__((tainted_args))
2287 : on functions and on function pointer fields in structs.
2288 :
2289 : Called on STATE representing a call to FNDECL.
2290 : Mark all params of FNDECL in STATE as "tainted". Mark the value of all
2291 : regions pointed to by params of FNDECL as "tainted".
2292 :
2293 : Return true if successful; return false if the "taint" state machine
2294 : was not found. */
2295 :
2296 : static bool
2297 184 : mark_params_as_tainted (program_state *state, tree fndecl,
2298 : const extrinsic_state &ext_state)
2299 : {
2300 184 : unsigned taint_sm_idx;
2301 184 : if (!ext_state.get_sm_idx_by_name ("taint", &taint_sm_idx))
2302 : return false;
2303 184 : sm_state_map *smap = state->m_checker_states[taint_sm_idx];
2304 :
2305 184 : const state_machine &sm = ext_state.get_sm (taint_sm_idx);
2306 184 : state_machine::state_t tainted = sm.get_state_by_name ("tainted");
2307 :
2308 184 : region_model_manager *mgr = ext_state.get_model_manager ();
2309 :
2310 184 : function *fun = DECL_STRUCT_FUNCTION (fndecl);
2311 184 : gcc_assert (fun);
2312 :
2313 435 : for (tree iter_parm = DECL_ARGUMENTS (fndecl); iter_parm;
2314 251 : iter_parm = DECL_CHAIN (iter_parm))
2315 : {
2316 251 : tree param = iter_parm;
2317 251 : if (tree parm_default_ssa = ssa_default_def (fun, iter_parm))
2318 193 : param = parm_default_ssa;
2319 251 : const region *param_reg = state->m_region_model->get_lvalue (param, nullptr);
2320 251 : const svalue *init_sval = mgr->get_or_create_initial_value (param_reg);
2321 251 : smap->set_state (state->m_region_model, init_sval,
2322 : tainted, nullptr /*origin_new_sval*/, ext_state);
2323 251 : if (POINTER_TYPE_P (TREE_TYPE (param)))
2324 : {
2325 48 : const region *pointee_reg = mgr->get_symbolic_region (init_sval);
2326 : /* Mark "*param" as tainted. */
2327 48 : const svalue *init_pointee_sval
2328 48 : = mgr->get_or_create_initial_value (pointee_reg);
2329 48 : smap->set_state (state->m_region_model, init_pointee_sval,
2330 : tainted, nullptr /*origin_new_sval*/, ext_state);
2331 : }
2332 : }
2333 :
2334 : return true;
2335 : }
2336 :
2337 : /* Custom event for use by tainted_args_function_info when a function
2338 : has been marked with __attribute__((tainted_args)). */
2339 :
2340 : class tainted_args_function_custom_event : public custom_event
2341 : {
2342 : public:
2343 109 : tainted_args_function_custom_event (const event_loc_info &loc_info)
2344 109 : : custom_event (loc_info),
2345 109 : m_fndecl (loc_info.m_fndecl)
2346 : {
2347 : }
2348 :
2349 : void
2350 218 : print_desc (pretty_printer &pp) const final override
2351 : {
2352 218 : pp_printf (&pp,
2353 : "function %qE marked with %<__attribute__((tainted_args))%>",
2354 218 : m_fndecl);
2355 218 : }
2356 :
2357 : private:
2358 : tree m_fndecl;
2359 : };
2360 :
2361 : /* Custom exploded_edge info for top-level calls to a function
2362 : marked with __attribute__((tainted_args)). */
2363 :
2364 : class tainted_args_function_info : public custom_edge_info
2365 : {
2366 : public:
2367 174 : tainted_args_function_info (tree fndecl)
2368 174 : : m_fndecl (fndecl)
2369 : {}
2370 :
2371 0 : void print (pretty_printer *pp) const final override
2372 : {
2373 0 : pp_string (pp, "call to tainted_args function");
2374 0 : };
2375 :
2376 274 : bool update_model (region_model *model,
2377 : const exploded_edge *eedge,
2378 : region_model_context *ctxt) const final override
2379 : {
2380 274 : function *fun = eedge->m_dest->get_function ();
2381 274 : gcc_assert (fun);
2382 274 : model->push_frame (*fun, nullptr, nullptr, ctxt);
2383 274 : return true;
2384 : }
2385 :
2386 109 : void add_events_to_path (checker_path *emission_path,
2387 : const exploded_edge &,
2388 : pending_diagnostic &,
2389 : const state_transition *) const final override
2390 : {
2391 109 : emission_path->add_event
2392 109 : (std::make_unique<tainted_args_function_custom_event>
2393 109 : (event_loc_info (DECL_SOURCE_LOCATION (m_fndecl), m_fndecl, 0)));
2394 109 : }
2395 :
2396 : private:
2397 : tree m_fndecl;
2398 : };
2399 :
2400 : /* Ensure that there is an exploded_node representing an external call to
2401 : FUN, adding it to the worklist if creating it.
2402 :
2403 : Add an edge from the origin exploded_node to the function entrypoint
2404 : exploded_node.
2405 :
2406 : Return the exploded_node for the entrypoint to the function. */
2407 :
2408 : exploded_node *
2409 10936 : exploded_graph::add_function_entry (const function &fun)
2410 : {
2411 10936 : gcc_assert (gimple_has_body_p (fun.decl));
2412 :
2413 : /* Be idempotent. */
2414 10936 : function *key = const_cast<function *> (&fun);
2415 10936 : if (m_functions_with_enodes.contains (key))
2416 : {
2417 294 : logger * const logger = get_logger ();
2418 294 : if (logger)
2419 0 : logger->log ("entrypoint for %qE already exists", fun.decl);
2420 294 : return nullptr;
2421 : }
2422 :
2423 10642 : program_point point
2424 10642 : = program_point::from_function_entry (*m_ext_state.get_model_manager (),
2425 : m_sg, fun);
2426 10642 : program_state state (m_ext_state);
2427 10642 : state.push_frame (m_ext_state, fun);
2428 :
2429 10642 : std::unique_ptr<custom_edge_info> edge_info = nullptr;
2430 :
2431 10642 : if (lookup_attribute ("tainted_args", DECL_ATTRIBUTES (fun.decl)))
2432 : {
2433 174 : if (mark_params_as_tainted (&state, fun.decl, m_ext_state))
2434 174 : edge_info = std::make_unique<tainted_args_function_info> (fun.decl);
2435 : }
2436 :
2437 10642 : if (!state.m_valid)
2438 : return nullptr;
2439 :
2440 10642 : exploded_node *enode = get_or_create_node (point, state, nullptr);
2441 10642 : if (!enode)
2442 : return nullptr;
2443 :
2444 10634 : add_edge (m_origin, enode, nullptr, false, std::move (edge_info));
2445 :
2446 10634 : m_functions_with_enodes.add (key);
2447 :
2448 10634 : return enode;
2449 10642 : }
2450 :
2451 : /* Get or create an exploded_node for (POINT, STATE).
2452 : If a new node is created and ADD_TO_WORKLIST is true,
2453 : it is added to the worklist.
2454 :
2455 : Use ENODE_FOR_DIAG, a pre-existing enode, for any diagnostics
2456 : that need to be emitted (e.g. when purging state *before* we have
2457 : a new enode). */
2458 :
2459 : exploded_node *
2460 420624 : exploded_graph::get_or_create_node (const program_point &point,
2461 : const program_state &state,
2462 : exploded_node *enode_for_diag,
2463 : bool add_to_worklist)
2464 : {
2465 420624 : logger * const logger = get_logger ();
2466 420624 : LOG_FUNC (logger);
2467 420624 : if (logger)
2468 : {
2469 214 : format f (false);
2470 214 : pretty_printer *pp = logger->get_printer ();
2471 214 : logger->start_log_line ();
2472 214 : pp_string (pp, "point: ");
2473 214 : point.print (pp, f);
2474 214 : logger->end_log_line ();
2475 214 : logger->start_log_line ();
2476 214 : pp_string (pp, "state: ");
2477 214 : state.dump_to_pp (m_ext_state, true, false, pp);
2478 214 : logger->end_log_line ();
2479 : }
2480 :
2481 : /* Stop exploring paths for which we don't know how to effectively
2482 : model the state. */
2483 420624 : if (!state.m_valid)
2484 : {
2485 0 : if (logger)
2486 0 : logger->log ("invalid state; not creating node");
2487 0 : return nullptr;
2488 : }
2489 :
2490 420624 : if (point.get_call_string ().calc_recursion_depth ()
2491 420624 : > param_analyzer_max_recursion_depth)
2492 : {
2493 603 : if (logger)
2494 0 : logger->log ("rejecting node: recursion limit exceeded");
2495 603 : return nullptr;
2496 : }
2497 :
2498 836568 : auto_cfun sentinel (point.get_function ());
2499 :
2500 420021 : state.validate (get_ext_state ());
2501 :
2502 : //state.dump (get_ext_state ());
2503 :
2504 : /* Prune state to try to improve the chances of a cache hit,
2505 : avoiding generating redundant nodes. */
2506 420021 : uncertainty_t uncertainty;
2507 420021 : program_state pruned_state
2508 420021 : = state.prune_for_point (*this, point, enode_for_diag, &uncertainty);
2509 :
2510 420021 : pruned_state.validate (get_ext_state ());
2511 :
2512 : //pruned_state.dump (get_ext_state ());
2513 :
2514 420021 : if (logger)
2515 : {
2516 214 : pretty_printer *pp = logger->get_printer ();
2517 214 : logger->start_log_line ();
2518 214 : pp_string (pp, "pruned_state: ");
2519 214 : pruned_state.dump_to_pp (m_ext_state, true, false, pp);
2520 214 : logger->end_log_line ();
2521 214 : pruned_state.m_region_model->dump_to_pp (logger->get_printer (), true,
2522 : false);
2523 : }
2524 :
2525 836568 : stats *per_fn_stats = get_or_create_function_stats (point.get_function ());
2526 :
2527 420021 : stats *per_cs_stats
2528 420021 : = &get_or_create_per_call_string_data (point.get_call_string ())->m_stats;
2529 :
2530 420021 : point_and_state ps (point, pruned_state);
2531 420021 : ps.validate (m_ext_state);
2532 420021 : if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
2533 : {
2534 : /* An exploded_node for PS already exists. */
2535 3333 : if (logger)
2536 11 : logger->log ("reused EN: %i", (*slot)->m_index);
2537 3333 : m_global_stats.m_node_reuse_count++;
2538 3333 : per_fn_stats->m_node_reuse_count++;
2539 3333 : per_cs_stats->m_node_reuse_count++;
2540 3333 : return *slot;
2541 : }
2542 :
2543 416688 : per_program_point_data *per_point_data
2544 416688 : = get_or_create_per_program_point_data (point);
2545 :
2546 : /* Consider merging state with another enode at this program_point. */
2547 982883 : if (flag_analyzer_state_merge && point.state_merge_at_p ())
2548 : {
2549 : exploded_node *existing_enode;
2550 : unsigned i;
2551 388359 : FOR_EACH_VEC_ELT (per_point_data->m_enodes, i, existing_enode)
2552 : {
2553 235041 : if (logger)
2554 130 : logger->log ("considering merging with existing EN: %i for point",
2555 130 : existing_enode->m_index);
2556 235041 : gcc_assert (existing_enode->get_point () == point);
2557 235041 : const program_state &existing_state = existing_enode->get_state ();
2558 :
2559 : /* This merges successfully within the loop. */
2560 :
2561 235041 : program_state merged_state (m_ext_state);
2562 235041 : if (pruned_state.can_merge_with_p (existing_state, m_ext_state, point,
2563 : &merged_state))
2564 : {
2565 27702 : merged_state.validate (m_ext_state);
2566 27702 : if (logger)
2567 63 : logger->log ("merging new state with that of EN: %i",
2568 63 : existing_enode->m_index);
2569 :
2570 : /* Try again for a cache hit.
2571 : Whether we get one or not, merged_state's value_ids have no
2572 : relationship to those of the input state, and thus to those
2573 : of CHANGE, so we must purge any svalue_ids from *CHANGE. */
2574 27702 : ps.set_state (merged_state);
2575 :
2576 27702 : if (exploded_node **slot = m_point_and_state_to_node.get (&ps))
2577 : {
2578 : /* An exploded_node for PS already exists. */
2579 1839 : if (logger)
2580 1 : logger->log ("reused EN: %i", (*slot)->m_index);
2581 1839 : m_global_stats.m_node_reuse_after_merge_count++;
2582 1839 : per_fn_stats->m_node_reuse_after_merge_count++;
2583 1839 : per_cs_stats->m_node_reuse_after_merge_count++;
2584 1839 : return *slot;
2585 : }
2586 : }
2587 : else
2588 207339 : if (logger)
2589 67 : logger->log ("not merging new state with that of EN: %i",
2590 67 : existing_enode->m_index);
2591 235041 : }
2592 : }
2593 :
2594 : /* Impose a limit on the number of enodes per program point, and
2595 : simply stop if we exceed it. */
2596 414849 : if ((int)per_point_data->m_enodes.length ()
2597 414849 : >= param_analyzer_max_enodes_per_program_point)
2598 : {
2599 2199 : pretty_printer pp;
2600 2199 : point.print (&pp, format (false));
2601 2199 : print_enode_indices (&pp, per_point_data->m_enodes);
2602 2199 : if (logger)
2603 0 : logger->log ("not creating enode; too many at program point: %s",
2604 : pp_formatted_text (&pp));
2605 4394 : warning_at (point.get_location (), OPT_Wanalyzer_too_complex,
2606 : "terminating analysis for this program point: %s",
2607 : pp_formatted_text (&pp));
2608 2199 : per_point_data->m_excess_enodes++;
2609 2199 : return nullptr;
2610 2199 : }
2611 :
2612 412650 : ps.validate (m_ext_state);
2613 :
2614 : /* An exploded_node for "ps" doesn't already exist; create one. */
2615 821830 : exploded_node *node = new exploded_node (ps, m_nodes.length ());
2616 412650 : add_node (node);
2617 412650 : m_point_and_state_to_node.put (node->get_ps_key (), node);
2618 :
2619 : /* Update per-program_point data. */
2620 412650 : per_point_data->m_enodes.safe_push (node);
2621 :
2622 412650 : m_global_stats.m_num_nodes++;
2623 412650 : per_fn_stats->m_num_nodes++;
2624 412650 : per_cs_stats->m_num_nodes++;
2625 :
2626 412650 : if (logger)
2627 : {
2628 202 : format f (false);
2629 202 : pretty_printer *pp = logger->get_printer ();
2630 202 : logger->log ("created EN: %i", node->m_index);
2631 202 : logger->start_log_line ();
2632 202 : pp_string (pp, "point: ");
2633 202 : point.print (pp, f);
2634 202 : logger->end_log_line ();
2635 202 : logger->start_log_line ();
2636 202 : pp_string (pp, "state: ");
2637 202 : ps.get_state ().dump_to_pp (m_ext_state, true, false, pp);
2638 202 : logger->end_log_line ();
2639 : }
2640 :
2641 : /* Add the new node to the worlist. */
2642 412650 : if (add_to_worklist)
2643 405189 : m_worklist.add_node (node);
2644 : else
2645 7461 : node->set_status (exploded_node::status::special);
2646 : return node;
2647 840645 : }
2648 :
2649 : /* Add an exploded_edge from SRC to DEST, recording its association
2650 : with SEDGE (which may be NULL), and, if non-NULL, taking ownership
2651 : of CUSTOM_INFO. COULD_DO_WORK is used for detecting infinite loops.
2652 : Return the newly-created eedge. */
2653 :
2654 : exploded_edge *
2655 426907 : exploded_graph::add_edge (exploded_node *src, exploded_node *dest,
2656 : const superedge *sedge, bool could_do_work,
2657 : std::unique_ptr<custom_edge_info> custom_info)
2658 : {
2659 426907 : if (get_logger ())
2660 213 : get_logger ()->log ("creating edge EN: %i -> EN: %i",
2661 213 : src->m_index, dest->m_index);
2662 426907 : exploded_edge *e
2663 : = new exploded_edge (src, dest, sedge, could_do_work,
2664 426907 : std::move (custom_info));
2665 426907 : digraph<eg_traits>::add_edge (e);
2666 426907 : return e;
2667 : }
2668 :
2669 : /* Ensure that this graph has per-program_point-data for POINT;
2670 : borrow a pointer to it. */
2671 :
2672 : per_program_point_data *
2673 416688 : exploded_graph::
2674 : get_or_create_per_program_point_data (const program_point &point)
2675 : {
2676 416688 : if (per_program_point_data **slot = m_per_point_data.get (&point))
2677 165305 : return *slot;
2678 :
2679 251383 : per_program_point_data *per_point_data = new per_program_point_data (point);
2680 251383 : m_per_point_data.put (&per_point_data->m_key, per_point_data);
2681 251383 : return per_point_data;
2682 : }
2683 :
2684 : /* Get this graph's per-program-point-data for POINT if there is any,
2685 : otherwise nullptr. */
2686 :
2687 : per_program_point_data *
2688 0 : exploded_graph::get_per_program_point_data (const program_point &point) const
2689 : {
2690 0 : if (per_program_point_data **slot
2691 0 : = const_cast <point_map_t &> (m_per_point_data).get (&point))
2692 0 : return *slot;
2693 :
2694 : return nullptr;
2695 : }
2696 :
2697 : /* Ensure that this graph has per-call_string-data for CS;
2698 : borrow a pointer to it. */
2699 :
2700 : per_call_string_data *
2701 420021 : exploded_graph::get_or_create_per_call_string_data (const call_string &cs)
2702 : {
2703 420021 : if (per_call_string_data **slot = m_per_call_string_data.get (&cs))
2704 410077 : return *slot;
2705 :
2706 19884 : per_call_string_data *data = new per_call_string_data (cs, m_sg.m_nodes.length ());
2707 9944 : m_per_call_string_data.put (&data->m_key,
2708 : data);
2709 9944 : return data;
2710 : }
2711 :
2712 : /* Ensure that this graph has per-function-data for FUN;
2713 : borrow a pointer to it. */
2714 :
2715 : per_function_data *
2716 631 : exploded_graph::get_or_create_per_function_data (function *fun)
2717 : {
2718 631 : if (per_function_data **slot = m_per_function_data.get (fun))
2719 264 : return *slot;
2720 :
2721 367 : per_function_data *data = new per_function_data ();
2722 367 : m_per_function_data.put (fun, data);
2723 367 : return data;
2724 : }
2725 :
2726 : /* Get this graph's per-function-data for FUN if there is any,
2727 : otherwise nullptr. */
2728 :
2729 : per_function_data *
2730 789 : exploded_graph::get_per_function_data (function *fun) const
2731 : {
2732 1578 : if (per_function_data **slot
2733 789 : = const_cast <per_function_data_t &> (m_per_function_data).get (fun))
2734 751 : return *slot;
2735 :
2736 : return nullptr;
2737 : }
2738 :
2739 : /* Return true if FUN should be traversed directly, rather than only as
2740 : called via other functions. */
2741 :
2742 : static bool
2743 10792 : toplevel_function_p (const function &fun, logger *logger)
2744 : {
2745 : /* Don't directly traverse into functions that have an "__analyzer_"
2746 : prefix. Doing so is useful for the analyzer test suite, allowing
2747 : us to have functions that are called in traversals, but not directly
2748 : explored, thus testing how the analyzer handles calls and returns.
2749 : With this, we can have DejaGnu directives that cover just the case
2750 : of where a function is called by another function, without generating
2751 : excess messages from the case of the first function being traversed
2752 : directly. */
2753 : #define ANALYZER_PREFIX "__analyzer_"
2754 10792 : if (!strncmp (IDENTIFIER_POINTER (DECL_NAME (fun.decl)), ANALYZER_PREFIX,
2755 : strlen (ANALYZER_PREFIX)))
2756 : {
2757 150 : if (logger)
2758 0 : logger->log ("not traversing %qE (starts with %qs)",
2759 : fun.decl, ANALYZER_PREFIX);
2760 150 : return false;
2761 : }
2762 :
2763 10642 : if (logger)
2764 6 : logger->log ("traversing %qE (all checks passed)", fun.decl);
2765 :
2766 : return true;
2767 : }
2768 :
2769 : /* Custom event for use by tainted_call_info when a callback field has been
2770 : marked with __attribute__((tainted_args)), for labelling the field. */
2771 :
2772 : class tainted_args_field_custom_event : public custom_event
2773 : {
2774 : public:
2775 4 : tainted_args_field_custom_event (tree field)
2776 4 : : custom_event (event_loc_info (DECL_SOURCE_LOCATION (field), NULL_TREE, 0)),
2777 4 : m_field (field)
2778 : {
2779 4 : }
2780 :
2781 8 : void print_desc (pretty_printer &pp) const final override
2782 : {
2783 8 : pp_printf (&pp,
2784 : "field %qE of %qT"
2785 : " is marked with %<__attribute__((tainted_args))%>",
2786 8 : m_field, DECL_CONTEXT (m_field));
2787 8 : }
2788 :
2789 : private:
2790 : tree m_field;
2791 : };
2792 :
2793 : /* Custom event for use by tainted_call_info when a callback field has been
2794 : marked with __attribute__((tainted_args)), for labelling the function used
2795 : in that callback. */
2796 :
2797 : class tainted_args_callback_custom_event : public custom_event
2798 : {
2799 : public:
2800 4 : tainted_args_callback_custom_event (const event_loc_info &loc_info,
2801 : tree field)
2802 4 : : custom_event (loc_info),
2803 4 : m_field (field)
2804 : {
2805 : }
2806 :
2807 8 : void print_desc (pretty_printer &pp) const final override
2808 : {
2809 8 : pp_printf (&pp,
2810 : "function %qE used as initializer for field %qE"
2811 : " marked with %<__attribute__((tainted_args))%>",
2812 8 : get_fndecl (), m_field);
2813 8 : }
2814 :
2815 : private:
2816 : tree m_field;
2817 : };
2818 :
2819 : /* Custom edge info for use when adding a function used by a callback field
2820 : marked with '__attribute__((tainted_args))'. */
2821 :
2822 : class tainted_args_call_info : public custom_edge_info
2823 : {
2824 : public:
2825 10 : tainted_args_call_info (tree field, tree fndecl, location_t loc)
2826 10 : : m_field (field), m_fndecl (fndecl), m_loc (loc)
2827 : {}
2828 :
2829 0 : void print (pretty_printer *pp) const final override
2830 : {
2831 0 : pp_string (pp, "call to tainted field");
2832 0 : };
2833 :
2834 13 : bool update_model (region_model *model,
2835 : const exploded_edge *eedge,
2836 : region_model_context *) const final override
2837 : {
2838 26 : model->push_frame (*eedge->m_dest->get_function (),
2839 : nullptr, nullptr, nullptr);
2840 13 : return true;
2841 : }
2842 :
2843 4 : void add_events_to_path (checker_path *emission_path,
2844 : const exploded_edge &,
2845 : pending_diagnostic &,
2846 : const state_transition *) const final override
2847 : {
2848 : /* Show the field in the struct declaration, e.g.
2849 : "(1) field 'store' is marked with '__attribute__((tainted_args))'" */
2850 4 : emission_path->add_event
2851 4 : (std::make_unique<tainted_args_field_custom_event> (m_field));
2852 :
2853 : /* Show the callback in the initializer
2854 : e.g.
2855 : "(2) function 'gadget_dev_desc_UDC_store' used as initializer
2856 : for field 'store' marked with '__attribute__((tainted_args))'". */
2857 4 : emission_path->add_event
2858 4 : (std::make_unique<tainted_args_callback_custom_event>
2859 4 : (event_loc_info (m_loc, m_fndecl, 0),
2860 : m_field));
2861 4 : }
2862 :
2863 : private:
2864 : tree m_field;
2865 : tree m_fndecl;
2866 : location_t m_loc;
2867 : };
2868 :
2869 : /* Given an initializer at LOC for FIELD marked with
2870 : '__attribute__((tainted_args))' initialized with FNDECL, add an
2871 : entrypoint to FNDECL to EG (and to its worklist) where the params to
2872 : FNDECL are marked as tainted. */
2873 :
2874 : static void
2875 10 : add_tainted_args_callback (exploded_graph *eg, tree field, tree fndecl,
2876 : location_t loc)
2877 : {
2878 10 : logger *logger = eg->get_logger ();
2879 :
2880 10 : LOG_SCOPE (logger);
2881 :
2882 10 : if (!gimple_has_body_p (fndecl))
2883 : return;
2884 :
2885 10 : const extrinsic_state &ext_state = eg->get_ext_state ();
2886 :
2887 10 : function *fun = DECL_STRUCT_FUNCTION (fndecl);
2888 10 : gcc_assert (fun);
2889 :
2890 10 : program_point point
2891 10 : = program_point::from_function_entry (*ext_state.get_model_manager (),
2892 : eg->get_supergraph (), *fun);
2893 10 : program_state state (ext_state);
2894 10 : state.push_frame (ext_state, *fun);
2895 :
2896 10 : if (!mark_params_as_tainted (&state, fndecl, ext_state))
2897 : return;
2898 :
2899 10 : if (!state.m_valid)
2900 : return;
2901 :
2902 10 : exploded_node *enode = eg->get_or_create_node (point, state, nullptr);
2903 10 : if (logger)
2904 : {
2905 0 : if (enode)
2906 0 : logger->log ("created EN %i for tainted_args %qE entrypoint",
2907 0 : enode->m_index, fndecl);
2908 : else
2909 : {
2910 0 : logger->log ("did not create enode for tainted_args %qE entrypoint",
2911 : fndecl);
2912 0 : return;
2913 : }
2914 : }
2915 :
2916 10 : eg->add_edge (eg->get_origin (), enode, nullptr, false,
2917 10 : std::make_unique<tainted_args_call_info> (field, fndecl, loc));
2918 10 : }
2919 :
2920 : /* Callback for walk_tree for finding callbacks within initializers;
2921 : ensure that any callback initializer where the corresponding field is
2922 : marked with '__attribute__((tainted_args))' is treated as an entrypoint
2923 : to the analysis, special-casing that the inputs to the callback are
2924 : untrustworthy. */
2925 :
2926 : static tree
2927 27625 : add_any_callbacks (tree *tp, int *, void *data)
2928 : {
2929 27625 : exploded_graph *eg = (exploded_graph *)data;
2930 27625 : if (TREE_CODE (*tp) == CONSTRUCTOR)
2931 : {
2932 : /* Find fields with the "tainted_args" attribute.
2933 : walk_tree only walks the values, not the index values;
2934 : look at the index values. */
2935 : unsigned HOST_WIDE_INT idx;
2936 : constructor_elt *ce;
2937 :
2938 18484 : for (idx = 0; vec_safe_iterate (CONSTRUCTOR_ELTS (*tp), idx, &ce);
2939 : idx++)
2940 13732 : if (ce->index && TREE_CODE (ce->index) == FIELD_DECL)
2941 12774 : if (lookup_attribute ("tainted_args", DECL_ATTRIBUTES (ce->index)))
2942 : {
2943 10 : tree value = ce->value;
2944 10 : if (TREE_CODE (value) == ADDR_EXPR
2945 10 : && TREE_CODE (TREE_OPERAND (value, 0)) == FUNCTION_DECL)
2946 20 : add_tainted_args_callback (eg, ce->index,
2947 10 : TREE_OPERAND (value, 0),
2948 10 : EXPR_LOCATION (value));
2949 : }
2950 : }
2951 :
2952 27625 : return NULL_TREE;
2953 : }
2954 :
2955 : /* Add initial nodes to EG, with entrypoints for externally-callable
2956 : functions. */
2957 :
2958 : void
2959 3474 : exploded_graph::build_initial_worklist ()
2960 : {
2961 3474 : logger * const logger = get_logger ();
2962 3474 : LOG_SCOPE (logger);
2963 :
2964 3474 : cgraph_node *node;
2965 14266 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
2966 : {
2967 10792 : function *fun = node->get_fun ();
2968 10792 : gcc_assert (fun);
2969 10792 : if (!toplevel_function_p (*fun, logger))
2970 150 : continue;
2971 10642 : exploded_node *enode = add_function_entry (*fun);
2972 10642 : if (logger)
2973 : {
2974 6 : if (enode)
2975 6 : logger->log ("created EN %i for %qE entrypoint",
2976 6 : enode->m_index, fun->decl);
2977 : else
2978 0 : logger->log ("did not create enode for %qE entrypoint", fun->decl);
2979 : }
2980 : }
2981 :
2982 : /* Find callbacks that are reachable from global initializers. */
2983 3474 : varpool_node *vpnode;
2984 9538 : FOR_EACH_VARIABLE (vpnode)
2985 : {
2986 6064 : tree decl = vpnode->decl;
2987 6064 : tree init = DECL_INITIAL (decl);
2988 6064 : if (!init)
2989 1293 : continue;
2990 4771 : walk_tree (&init, add_any_callbacks, this, nullptr);
2991 : }
2992 3474 : }
2993 :
2994 : /* The main loop of the analysis.
2995 : Take freshly-created exploded_nodes from the worklist, calling
2996 : process_node on them to explore the <point, state> graph.
2997 : Add edges to their successors, potentially creating new successors
2998 : (which are also added to the worklist). */
2999 :
3000 : void
3001 3474 : exploded_graph::process_worklist ()
3002 : {
3003 3474 : logger * const logger = get_logger ();
3004 3474 : LOG_SCOPE (logger);
3005 3474 : auto_timevar tv (TV_ANALYZER_WORKLIST);
3006 :
3007 390691 : while (m_worklist.length () > 0)
3008 : {
3009 383820 : exploded_node *node = m_worklist.take_next ();
3010 383820 : gcc_assert (node->get_status () == exploded_node::status::worklist);
3011 :
3012 383820 : if (logger)
3013 194 : logger->log ("next to process: EN: %i", node->m_index);
3014 :
3015 : /* If we have a run of nodes at the same point, try merging and
3016 : processing them together, rather than pairwise or individually. */
3017 383820 : if (flag_analyzer_state_merge
3018 383820 : && node->get_point ().state_merge_at_p ())
3019 132373 : if (maybe_process_run_of_enodes (node))
3020 7439 : goto handle_limit;
3021 :
3022 : /* Avoid exponential explosions of nodes by attempting to merge
3023 : nodes that are at the same program point and which have
3024 : sufficiently similar state. */
3025 376381 : if (flag_analyzer_state_merge && node != m_origin)
3026 369303 : if (exploded_node *node_2 = m_worklist.peek_next ())
3027 : {
3028 318013 : gcc_assert (node_2->get_status ()
3029 : == exploded_node::status::worklist);
3030 318013 : gcc_assert (node != node_2);
3031 :
3032 318013 : if (logger)
3033 148 : logger->log ("peek worklist: EN: %i", node_2->m_index);
3034 :
3035 318013 : if (node->get_point () == node_2->get_point ())
3036 : {
3037 65386 : const program_point &point = node->get_point ();
3038 65386 : if (logger)
3039 : {
3040 29 : format f (false);
3041 29 : pretty_printer *pp = logger->get_printer ();
3042 29 : logger->start_log_line ();
3043 29 : logger->log_partial
3044 29 : ("got potential merge EN: %i and EN: %i at ",
3045 29 : node->m_index, node_2->m_index);
3046 29 : point.print (pp, f);
3047 29 : logger->end_log_line ();
3048 : }
3049 65386 : const program_state &state = node->get_state ();
3050 65386 : const program_state &state_2 = node_2->get_state ();
3051 :
3052 : /* They shouldn't be equal, or we wouldn't have two
3053 : separate nodes. */
3054 65386 : gcc_assert (state != state_2);
3055 :
3056 65386 : program_state merged_state (m_ext_state);
3057 65386 : if (state.can_merge_with_p (state_2, m_ext_state,
3058 : point, &merged_state))
3059 : {
3060 2690 : if (logger)
3061 4 : logger->log ("merging EN: %i and EN: %i",
3062 4 : node->m_index, node_2->m_index);
3063 :
3064 2690 : if (merged_state == state)
3065 : {
3066 : /* Then merge node_2 into node by adding an edge. */
3067 447 : add_edge (node_2, node, nullptr, false);
3068 :
3069 : /* Remove node_2 from the worklist. */
3070 447 : m_worklist.take_next ();
3071 447 : node_2->set_status (exploded_node::status::merger);
3072 :
3073 : /* Continue processing "node" below. */
3074 : }
3075 2243 : else if (merged_state == state_2)
3076 : {
3077 : /* Then merge node into node_2, and leave node_2
3078 : in the worklist, to be processed on the next
3079 : iteration. */
3080 1296 : add_edge (node, node_2, nullptr, false);
3081 1296 : node->set_status (exploded_node::status::merger);
3082 1296 : continue;
3083 : }
3084 : else
3085 : {
3086 : /* We have a merged state that differs from
3087 : both state and state_2. */
3088 :
3089 : /* Remove node_2 from the worklist. */
3090 947 : m_worklist.take_next ();
3091 :
3092 : /* Create (or get) an exploded node for the merged
3093 : states, adding to the worklist. */
3094 947 : exploded_node *merged_enode
3095 947 : = get_or_create_node (node->get_point (),
3096 : merged_state, node);
3097 947 : if (merged_enode == nullptr)
3098 85 : continue;
3099 :
3100 862 : if (logger)
3101 3 : logger->log ("merged EN: %i and EN: %i into EN: %i",
3102 3 : node->m_index, node_2->m_index,
3103 3 : merged_enode->m_index);
3104 :
3105 : /* "node" and "node_2" have both now been removed
3106 : from the worklist; we should not process them.
3107 :
3108 : "merged_enode" may be a new node; if so it will be
3109 : processed in a subsequent iteration.
3110 : Alternatively, "merged_enode" could be an existing
3111 : node; one way the latter can
3112 : happen is if we end up merging a succession of
3113 : similar nodes into one. */
3114 :
3115 : /* If merged_node is one of the two we were merging,
3116 : add it back to the worklist to ensure it gets
3117 : processed.
3118 :
3119 : Add edges from the merged nodes to it (but not a
3120 : self-edge). */
3121 862 : if (merged_enode == node)
3122 0 : m_worklist.add_node (merged_enode);
3123 : else
3124 : {
3125 862 : add_edge (node, merged_enode, nullptr, false);
3126 862 : node->set_status (exploded_node::status::merger);
3127 : }
3128 :
3129 862 : if (merged_enode == node_2)
3130 6 : m_worklist.add_node (merged_enode);
3131 : else
3132 : {
3133 856 : add_edge (node_2, merged_enode, nullptr, false);
3134 856 : node_2->set_status (exploded_node::status::merger);
3135 : }
3136 :
3137 862 : continue;
3138 862 : }
3139 : }
3140 :
3141 : /* TODO: should we attempt more than two nodes,
3142 : or just do pairs of nodes? (and hope that we get
3143 : a cascade of mergers). */
3144 65386 : }
3145 : }
3146 :
3147 374138 : process_node (node);
3148 :
3149 381577 : handle_limit:
3150 : /* Impose a hard limit on the number of exploded nodes, to ensure
3151 : that the analysis terminates in the face of pathological state
3152 : explosion (or bugs). */
3153 381577 : const int limit
3154 : = (// Per-supernode limit:
3155 381577 : (m_sg.num_nodes () * param_analyzer_supernode_explosion_factor)
3156 : // Allow one for the "origin" enode:
3157 381577 : + 1);
3158 381577 : if (m_global_stats.m_num_nodes > limit)
3159 : {
3160 77 : if (logger)
3161 0 : logger->log ("bailing out; too many nodes");
3162 231 : warning_at (node->get_point ().get_location (),
3163 77 : OPT_Wanalyzer_too_complex,
3164 : "analysis bailed out early"
3165 : " (%i enodes for %i snodes)",
3166 : m_nodes.length (),
3167 77 : m_sg.num_nodes ());
3168 77 : return;
3169 : }
3170 : }
3171 3474 : }
3172 :
3173 : /* Attempt to process a consecutive run of sufficiently-similar nodes in
3174 : the worklist at a point flagged with state_merge_at_p (having already
3175 : popped ENODE from the head of the worklist).
3176 :
3177 : If we have a consecutive run of enodes in the worklist all of which have
3178 : a single out-edge where all these out-edges are supports_bulk_merge_p and
3179 : all have the same successor snode and call string, then
3180 : process them all together, setting their status to status::bulk_merged,
3181 : and return true.
3182 : Otherwise, return false, in which case ENODE must be processed in the
3183 : normal way.
3184 :
3185 : When processing them all together, generate successor states based
3186 : on the edge op update_state_for_bulk_merger, and then attempt to merge
3187 : these states into a minimal set of merged successor states, partitioning
3188 : the inputs by merged successor state.
3189 :
3190 : Create new exploded nodes for all of the merged states, and add edges
3191 : connecting the input enodes to the corresponding merger exploded nodes.
3192 :
3193 : We hope we have a much smaller number of merged successor states
3194 : compared to the number of input enodes - ideally just one,
3195 : if all successor states can be merged.
3196 :
3197 : Processing and merging many together as one operation rather than as
3198 : pairs avoids scaling issues where per-pair mergers could bloat the
3199 : graph with merger nodes (especially so after switch statements). */
3200 :
3201 : bool
3202 132373 : exploded_graph::
3203 : maybe_process_run_of_enodes (exploded_node *enode)
3204 : {
3205 : /* A struct for tracking per-input state. */
3206 26382 : struct item
3207 : {
3208 26382 : item (exploded_node *input_enode)
3209 26382 : : m_input_enode (input_enode),
3210 26382 : m_processed_state (input_enode->get_state ()),
3211 26382 : m_merger_idx (-1)
3212 : {}
3213 :
3214 : exploded_node *m_input_enode;
3215 : program_state m_processed_state;
3216 : int m_merger_idx;
3217 : };
3218 :
3219 132373 : gcc_assert (enode->get_status () == exploded_node::status::worklist);
3220 :
3221 132373 : const program_point &src_point = enode->get_point ();
3222 132373 : const supernode *src_snode = src_point.get_supernode ();
3223 :
3224 132373 : logger * const logger = get_logger ();
3225 132373 : LOG_SCOPE (logger);
3226 :
3227 199465 : if (src_snode->m_succs.length () != 1)
3228 : return false;
3229 :
3230 105710 : auto sedge = src_snode->m_succs[0];
3231 :
3232 105710 : if (!sedge->supports_bulk_merge_p ())
3233 : return false;
3234 :
3235 38618 : const supernode *dst_snode = src_snode->m_succs[0]->m_dest;
3236 :
3237 : /* Find a run of enodes in the worklist that all have single out-sedges
3238 : go to the same supernode, all of which are bulk-mergeable (i.e. have
3239 : a simple single intraprocedural outcome). */
3240 38618 : auto_vec <exploded_node *> enodes;
3241 38618 : enodes.safe_push (enode);
3242 57561 : while (exploded_node *enode_2 = m_worklist.peek_next ())
3243 : {
3244 51542 : gcc_assert (enode_2->get_status ()
3245 : == exploded_node::status::worklist);
3246 51542 : gcc_assert (enode_2->m_succs.length () == 0);
3247 :
3248 51542 : const program_point &point_2 = enode_2->get_point ();
3249 51542 : const supernode *src_snode_2 = point_2.get_supernode ();
3250 :
3251 51542 : if (src_snode_2->m_succs.length () != 1)
3252 : break;
3253 50557 : auto sedge_2 = src_snode_2->m_succs[0];
3254 50557 : if (sedge_2->m_dest != dst_snode)
3255 : break;
3256 20365 : if (&point_2.get_call_string () != &src_point.get_call_string ())
3257 : break;
3258 18943 : if (!sedge_2->supports_bulk_merge_p ())
3259 : break;
3260 :
3261 18943 : enodes.safe_push (enode_2);
3262 18943 : m_worklist.take_next ();
3263 18943 : }
3264 :
3265 : /* If the only node is ENODE, then give up. */
3266 38618 : if (enodes.length () == 1)
3267 : return false;
3268 :
3269 7439 : if (logger)
3270 4 : logger->log ("got run of %i bulk-mergable enodes going to SN: %i",
3271 4 : enodes.length (), dst_snode->m_id);
3272 :
3273 : /* All of these enodes have a shared intraprocedural successor point
3274 : (even if they were for different in-edges). */
3275 7439 : program_point next_point (sedge->m_dest,
3276 7439 : src_point.get_call_string ());
3277 :
3278 : /* Calculate the successor state for each enode in enodes. */
3279 14878 : auto_delete_vec<item> items (enodes.length ());
3280 7439 : unsigned i;
3281 7439 : exploded_node *iter_enode;
3282 33821 : FOR_EACH_VEC_ELT (enodes, i, iter_enode)
3283 : {
3284 26382 : item *it = new item (iter_enode);
3285 26382 : items.quick_push (it);
3286 26382 : const program_state &state = iter_enode->get_state ();
3287 26382 : program_state *next_state = &it->m_processed_state;
3288 26382 : next_state->validate (m_ext_state);
3289 26382 : gcc_assert (iter_enode->get_supernode ()->m_succs.length () == 1);
3290 26382 : const superedge *iter_sedge = iter_enode->get_supernode ()->m_succs[0];
3291 26382 : if (auto op = iter_sedge->get_op ())
3292 414 : op->update_state_for_bulk_merger (state, *next_state);
3293 26382 : next_state->validate (m_ext_state);
3294 : }
3295 :
3296 : /* Attempt to partition the items into a set of merged states.
3297 : We hope we have a much smaller number of merged states
3298 : compared to the number of input enodes - ideally just one,
3299 : if all can be merged. */
3300 7439 : auto_delete_vec <program_state> merged_states;
3301 7439 : auto_vec<item *> first_item_for_each_merged_state;
3302 7439 : item *it;
3303 33821 : FOR_EACH_VEC_ELT (items, i, it)
3304 : {
3305 26382 : const program_state &it_state = it->m_processed_state;
3306 26382 : program_state *merged_state;
3307 26382 : unsigned iter_merger_idx;
3308 65176 : FOR_EACH_VEC_ELT (merged_states, iter_merger_idx, merged_state)
3309 : {
3310 48783 : merged_state->validate (m_ext_state);
3311 48783 : program_state merge (m_ext_state);
3312 48783 : if (it_state.can_merge_with_p (*merged_state, m_ext_state,
3313 : next_point, &merge))
3314 : {
3315 9989 : *merged_state = merge;
3316 9989 : merged_state->validate (m_ext_state);
3317 9989 : it->m_merger_idx = iter_merger_idx;
3318 9989 : if (logger)
3319 1 : logger->log ("reusing merger state %i for item %i (EN: %i)",
3320 1 : it->m_merger_idx, i, it->m_input_enode->m_index);
3321 9989 : goto got_merger;
3322 : }
3323 48783 : }
3324 : /* If it couldn't be merged with any existing merged_states,
3325 : create a new one. */
3326 16393 : if (it->m_merger_idx == -1)
3327 : {
3328 16393 : it->m_merger_idx = merged_states.length ();
3329 16393 : merged_states.safe_push (new program_state (it_state));
3330 16393 : first_item_for_each_merged_state.safe_push (it);
3331 16393 : if (logger)
3332 8 : logger->log ("using new merger state %i for item %i (EN: %i)",
3333 8 : it->m_merger_idx, i, it->m_input_enode->m_index);
3334 : }
3335 0 : got_merger:
3336 26382 : gcc_assert (it->m_merger_idx >= 0);
3337 26382 : gcc_assert ((unsigned)it->m_merger_idx < merged_states.length ());
3338 : }
3339 :
3340 : /* Create merger nodes. */
3341 22317 : auto_vec<exploded_node *> next_enodes (merged_states.length ());
3342 7439 : program_state *merged_state;
3343 23832 : FOR_EACH_VEC_ELT (merged_states, i, merged_state)
3344 : {
3345 16393 : exploded_node *src_enode
3346 16393 : = first_item_for_each_merged_state[i]->m_input_enode;
3347 16393 : exploded_node *next
3348 16393 : = get_or_create_node (next_point, *merged_state, src_enode);
3349 : /* "next" could be nullptr; we handle that when adding the edges below. */
3350 16393 : next_enodes.quick_push (next);
3351 16393 : if (logger)
3352 : {
3353 8 : if (next)
3354 8 : logger->log ("using EN: %i for merger state %i", next->m_index, i);
3355 : else
3356 0 : logger->log ("using NULL enode for merger state %i", i);
3357 : }
3358 : }
3359 :
3360 : /* Create edges from each input enode to the appropriate successor enode.
3361 : Update the status of the now-processed input enodes. */
3362 33821 : FOR_EACH_VEC_ELT (items, i, it)
3363 : {
3364 26382 : exploded_node *next = next_enodes[it->m_merger_idx];
3365 26382 : if (next)
3366 : {
3367 25809 : gcc_assert (it->m_input_enode->get_supernode ()->m_succs.length ()
3368 : == 1);
3369 25809 : const superedge *sedge
3370 25809 : = it->m_input_enode->get_supernode ()->m_succs[0];
3371 25809 : add_edge (it->m_input_enode, next, sedge,
3372 : false); /* no "work" is done during merger. */
3373 : }
3374 26382 : it->m_input_enode->set_status (exploded_node::status::bulk_merged);
3375 : }
3376 :
3377 7439 : if (logger)
3378 8 : logger->log ("merged %i in-enodes into %i out-enode(s) at SN: %i",
3379 4 : items.length (), merged_states.length (), dst_snode->m_id);
3380 :
3381 7439 : return true;
3382 139812 : }
3383 :
3384 : /* The core of exploded_graph::process_worklist (the main analysis loop),
3385 : handling one node in the worklist.
3386 :
3387 : Get successor <point, state> pairs for NODE, calling get_or_create on
3388 : them, and adding an exploded_edge to each successors.
3389 :
3390 : Freshly-created nodes will be added to the worklist. */
3391 :
3392 : void
3393 374138 : exploded_graph::process_node (exploded_node *node)
3394 : {
3395 374138 : logger * const logger = get_logger ();
3396 374138 : LOG_FUNC_1 (logger, "EN: %i", node->m_index);
3397 :
3398 374138 : node->set_status (exploded_node::status::processed);
3399 :
3400 374138 : const program_point &point = node->get_point ();
3401 :
3402 : /* Update cfun and input_location in case of an ICE: make it easier to
3403 : track down which source construct we're failing to handle. */
3404 744806 : auto_cfun sentinel (node->get_function ());
3405 :
3406 374138 : input_location = node->get_location ();
3407 :
3408 374138 : const program_state &state = node->get_state ();
3409 374138 : if (logger)
3410 : {
3411 187 : pretty_printer *pp = logger->get_printer ();
3412 187 : logger->start_log_line ();
3413 187 : pp_string (pp, "point: ");
3414 187 : point.print (pp, format (false));
3415 187 : pp_string (pp, ", state: ");
3416 187 : state.dump_to_pp (m_ext_state, true, false, pp);
3417 187 : logger->end_log_line ();
3418 : }
3419 :
3420 : /* Don't do anything for the origin enode; the initial population of the
3421 : worklist has already added successor enodes. */
3422 374138 : if (point.get_supernode () == nullptr)
3423 : return;
3424 :
3425 : /* Specialcase for EXIT BBs, which don't have out-edges. */
3426 370668 : if (point.get_supernode ()->exit_p ())
3427 : {
3428 19240 : gcc_assert (point.get_supernode ()->m_succs.length () == 0);
3429 :
3430 19240 : if (point.get_stack_depth () > 1)
3431 : {
3432 : /* Interprocedural return. */
3433 7344 : auto &src_call_string = point.get_call_string ();
3434 :
3435 7344 : const call_string::element_t &top_of_stack
3436 7344 : = src_call_string.get_top_of_stack ();
3437 7344 : const call_string *dst_call_string = src_call_string.get_parent ();
3438 7344 : const program_point dst_point
3439 : (top_of_stack.get_return_snode_in_caller (),
3440 7344 : *dst_call_string);
3441 7344 : auto edge_info
3442 7344 : = std::make_unique<interprocedural_return> (top_of_stack.get_call_stmt ());
3443 :
3444 7344 : const program_state &src_state (node->get_state ());
3445 7344 : program_state dst_state (src_state);
3446 7344 : uncertainty_t uncertainty;
3447 7344 : impl_region_model_context ctxt (*this, node,
3448 : &src_state, &dst_state, &uncertainty,
3449 : nullptr,
3450 7344 : nullptr);
3451 7344 : edge_info->update_state (&dst_state, nullptr, &ctxt);
3452 :
3453 7344 : program_state::detect_leaks (src_state, dst_state,
3454 : nullptr, get_ext_state (),
3455 : &ctxt);
3456 :
3457 14688 : if (exploded_node *next
3458 7344 : = get_or_create_node (dst_point, dst_state, node))
3459 7232 : add_edge (node, next, nullptr, false,
3460 7232 : std::move (edge_info));
3461 14688 : }
3462 : else
3463 : {
3464 : /* End of top-level of analysis for this function.
3465 : Detect leaks, and potentially create a function summary. */
3466 11896 : node->detect_leaks (*this);
3467 :
3468 11896 : if (flag_analyzer_call_summaries
3469 11896 : && point.get_call_string ().empty_p ())
3470 : {
3471 : /* TODO: create function summary
3472 : There can be more than one; each corresponds to a different
3473 : final enode in the function. */
3474 631 : if (logger)
3475 : {
3476 0 : pretty_printer *pp = logger->get_printer ();
3477 0 : logger->start_log_line ();
3478 0 : logger->log_partial
3479 0 : ("would create function summary for %qE; state: ",
3480 : point.get_fndecl ());
3481 0 : state.dump_to_pp (m_ext_state, true, false, pp);
3482 0 : logger->end_log_line ();
3483 : }
3484 631 : per_function_data *per_fn_data
3485 631 : = get_or_create_per_function_data (point.get_function ());
3486 631 : per_fn_data->add_call_summary (node);
3487 : }
3488 : }
3489 :
3490 19240 : return;
3491 : }
3492 :
3493 : /* Traverse into successors of the supernode. */
3494 : int i;
3495 : superedge *succ;
3496 1082538 : FOR_EACH_VEC_ELT (point.get_supernode ()->m_succs, i, succ)
3497 : {
3498 380685 : if (logger)
3499 : {
3500 194 : label_text succ_desc (succ->get_description (false));
3501 194 : logger->log ("considering SN: %i -> SN: %i (%s)",
3502 194 : succ->m_src->m_id, succ->m_dest->m_id,
3503 : succ_desc.get ());
3504 194 : }
3505 :
3506 380685 : program_point next_point (succ->m_dest, point.get_call_string ());
3507 380685 : program_state next_state (state);
3508 380685 : uncertainty_t uncertainty;
3509 :
3510 : /* Find the outcome(s) of any operation on the edge. */
3511 380685 : operation_context op_ctxt (*this, *node, *succ);
3512 :
3513 : /* Skip EH edges. */
3514 380685 : if (auto cfg_edge = succ->get_any_cfg_edge ())
3515 122976 : if (cfg_edge->flags & EDGE_EH)
3516 1028 : continue;
3517 :
3518 379657 : if (auto op = succ->get_op ())
3519 317567 : op->execute (op_ctxt);
3520 : else
3521 : {
3522 : /* No-op.
3523 : Unconditional goto to the dst point, which
3524 : must be in same function.
3525 : The supernode changes, but the callstring and
3526 : state do not change. */
3527 62090 : if (logger)
3528 34 : logger->log ("handling no-op edge");
3529 62090 : auto dst_point (op_ctxt.get_next_intraprocedural_point ());
3530 62090 : if (exploded_node *next
3531 62090 : = get_or_create_node (dst_point,
3532 : node->get_state (),
3533 : node))
3534 61920 : add_edge (node, next, succ, false);
3535 : }
3536 380685 : }
3537 374138 : }
3538 :
3539 : /* Ensure that this graph has a stats instance for FN, return it.
3540 : FN can be nullptr, in which case a stats instances is returned covering
3541 : "functionless" parts of the graph (the origin node). */
3542 :
3543 : stats *
3544 420021 : exploded_graph::get_or_create_function_stats (function *fn)
3545 : {
3546 420021 : if (!fn)
3547 3474 : return &m_functionless_stats;
3548 :
3549 416547 : if (stats **slot = m_per_function_stats.get (fn))
3550 405759 : return *slot;
3551 : else
3552 : {
3553 10788 : int num_supernodes = 0;
3554 1720785 : for (auto snode : m_sg.m_nodes)
3555 1688421 : if (snode->get_function () == fn)
3556 190851 : ++num_supernodes;
3557 10788 : stats *new_stats = new stats (num_supernodes);
3558 10788 : m_per_function_stats.put (fn, new_stats);
3559 10788 : return new_stats;
3560 : }
3561 : }
3562 :
3563 : /* Print bar charts to PP showing:
3564 : - the number of enodes per function, and
3565 : - for each function:
3566 : - the number of enodes per supernode/BB
3567 : - the number of excess enodes per supernode/BB beyond the
3568 : per-program-point limit, if there were any. */
3569 :
3570 : void
3571 5 : exploded_graph::print_bar_charts (pretty_printer *pp) const
3572 : {
3573 5 : cgraph_node *cgnode;
3574 :
3575 5 : pp_string (pp, "enodes per function:");
3576 5 : pp_newline (pp);
3577 5 : bar_chart enodes_per_function;
3578 11 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode)
3579 : {
3580 6 : function *fn = cgnode->get_fun ();
3581 6 : const stats * const *s_ptr
3582 6 : = const_cast <function_stat_map_t &> (m_per_function_stats).get (fn);
3583 12 : enodes_per_function.add_item (function_name (fn),
3584 6 : s_ptr ? (*s_ptr)->get_total_enodes () : 0);
3585 : }
3586 5 : enodes_per_function.print (pp);
3587 :
3588 : /* Accumulate number of enodes per supernode. */
3589 10 : auto_vec<unsigned> enodes_per_supernode (m_sg.m_nodes.length ());
3590 170 : for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
3591 80 : enodes_per_supernode.quick_push (0);
3592 : int i;
3593 : exploded_node *enode;
3594 207 : FOR_EACH_VEC_ELT (m_nodes, i, enode)
3595 : {
3596 202 : const supernode *iter_snode = enode->get_supernode ();
3597 202 : if (!iter_snode)
3598 5 : continue;
3599 197 : enodes_per_supernode[iter_snode->m_id]++;
3600 : }
3601 :
3602 : /* Accumulate excess enodes per supernode. */
3603 10 : auto_vec<unsigned> excess_enodes_per_supernode (m_sg.m_nodes.length ());
3604 85 : for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
3605 80 : excess_enodes_per_supernode.quick_push (0);
3606 83 : for (point_map_t::iterator iter = m_per_point_data.begin ();
3607 161 : iter != m_per_point_data.end (); ++iter)
3608 : {
3609 78 : const program_point *point = (*iter).first;
3610 78 : const supernode *iter_snode = point->get_supernode ();
3611 78 : if (!iter_snode)
3612 5 : continue;
3613 73 : const per_program_point_data *point_data = (*iter).second;
3614 73 : excess_enodes_per_supernode[iter_snode->m_id]
3615 73 : += point_data->m_excess_enodes;
3616 : }
3617 :
3618 : /* Show per-function bar_charts of enodes per supernode/BB. */
3619 5 : pp_string (pp, "per-function enodes per supernode/BB:");
3620 5 : pp_newline (pp);
3621 11 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (cgnode)
3622 : {
3623 6 : function *fn = cgnode->get_fun ();
3624 6 : pp_printf (pp, "function: %qs", function_name (fn));
3625 6 : pp_newline (pp);
3626 :
3627 6 : bar_chart enodes_per_snode;
3628 6 : bar_chart excess_enodes_per_snode;
3629 6 : bool have_excess_enodes = false;
3630 112 : for (size_t i = 0; i < m_sg.m_nodes.length (); i++)
3631 : {
3632 106 : const supernode *iter_snode = m_sg.m_nodes[i];
3633 106 : if (iter_snode->get_function () != fn)
3634 26 : continue;
3635 80 : pretty_printer tmp_pp;
3636 80 : pp_printf (&tmp_pp, "sn %i (bb %i)",
3637 80 : iter_snode->m_id, iter_snode->m_bb->index);
3638 80 : enodes_per_snode.add_item (pp_formatted_text (&tmp_pp),
3639 80 : enodes_per_supernode[iter_snode->m_id]);
3640 80 : const int num_excess
3641 80 : = excess_enodes_per_supernode[iter_snode->m_id];
3642 80 : excess_enodes_per_snode.add_item (pp_formatted_text (&tmp_pp),
3643 : num_excess);
3644 80 : if (num_excess)
3645 0 : have_excess_enodes = true;
3646 80 : }
3647 6 : enodes_per_snode.print (pp);
3648 6 : if (have_excess_enodes)
3649 : {
3650 0 : pp_printf (pp, "EXCESS ENODES:");
3651 0 : pp_newline (pp);
3652 0 : excess_enodes_per_snode.print (pp);
3653 : }
3654 6 : }
3655 5 : }
3656 :
3657 : /* Write all stats information to this graph's logger, if any. */
3658 :
3659 : void
3660 3474 : exploded_graph::log_stats () const
3661 : {
3662 3474 : logger * const logger = get_logger ();
3663 3474 : if (!logger)
3664 3469 : return;
3665 :
3666 5 : LOG_SCOPE (logger);
3667 :
3668 5 : m_ext_state.get_engine ()->log_stats (logger);
3669 :
3670 10 : logger->log ("m_sg.num_nodes (): %i", m_sg.num_nodes ());
3671 10 : logger->log ("m_nodes.length (): %i", m_nodes.length ());
3672 10 : logger->log ("m_edges.length (): %i", m_edges.length ());
3673 5 : logger->log ("remaining enodes in worklist: %i", m_worklist.length ());
3674 :
3675 5 : logger->log ("global stats:");
3676 5 : m_global_stats.log (logger);
3677 :
3678 5 : for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
3679 22 : iter != m_per_function_stats.end ();
3680 6 : ++iter)
3681 : {
3682 6 : function *fn = (*iter).first;
3683 6 : log_scope s (logger, function_name (fn));
3684 6 : (*iter).second->log (logger);
3685 6 : }
3686 :
3687 5 : print_bar_charts (logger->get_printer ());
3688 5 : }
3689 :
3690 : /* Dump all stats information to OUT. */
3691 :
3692 : void
3693 0 : exploded_graph::dump_stats (FILE *out) const
3694 : {
3695 0 : fprintf (out, "m_sg.num_nodes (): %i\n", m_sg.num_nodes ());
3696 0 : fprintf (out, "m_nodes.length (): %i\n", m_nodes.length ());
3697 0 : fprintf (out, "m_edges.length (): %i\n", m_edges.length ());
3698 0 : fprintf (out, "remaining enodes in worklist: %i", m_worklist.length ());
3699 :
3700 0 : fprintf (out, "global stats:\n");
3701 0 : m_global_stats.dump (out);
3702 :
3703 0 : for (function_stat_map_t::iterator iter = m_per_function_stats.begin ();
3704 0 : iter != m_per_function_stats.end ();
3705 0 : ++iter)
3706 : {
3707 0 : function *fn = (*iter).first;
3708 0 : fprintf (out, "function: %s\n", function_name (fn));
3709 0 : (*iter).second->dump (out);
3710 : }
3711 0 : }
3712 :
3713 : /* Return a new json::object of the form
3714 : {"nodes" : [objs for enodes],
3715 : "edges" : [objs for eedges],
3716 : "ext_state": object for extrinsic_state,
3717 : "diagnostic_manager": object for diagnostic_manager}. */
3718 :
3719 : std::unique_ptr<json::object>
3720 0 : exploded_graph::to_json () const
3721 : {
3722 0 : auto egraph_obj = std::make_unique<json::object> ();
3723 :
3724 : /* Nodes. */
3725 0 : {
3726 0 : auto nodes_arr = std::make_unique<json::array> ();
3727 0 : unsigned i;
3728 0 : exploded_node *n;
3729 0 : FOR_EACH_VEC_ELT (m_nodes, i, n)
3730 0 : nodes_arr->append (n->to_json (m_ext_state));
3731 0 : egraph_obj->set ("nodes", std::move (nodes_arr));
3732 0 : }
3733 :
3734 : /* Edges. */
3735 0 : {
3736 0 : auto edges_arr = std::make_unique<json::array> ();
3737 0 : unsigned i;
3738 0 : exploded_edge *n;
3739 0 : FOR_EACH_VEC_ELT (m_edges, i, n)
3740 0 : edges_arr->append (n->to_json ());
3741 0 : egraph_obj->set ("edges", std::move (edges_arr));
3742 0 : }
3743 :
3744 : /* m_sg is JSONified at the top-level. */
3745 :
3746 0 : egraph_obj->set ("ext_state", m_ext_state.to_json ());
3747 0 : egraph_obj->set ("worklist", m_worklist.to_json ());
3748 0 : egraph_obj->set ("diagnostic_manager", m_diagnostic_manager.to_json ());
3749 :
3750 : /* The following fields aren't yet being JSONified:
3751 : const state_purge_map *const m_purge_map;
3752 : const analysis_plan &m_plan;
3753 : stats m_global_stats;
3754 : function_stat_map_t m_per_function_stats;
3755 : stats m_functionless_stats;
3756 : call_string_data_map_t m_per_call_string_data; */
3757 :
3758 0 : return egraph_obj;
3759 : }
3760 :
3761 : /* class feasibility_problem. */
3762 :
3763 : void
3764 4 : feasibility_problem::dump_to_pp (pretty_printer *pp) const
3765 : {
3766 4 : pp_printf (pp, "edge from EN: %i to EN: %i",
3767 4 : m_eedge.m_src->m_index, m_eedge.m_dest->m_index);
3768 4 : if (m_rc)
3769 : {
3770 4 : pp_string (pp, "; rejected constraint: ");
3771 4 : m_rc->dump_to_pp (pp);
3772 4 : pp_string (pp, "; rmodel: ");
3773 4 : m_rc->get_model ().dump_to_pp (pp, true, false);
3774 : }
3775 4 : }
3776 :
3777 : /* class feasibility_state. */
3778 :
3779 : /* Ctor for feasibility_state, at the beginning of a path. */
3780 :
3781 6642 : feasibility_state::feasibility_state (region_model_manager *manager,
3782 6642 : const supergraph &sg)
3783 6642 : : m_model (manager),
3784 13284 : m_snodes_visited (sg.m_nodes.length ())
3785 : {
3786 6642 : bitmap_clear (m_snodes_visited);
3787 6642 : }
3788 :
3789 : /* Copy ctor for feasibility_state, for extending a path. */
3790 :
3791 475911 : feasibility_state::feasibility_state (const feasibility_state &other)
3792 475911 : : m_model (other.m_model),
3793 475911 : m_snodes_visited (const_sbitmap (other.m_snodes_visited)->n_bits)
3794 : {
3795 475911 : bitmap_copy (m_snodes_visited, other.m_snodes_visited);
3796 475911 : }
3797 :
3798 32615 : feasibility_state::feasibility_state (const region_model &model,
3799 32615 : const supergraph &sg)
3800 32615 : : m_model (model),
3801 65230 : m_snodes_visited (sg.m_nodes.length ())
3802 : {
3803 32615 : bitmap_clear (m_snodes_visited);
3804 32615 : }
3805 :
3806 : feasibility_state &
3807 58683 : feasibility_state::operator= (const feasibility_state &other)
3808 : {
3809 58683 : m_model = other.m_model;
3810 58683 : bitmap_copy (m_snodes_visited, other.m_snodes_visited);
3811 58683 : return *this;
3812 : }
3813 :
3814 : /* The heart of feasibility-checking.
3815 :
3816 : Attempt to update this state in-place based on traversing EEDGE
3817 : in a path.
3818 : Update the model for the stmts in the src enode.
3819 : Attempt to add constraints for EEDGE.
3820 :
3821 : If feasible, return true.
3822 : Otherwise, return false and write to *OUT_RC. */
3823 :
3824 : bool
3825 208036 : feasibility_state::
3826 : maybe_update_for_edge (logger *logger,
3827 : const exploded_edge *eedge,
3828 : region_model_context *ctxt,
3829 : std::unique_ptr<rejected_constraint> *out_rc)
3830 : {
3831 208036 : const exploded_node &src_enode = *eedge->m_src;
3832 208036 : const program_point &src_point = src_enode.get_point ();
3833 208036 : if (logger)
3834 : {
3835 82 : logger->start_log_line ();
3836 82 : src_point.print (logger->get_printer (), format (false));
3837 82 : logger->end_log_line ();
3838 : }
3839 :
3840 208036 : if (eedge->m_custom_info)
3841 9814 : eedge->m_custom_info->update_model (&m_model, eedge, ctxt);
3842 : else
3843 : {
3844 198222 : const superedge *sedge = eedge->m_sedge;
3845 198222 : if (sedge)
3846 : {
3847 190060 : if (logger)
3848 : {
3849 80 : label_text desc (sedge->get_description (false));
3850 80 : logger->log (" sedge: SN:%i -> SN:%i %s",
3851 80 : sedge->m_src->m_id,
3852 80 : sedge->m_dest->m_id,
3853 : desc.get ());
3854 80 : }
3855 :
3856 190060 : if (sedge->get_op ())
3857 150964 : if (!sedge->get_op ()->execute_for_feasibility (*eedge,
3858 : *this,
3859 : ctxt,
3860 : out_rc))
3861 : {
3862 6759 : if (logger)
3863 : {
3864 8 : logger->start_log_line ();
3865 8 : logger->log_partial ("rejecting due to region model: ");
3866 8 : m_model.dump_to_pp (logger->get_printer (), true, false);
3867 8 : logger->end_log_line ();
3868 : }
3869 6759 : return false;
3870 : }
3871 : }
3872 : else
3873 : {
3874 : /* Special-case the initial eedge from the origin node to the
3875 : initial function by pushing a frame for it. */
3876 8162 : if (eedge->m_src->m_index == 0)
3877 : {
3878 6468 : function *fun = eedge->m_dest->get_function ();
3879 6468 : gcc_assert (fun);
3880 6468 : m_model.push_frame (*fun, nullptr, nullptr, ctxt);
3881 6468 : if (logger)
3882 2 : logger->log (" pushing frame for %qD", fun->decl);
3883 : }
3884 : }
3885 : }
3886 :
3887 :
3888 201277 : {
3889 201277 : const exploded_node &dst_enode = *eedge->m_dest;
3890 201277 : const unsigned dst_snode_idx = dst_enode.get_supernode ()->m_id;
3891 201277 : bitmap_set_bit (m_snodes_visited, dst_snode_idx);
3892 : }
3893 :
3894 201277 : return true;
3895 : }
3896 :
3897 : /* Dump this object to PP. */
3898 :
3899 : void
3900 70 : feasibility_state::dump_to_pp (pretty_printer *pp,
3901 : bool simple, bool multiline) const
3902 : {
3903 70 : m_model.dump_to_pp (pp, simple, multiline);
3904 70 : }
3905 :
3906 : /* A family of cluster subclasses for use when generating .dot output for
3907 : exploded graphs (-fdump-analyzer-exploded-graph), for grouping the
3908 : enodes into hierarchical boxes.
3909 :
3910 : All functionless enodes appear in the top-level graph.
3911 : Every (function, call_string) pair gets its own cluster. Within that
3912 : cluster, each supernode gets its own cluster.
3913 :
3914 : Hence all enodes relating to a particular function with a particular
3915 : callstring will be in a cluster together; all enodes for the same
3916 : function but with a different callstring will be in a different
3917 : cluster. */
3918 :
3919 : /* Base class of cluster for clustering exploded_node instances in .dot
3920 : output, based on various subclass-specific criteria. */
3921 :
3922 1291 : class exploded_cluster : public cluster<eg_traits>
3923 : {
3924 : };
3925 :
3926 : /* Cluster containing all exploded_node instances for one supernode. */
3927 :
3928 : class supernode_cluster : public exploded_cluster
3929 : {
3930 : public:
3931 429 : supernode_cluster (const supernode *supernode) : m_supernode (supernode) {}
3932 :
3933 : // TODO: dtor?
3934 :
3935 429 : void dump_dot (graphviz_out *gv, const dump_args_t &args) const final override
3936 : {
3937 429 : gv->println ("subgraph \"cluster_supernode_%i\" {", m_supernode->m_id);
3938 429 : gv->indent ();
3939 429 : gv->println ("style=\"dashed\";");
3940 858 : gv->println ("label=\"SN: %i (bb: %i; scc: %i)\";",
3941 429 : m_supernode->m_id, m_supernode->m_bb->index,
3942 429 : args.m_eg.get_scc_id (*m_supernode));
3943 :
3944 429 : int i;
3945 429 : exploded_node *enode;
3946 1287 : FOR_EACH_VEC_ELT (m_enodes, i, enode)
3947 429 : enode->dump_dot (gv, args);
3948 :
3949 : /* Terminate subgraph. */
3950 429 : gv->outdent ();
3951 429 : gv->println ("}");
3952 429 : }
3953 :
3954 429 : void add_node (exploded_node *en) final override
3955 : {
3956 0 : m_enodes.safe_push (en);
3957 0 : }
3958 :
3959 : /* Comparator for use by auto_vec<supernode_cluster *>::qsort. */
3960 :
3961 0 : static int cmp_ptr_ptr (const void *p1, const void *p2)
3962 : {
3963 0 : const supernode_cluster *c1
3964 : = *(const supernode_cluster * const *)p1;
3965 0 : const supernode_cluster *c2
3966 : = *(const supernode_cluster * const *)p2;
3967 0 : return c1->m_supernode->m_id - c2->m_supernode->m_id;
3968 : }
3969 :
3970 : private:
3971 : const supernode *m_supernode;
3972 : auto_vec <exploded_node *> m_enodes;
3973 : };
3974 :
3975 : /* Cluster containing all supernode_cluster instances for one
3976 : (function, call_string) pair. */
3977 :
3978 : class function_call_string_cluster : public exploded_cluster
3979 : {
3980 : public:
3981 429 : function_call_string_cluster (function *fun, const call_string &cs)
3982 429 : : m_fun (fun), m_cs (cs) {}
3983 :
3984 858 : ~function_call_string_cluster ()
3985 429 : {
3986 429 : for (map_t::iterator iter = m_map.begin ();
3987 1716 : iter != m_map.end ();
3988 429 : ++iter)
3989 429 : delete (*iter).second;
3990 858 : }
3991 :
3992 429 : void dump_dot (graphviz_out *gv, const dump_args_t &args) const final override
3993 : {
3994 429 : const char *funcname = function_name (m_fun);
3995 :
3996 858 : gv->println ("subgraph \"cluster_function_%s\" {",
3997 429 : IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (m_fun->decl)));
3998 429 : gv->indent ();
3999 429 : gv->write_indent ();
4000 429 : gv->print ("label=\"call string: ");
4001 429 : m_cs.print (gv->get_pp ());
4002 429 : gv->print (" function: %s \";", funcname);
4003 429 : gv->print ("\n");
4004 :
4005 : /* Dump m_map, sorting it to avoid churn when comparing dumps. */
4006 429 : auto_vec<supernode_cluster *> child_clusters (m_map.elements ());
4007 429 : for (map_t::iterator iter = m_map.begin ();
4008 1716 : iter != m_map.end ();
4009 429 : ++iter)
4010 429 : child_clusters.quick_push ((*iter).second);
4011 :
4012 429 : child_clusters.qsort (supernode_cluster::cmp_ptr_ptr);
4013 :
4014 : unsigned i;
4015 : supernode_cluster *child_cluster;
4016 858 : FOR_EACH_VEC_ELT (child_clusters, i, child_cluster)
4017 429 : child_cluster->dump_dot (gv, args);
4018 :
4019 : /* Terminate subgraph. */
4020 429 : gv->outdent ();
4021 429 : gv->println ("}");
4022 429 : }
4023 :
4024 429 : void add_node (exploded_node *en) final override
4025 : {
4026 429 : const supernode *supernode = en->get_supernode ();
4027 429 : gcc_assert (supernode);
4028 429 : supernode_cluster **slot = m_map.get (supernode);
4029 429 : if (slot)
4030 0 : (*slot)->add_node (en);
4031 : else
4032 : {
4033 429 : supernode_cluster *child = new supernode_cluster (supernode);
4034 429 : m_map.put (supernode, child);
4035 429 : child->add_node (en);
4036 : }
4037 429 : }
4038 :
4039 : /* Comparator for use by auto_vec<function_call_string_cluster *>. */
4040 :
4041 17678 : static int cmp_ptr_ptr (const void *p1, const void *p2)
4042 : {
4043 17678 : const function_call_string_cluster *c1
4044 : = *(const function_call_string_cluster * const *)p1;
4045 17678 : const function_call_string_cluster *c2
4046 : = *(const function_call_string_cluster * const *)p2;
4047 35356 : if (int cmp_names
4048 17678 : = strcmp (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (c1->m_fun->decl)),
4049 17678 : IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (c2->m_fun->decl))))
4050 : return cmp_names;
4051 5037 : return call_string::cmp (c1->m_cs, c2->m_cs);
4052 : }
4053 :
4054 : private:
4055 : function *m_fun;
4056 : const call_string &m_cs;
4057 : typedef ordered_hash_map<const supernode *, supernode_cluster *> map_t;
4058 : map_t m_map;
4059 : };
4060 :
4061 : /* Keys for root_cluster. */
4062 :
4063 : struct function_call_string
4064 : {
4065 429 : function_call_string (function *fun, const call_string *cs)
4066 429 : : m_fun (fun), m_cs (cs)
4067 : {
4068 429 : gcc_assert (fun);
4069 429 : gcc_assert (cs);
4070 429 : }
4071 :
4072 : function *m_fun;
4073 : const call_string *m_cs;
4074 : };
4075 :
4076 : } // namespace ana
4077 :
4078 : template <> struct default_hash_traits<function_call_string>
4079 : : public pod_hash_traits<function_call_string>
4080 : {
4081 : static const bool empty_zero_p = false;
4082 : };
4083 :
4084 : template <>
4085 : inline hashval_t
4086 6043 : pod_hash_traits<function_call_string>::hash (value_type v)
4087 : {
4088 6043 : return (pointer_hash <function>::hash (v.m_fun)
4089 6043 : ^ pointer_hash <const call_string>::hash (v.m_cs));
4090 : }
4091 :
4092 : template <>
4093 : inline bool
4094 29984 : pod_hash_traits<function_call_string>::equal (const value_type &existing,
4095 : const value_type &candidate)
4096 : {
4097 29984 : return existing.m_fun == candidate.m_fun && &existing.m_cs == &candidate.m_cs;
4098 : }
4099 : template <>
4100 : inline void
4101 : pod_hash_traits<function_call_string>::mark_deleted (value_type &v)
4102 : {
4103 : v.m_fun = reinterpret_cast<function *> (1);
4104 : }
4105 : template <>
4106 : inline void
4107 1932 : pod_hash_traits<function_call_string>::mark_empty (value_type &v)
4108 : {
4109 1932 : v.m_fun = nullptr;
4110 : }
4111 : template <>
4112 : inline bool
4113 47620 : pod_hash_traits<function_call_string>::is_deleted (value_type v)
4114 : {
4115 47620 : return v.m_fun == reinterpret_cast<function *> (1);
4116 : }
4117 : template <>
4118 : inline bool
4119 104995 : pod_hash_traits<function_call_string>::is_empty (value_type v)
4120 : {
4121 104566 : return v.m_fun == nullptr;
4122 : }
4123 :
4124 : namespace ana {
4125 :
4126 : /* Top-level cluster for generating .dot output for exploded graphs,
4127 : handling the functionless nodes, and grouping the remaining nodes by
4128 : callstring. */
4129 :
4130 : class root_cluster : public exploded_cluster
4131 : {
4132 : public:
4133 4 : ~root_cluster ()
4134 4 : {
4135 433 : for (map_t::iterator iter = m_map.begin ();
4136 433 : iter != m_map.end ();
4137 429 : ++iter)
4138 429 : delete (*iter).second;
4139 4 : }
4140 :
4141 4 : void dump_dot (graphviz_out *gv, const dump_args_t &args) const final override
4142 : {
4143 4 : int i;
4144 4 : exploded_node *enode;
4145 8 : FOR_EACH_VEC_ELT (m_functionless_enodes, i, enode)
4146 4 : enode->dump_dot (gv, args);
4147 :
4148 : /* Dump m_map, sorting it to avoid churn when comparing dumps. */
4149 4 : auto_vec<function_call_string_cluster *> child_clusters (m_map.elements ());
4150 4 : for (map_t::iterator iter = m_map.begin ();
4151 433 : iter != m_map.end ();
4152 429 : ++iter)
4153 429 : child_clusters.quick_push ((*iter).second);
4154 :
4155 4 : child_clusters.qsort (function_call_string_cluster::cmp_ptr_ptr);
4156 :
4157 : function_call_string_cluster *child_cluster;
4158 437 : FOR_EACH_VEC_ELT (child_clusters, i, child_cluster)
4159 429 : child_cluster->dump_dot (gv, args);
4160 4 : }
4161 :
4162 433 : void add_node (exploded_node *en) final override
4163 : {
4164 433 : function *fun = en->get_function ();
4165 429 : if (!fun)
4166 : {
4167 4 : m_functionless_enodes.safe_push (en);
4168 4 : return;
4169 : }
4170 :
4171 429 : const call_string &cs = en->get_point ().get_call_string ();
4172 429 : function_call_string key (fun, &cs);
4173 429 : function_call_string_cluster **slot = m_map.get (key);
4174 429 : if (slot)
4175 0 : (*slot)->add_node (en);
4176 : else
4177 : {
4178 429 : function_call_string_cluster *child
4179 429 : = new function_call_string_cluster (fun, cs);
4180 429 : m_map.put (key, child);
4181 429 : child->add_node (en);
4182 : }
4183 : }
4184 :
4185 : private:
4186 : typedef hash_map<function_call_string, function_call_string_cluster *> map_t;
4187 : map_t m_map;
4188 :
4189 : /* This should just be the origin exploded_node. */
4190 : auto_vec <exploded_node *> m_functionless_enodes;
4191 : };
4192 :
4193 : /* Subclass of range_label for use within
4194 : exploded_graph::dump_exploded_nodes for implementing
4195 : -fdump-analyzer-exploded-nodes: a label for a specific
4196 : exploded_node. */
4197 :
4198 : class enode_label : public range_label
4199 : {
4200 : public:
4201 0 : enode_label (const extrinsic_state &ext_state,
4202 : exploded_node *enode)
4203 0 : : m_ext_state (ext_state), m_enode (enode) {}
4204 :
4205 0 : label_text get_text (unsigned) const final override
4206 : {
4207 0 : pretty_printer pp;
4208 0 : pp_format_decoder (&pp) = default_tree_printer;
4209 0 : m_enode->get_state ().dump_to_pp (m_ext_state, true, false, &pp);
4210 0 : return make_label_text (false, "EN: %i: %s",
4211 0 : m_enode->m_index, pp_formatted_text (&pp));
4212 0 : }
4213 :
4214 : private:
4215 : const extrinsic_state &m_ext_state;
4216 : exploded_node *m_enode;
4217 : };
4218 :
4219 : /* Postprocessing support for dumping the exploded nodes.
4220 : Handle -fdump-analyzer-exploded-nodes,
4221 : -fdump-analyzer-exploded-nodes-2, and the
4222 : "__analyzer_dump_exploded_nodes" builtin. */
4223 :
4224 : void
4225 3474 : exploded_graph::dump_exploded_nodes () const
4226 : {
4227 : // TODO
4228 : /* Locate calls to __analyzer_dump_exploded_nodes. */
4229 : // Print how many egs there are for them?
4230 : /* Better: log them as we go, and record the exploded nodes
4231 : in question. */
4232 :
4233 : /* Show every enode. */
4234 :
4235 : /* Gather them by stmt, so that we can more clearly see the
4236 : "hotspots" requiring numerous exploded nodes. */
4237 :
4238 : /* Alternatively, simply throw them all into one big rich_location
4239 : and see if the label-printing will sort it out...
4240 : This requires them all to be in the same source file. */
4241 :
4242 3474 : if (flag_dump_analyzer_exploded_nodes)
4243 : {
4244 0 : auto_timevar tv (TV_ANALYZER_DUMP);
4245 0 : gcc_rich_location richloc (UNKNOWN_LOCATION);
4246 0 : unsigned i;
4247 0 : exploded_node *enode;
4248 0 : FOR_EACH_VEC_ELT (m_nodes, i, enode)
4249 : {
4250 0 : location_t loc = enode->get_location ();
4251 0 : if (get_pure_location (richloc.get_loc ()) == UNKNOWN_LOCATION)
4252 0 : richloc.set_range (0, loc, SHOW_RANGE_WITH_CARET);
4253 : else
4254 0 : richloc.add_range (loc,
4255 : SHOW_RANGE_WITHOUT_CARET,
4256 0 : new enode_label (m_ext_state, enode));
4257 : }
4258 0 : warning_at (&richloc, 0, "%i exploded nodes", m_nodes.length ());
4259 :
4260 : /* Repeat the warning without all the labels, so that message is visible
4261 : (the other one may well have scrolled past the terminal limit). */
4262 0 : warning_at (richloc.get_loc (), 0,
4263 : "%i exploded nodes", m_nodes.length ());
4264 :
4265 0 : if (m_worklist.length () > 0)
4266 0 : warning_at (richloc.get_loc (), 0,
4267 : "worklist still contains %i nodes", m_worklist.length ());
4268 0 : }
4269 :
4270 : /* Dump the egraph in textual form to a dump file. */
4271 3474 : if (flag_dump_analyzer_exploded_nodes_2)
4272 : {
4273 0 : auto_timevar tv (TV_ANALYZER_DUMP);
4274 0 : char *filename
4275 0 : = concat (dump_base_name, ".eg.txt", nullptr);
4276 0 : FILE *outf = fopen (filename, "w");
4277 0 : if (!outf)
4278 0 : error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename);
4279 0 : free (filename);
4280 :
4281 0 : fprintf (outf, "exploded graph for %s\n", dump_base_name);
4282 0 : fprintf (outf, " nodes: %i\n", m_nodes.length ());
4283 0 : fprintf (outf, " edges: %i\n", m_edges.length ());
4284 :
4285 0 : unsigned i;
4286 0 : exploded_node *enode;
4287 0 : FOR_EACH_VEC_ELT (m_nodes, i, enode)
4288 : {
4289 0 : fprintf (outf, "\nEN %i:\n", enode->m_index);
4290 0 : enode->dump_succs_and_preds (outf);
4291 0 : pretty_printer pp;
4292 0 : enode->get_point ().print (&pp, format (true));
4293 0 : fprintf (outf, "%s\n", pp_formatted_text (&pp));
4294 0 : text_art::dump_to_file (enode->get_state (), outf);
4295 0 : }
4296 :
4297 0 : fclose (outf);
4298 0 : }
4299 :
4300 : /* Dump the egraph in textual form to multiple dump files, one per enode. */
4301 3474 : if (flag_dump_analyzer_exploded_nodes_3)
4302 : {
4303 0 : auto_timevar tv (TV_ANALYZER_DUMP);
4304 :
4305 0 : unsigned i;
4306 0 : exploded_node *enode;
4307 0 : FOR_EACH_VEC_ELT (m_nodes, i, enode)
4308 : {
4309 0 : char *filename
4310 0 : = xasprintf ("%s.en-%i.txt", dump_base_name, i);
4311 0 : FILE *outf = fopen (filename, "w");
4312 0 : if (!outf)
4313 0 : error_at (UNKNOWN_LOCATION, "unable to open %qs for writing",
4314 : filename);
4315 0 : free (filename);
4316 :
4317 0 : fprintf (outf, "EN %i:\n", enode->m_index);
4318 0 : enode->dump_succs_and_preds (outf);
4319 0 : pretty_printer pp;
4320 0 : enode->get_point ().print (&pp, format (true));
4321 0 : fprintf (outf, "%s\n", pp_formatted_text (&pp));
4322 0 : text_art::dump_to_file (enode->get_state (), outf);
4323 :
4324 0 : fclose (outf);
4325 0 : }
4326 0 : }
4327 :
4328 : /* Emit a warning at any call to "__analyzer_dump_exploded_nodes",
4329 : giving the number of processed exploded nodes at the snode before
4330 : the call, and the IDs of processed, merger, and worklist enodes.
4331 :
4332 : We highlight the count of *processed* enodes since this is of most
4333 : interest in DejaGnu tests for ensuring that state merger has
4334 : happened.
4335 :
4336 : We don't show the count of merger and worklist enodes, as this is
4337 : more of an implementation detail of the merging/worklist that we
4338 : don't want to bake into our expected DejaGnu messages. */
4339 :
4340 3474 : unsigned i;
4341 3474 : exploded_node *enode;
4342 3474 : hash_set<const gimple *> seen;
4343 419594 : FOR_EACH_VEC_ELT (m_nodes, i, enode)
4344 : {
4345 412650 : const supernode *snode = enode->get_supernode ();
4346 412650 : if (!snode)
4347 3470 : continue;
4348 409180 : if (snode->m_succs.length () != 1)
4349 46954 : continue;
4350 362226 : const superedge *sedge = snode->m_succs[0];
4351 362226 : if (!sedge->get_op ())
4352 88310 : continue;
4353 273916 : const call_and_return_op *op
4354 273916 : = sedge->get_op ()->dyn_cast_call_and_return_op ();
4355 273916 : if (!op)
4356 209594 : continue;
4357 64322 : const gcall &call = op->get_gcall ();
4358 64322 : if (is_special_named_call_p (call, "__analyzer_dump_exploded_nodes", 1))
4359 : {
4360 1129 : if (seen.contains (&call))
4361 444 : continue;
4362 :
4363 685 : auto_vec<exploded_node *> processed_enodes;
4364 685 : auto_vec<exploded_node *> merger_enodes;
4365 685 : auto_vec<exploded_node *> worklist_enodes;
4366 : /* This is O(N^2). */
4367 685 : unsigned j;
4368 685 : exploded_node *other_enode;
4369 121428 : FOR_EACH_VEC_ELT (m_nodes, j, other_enode)
4370 : {
4371 120743 : if (other_enode->get_supernode () == snode)
4372 1129 : switch (other_enode->get_status ())
4373 : {
4374 0 : default:
4375 0 : gcc_unreachable ();
4376 0 : case exploded_node::status::worklist:
4377 0 : worklist_enodes.safe_push (other_enode);
4378 0 : break;
4379 1060 : case exploded_node::status::processed:
4380 1060 : processed_enodes.safe_push (other_enode);
4381 1060 : break;
4382 69 : case exploded_node::status::merger:
4383 69 : merger_enodes.safe_push (other_enode);
4384 69 : break;
4385 : }
4386 : }
4387 :
4388 1370 : pretty_printer pp;
4389 685 : pp_character (&pp, '[');
4390 685 : print_enode_indices (&pp, processed_enodes);
4391 685 : if (merger_enodes.length () > 0)
4392 : {
4393 44 : pp_string (&pp, "] merger(s): [");
4394 44 : print_enode_indices (&pp, merger_enodes);
4395 : }
4396 685 : if (worklist_enodes.length () > 0)
4397 : {
4398 0 : pp_string (&pp, "] worklist: [");
4399 0 : print_enode_indices (&pp, worklist_enodes);
4400 : }
4401 685 : pp_character (&pp, ']');
4402 :
4403 1370 : warning_n (call.location, 0, processed_enodes.length (),
4404 : "%i processed enode: %s",
4405 : "%i processed enodes: %s",
4406 : processed_enodes.length (), pp_formatted_text (&pp));
4407 685 : seen.add (&call);
4408 :
4409 : /* If the argument is non-zero, then print all of the states
4410 : of the various enodes. */
4411 685 : tree t_arg = fold (gimple_call_arg (&call, 0));
4412 685 : if (TREE_CODE (t_arg) != INTEGER_CST)
4413 : {
4414 0 : error_at (snode->m_loc,
4415 : "integer constant required for arg 1");
4416 0 : return;
4417 : }
4418 685 : int i_arg = TREE_INT_CST_LOW (t_arg);
4419 685 : if (i_arg)
4420 : {
4421 : exploded_node *other_enode;
4422 685 : FOR_EACH_VEC_ELT (processed_enodes, j, other_enode)
4423 : {
4424 0 : fprintf (stderr, "%i of %i: EN %i:\n",
4425 : j + 1, processed_enodes.length (),
4426 0 : other_enode->m_index);
4427 0 : other_enode->dump_succs_and_preds (stderr);
4428 : /* Dump state. */
4429 0 : other_enode->get_state ().dump (m_ext_state, false);
4430 : }
4431 : }
4432 685 : }
4433 : }
4434 3474 : }
4435 :
4436 : DEBUG_FUNCTION exploded_node *
4437 0 : exploded_graph::get_node_by_index (int idx) const
4438 : {
4439 0 : exploded_node *enode = m_nodes[idx];
4440 0 : gcc_assert (enode->m_index == idx);
4441 0 : return enode;
4442 : }
4443 :
4444 : /* Ensure that there is an exploded_node for a top-level call to FNDECL. */
4445 :
4446 : void
4447 298 : exploded_graph::on_escaped_function (tree fndecl)
4448 : {
4449 298 : logger * const logger = get_logger ();
4450 298 : LOG_FUNC_1 (logger, "%qE", fndecl);
4451 :
4452 298 : cgraph_node *cgnode = cgraph_node::get (fndecl);
4453 298 : if (!cgnode)
4454 : return;
4455 :
4456 298 : function *fun = cgnode->get_fun ();
4457 298 : if (!fun)
4458 : return;
4459 :
4460 294 : if (!gimple_has_body_p (fndecl))
4461 : return;
4462 :
4463 294 : exploded_node *enode = add_function_entry (*fun);
4464 294 : if (logger)
4465 : {
4466 0 : if (enode)
4467 0 : logger->log ("created EN %i for %qE entrypoint",
4468 0 : enode->m_index, fun->decl);
4469 : else
4470 0 : logger->log ("did not create enode for %qE entrypoint", fun->decl);
4471 : }
4472 298 : }
4473 :
4474 : /* Subclass of dot_annotator for implementing
4475 : DUMP_BASE_NAME.supergraph.N.eg.dot, a post-analysis dump of the supergraph.
4476 :
4477 : Annotate the supergraph nodes by printing the exploded nodes in concise
4478 : form within them, colorizing the exploded nodes based on sm-state.
4479 : Also show saved diagnostics within the exploded nodes, giving information
4480 : on whether they were feasible, and, if infeasible, where the problem
4481 : was. */
4482 :
4483 4 : class exploded_graph_annotator : public dot_annotator
4484 : {
4485 : public:
4486 4 : exploded_graph_annotator (const exploded_graph &eg)
4487 4 : : m_eg (eg)
4488 : {
4489 : /* Avoid O(N^2) by prepopulating m_enodes_per_snode_id. */
4490 390 : for (size_t i = 0; i < eg.get_supergraph ().m_nodes.length (); ++i)
4491 191 : m_enodes_per_snode_id.push_back (std::vector<exploded_node *> ());
4492 : exploded_node *enode;
4493 : unsigned i;
4494 437 : FOR_EACH_VEC_ELT (m_eg.m_nodes, i, enode)
4495 433 : if (enode->get_supernode ())
4496 429 : m_enodes_per_snode_id[enode->get_supernode ()->m_id].push_back (enode);
4497 4 : }
4498 :
4499 : /* Show exploded nodes for N. */
4500 191 : void add_node_annotations (graphviz_out *gv, const supernode &n)
4501 : const final override
4502 : {
4503 191 : gv->begin_tr ();
4504 191 : pretty_printer *pp = gv->get_pp ();
4505 :
4506 191 : if (m_enodes_per_snode_id[n.m_id].empty ())
4507 4 : pp_string (pp, "<TD BGCOLOR=\"red\">UNREACHED</TD>");
4508 : else
4509 : {
4510 : /* Adding an empty TD here makes the actual enodes
4511 : be right-aligned and tightly packed, greatly
4512 : improving the readability of the graph. */
4513 187 : pp_string (pp, "<TD></TD>");
4514 616 : for (auto enode : m_enodes_per_snode_id[n.m_id])
4515 : {
4516 429 : gcc_assert (enode->get_supernode () == &n);
4517 429 : print_enode (gv, enode);
4518 : }
4519 : }
4520 :
4521 191 : pp_flush (pp);
4522 191 : gv->end_tr ();
4523 191 : }
4524 :
4525 : void
4526 4 : add_extra_objects (graphviz_out *gv) const final override
4527 : {
4528 4 : pretty_printer *pp = gv->get_pp ();
4529 :
4530 4 : pp_string (pp, "en_0 [shape=none,margin=0,style=filled,label=<<TABLE><TR>");
4531 4 : print_enode (gv, m_eg.m_nodes[0]);
4532 4 : pp_string (pp, "</TR></TABLE>>];\n\n");
4533 4 : pp_flush (pp);
4534 :
4535 4 : unsigned i;
4536 4 : exploded_edge *eedge;
4537 449 : FOR_EACH_VEC_ELT (m_eg.m_edges, i, eedge)
4538 : {
4539 445 : print_enode_port (pp, *eedge->m_src, "s");
4540 445 : pp_string (pp, " -> ");
4541 445 : print_enode_port (pp, *eedge->m_dest, "n");
4542 445 : dot::attr_list attrs;
4543 445 : attrs.add (dot::id ("style"), dot::id ("dotted"));
4544 445 : if (eedge->m_custom_info)
4545 : {
4546 22 : pretty_printer info_pp;
4547 22 : pp_format_decoder (&info_pp) = default_tree_printer;
4548 22 : eedge->m_custom_info->print (&info_pp);
4549 44 : attrs.add (dot::id ("label"),
4550 44 : dot::id (pp_formatted_text (&info_pp)));
4551 22 : }
4552 445 : dot::writer w (*pp);
4553 445 : attrs.print (w);
4554 445 : pp_newline (pp);
4555 445 : }
4556 4 : }
4557 :
4558 : private:
4559 : void
4560 890 : print_enode_port (pretty_printer *pp,
4561 : const exploded_node &enode,
4562 : const char *compass_pt) const
4563 : {
4564 890 : if (const supernode *snode = enode.get_supernode ())
4565 878 : pp_printf (pp, "node_%i:en_%i:%s",
4566 878 : snode->m_id, enode.m_index, compass_pt);
4567 : else
4568 12 : pp_printf (pp, "en_%i:%s",
4569 12 : enode.m_index, compass_pt);
4570 890 : }
4571 :
4572 : /* Concisely print a TD element for ENODE, showing the index, status,
4573 : and any saved_diagnostics at the enode. Colorize it to show sm-state.
4574 :
4575 : Ideally we'd dump ENODE's state here, hidden behind some kind of
4576 : interactive disclosure method like a tooltip, so that the states
4577 : can be explored without overwhelming the graph.
4578 : However, I wasn't able to get graphviz/xdot to show tooltips on
4579 : individual elements within a HTML-like label. */
4580 433 : void print_enode (graphviz_out *gv, const exploded_node *enode) const
4581 : {
4582 433 : pretty_printer *pp = gv->get_pp ();
4583 433 : pp_printf (pp, "<TD BGCOLOR=\"%s\">",
4584 : enode->get_dot_fillcolor ());
4585 433 : pp_printf (pp, "<TABLE BORDER=\"0\" PORT=\"en_%i\">", enode->m_index);
4586 433 : gv->begin_trtd ();
4587 433 : pp_printf (pp, "EN: %i", enode->m_index);
4588 433 : switch (enode->get_status ())
4589 : {
4590 0 : default:
4591 0 : gcc_unreachable ();
4592 0 : case exploded_node::status::worklist:
4593 0 : pp_string (pp, "(W)");
4594 0 : break;
4595 : case exploded_node::status::processed:
4596 : break;
4597 6 : case exploded_node::status::special:
4598 6 : pp_string (pp, "(S)");
4599 6 : break;
4600 12 : case exploded_node::status::merger:
4601 12 : pp_string (pp, "(M)");
4602 12 : break;
4603 0 : case exploded_node::status::bulk_merged:
4604 0 : pp_string (pp, "(BM)");
4605 0 : break;
4606 : }
4607 433 : gv->end_tdtr ();
4608 :
4609 : /* Dump any saved_diagnostics at this enode. */
4610 437 : for (unsigned i = 0; i < enode->get_num_diagnostics (); i++)
4611 : {
4612 4 : const saved_diagnostic *sd = enode->get_saved_diagnostic (i);
4613 4 : print_saved_diagnostic (gv, sd);
4614 : }
4615 433 : pp_printf (pp, "</TABLE>");
4616 433 : pp_printf (pp, "</TD>");
4617 433 : }
4618 :
4619 : /* Print a TABLE element for SD, showing the kind, the length of the
4620 : exploded_path, whether the path was feasible, and if infeasible,
4621 : what the problem was. */
4622 4 : void print_saved_diagnostic (graphviz_out *gv,
4623 : const saved_diagnostic *sd) const
4624 : {
4625 4 : pretty_printer *pp = gv->get_pp ();
4626 4 : gv->begin_trtd ();
4627 4 : pp_printf (pp, "<TABLE BORDER=\"0\">");
4628 4 : gv->begin_tr ();
4629 4 : pp_string (pp, "<TD BGCOLOR=\"green\">");
4630 4 : pp_printf (pp, "DIAGNOSTIC: %s", sd->m_d->get_kind ());
4631 4 : gv->end_tdtr ();
4632 4 : gv->begin_trtd ();
4633 4 : if (sd->get_best_epath ())
4634 4 : pp_printf (pp, "epath length: %i", sd->get_epath_length ());
4635 : else
4636 0 : pp_printf (pp, "no best epath");
4637 4 : gv->end_tdtr ();
4638 4 : if (const feasibility_problem *p = sd->get_feasibility_problem ())
4639 : {
4640 0 : gv->begin_trtd ();
4641 0 : pp_printf (pp, "INFEASIBLE at eedge %i: EN:%i -> EN:%i",
4642 0 : p->m_eedge_idx,
4643 0 : p->m_eedge.m_src->m_index,
4644 0 : p->m_eedge.m_dest->m_index);
4645 0 : pp_write_text_as_html_like_dot_to_stream (pp);
4646 0 : gv->end_tdtr ();
4647 0 : gv->begin_trtd ();
4648 0 : p->m_eedge.m_sedge->dump (pp);
4649 0 : pp_write_text_as_html_like_dot_to_stream (pp);
4650 0 : gv->end_tdtr ();
4651 : /* Ideally we'd print p->m_model here; see the notes above about
4652 : tooltips. */
4653 : }
4654 4 : pp_printf (pp, "</TABLE>");
4655 4 : gv->end_tdtr ();
4656 4 : }
4657 :
4658 : const exploded_graph &m_eg;
4659 : std::vector<std::vector <exploded_node *> > m_enodes_per_snode_id;
4660 : };
4661 :
4662 : /* Implement -fdump-analyzer-json. */
4663 :
4664 : static void
4665 0 : dump_analyzer_json (const supergraph &sg,
4666 : const exploded_graph &eg)
4667 : {
4668 0 : auto_timevar tv (TV_ANALYZER_DUMP);
4669 0 : char *filename = concat (dump_base_name, ".analyzer.json.gz", nullptr);
4670 0 : gzFile output = gzopen (filename, "w");
4671 0 : if (!output)
4672 : {
4673 0 : error_at (UNKNOWN_LOCATION, "unable to open %qs for writing", filename);
4674 0 : free (filename);
4675 0 : return;
4676 : }
4677 :
4678 0 : auto toplev_obj = std::make_unique<json::object> ();
4679 0 : toplev_obj->set ("sgraph", sg.to_json ());
4680 0 : toplev_obj->set ("egraph", eg.to_json ());
4681 :
4682 0 : pretty_printer pp;
4683 0 : toplev_obj->print (&pp, flag_diagnostics_json_formatting);
4684 0 : pp_formatted_text (&pp);
4685 :
4686 0 : if (gzputs (output, pp_formatted_text (&pp)) == EOF
4687 0 : || gzclose (output))
4688 0 : error_at (UNKNOWN_LOCATION, "error writing %qs", filename);
4689 :
4690 0 : free (filename);
4691 0 : }
4692 :
4693 : /* Concrete subclass of on_ana_init, allowing plugins to register
4694 : new state machines. */
4695 :
4696 : class impl_on_ana_init : public gcc::topics::analyzer_events::on_ana_init
4697 : {
4698 : public:
4699 38 : impl_on_ana_init (std::vector<std::unique_ptr<state_machine>> &checkers,
4700 : known_function_manager &known_fn_mgr,
4701 : logger *logger)
4702 38 : : m_checkers (checkers),
4703 38 : m_known_fn_mgr (known_fn_mgr),
4704 38 : m_logger (logger)
4705 : {}
4706 :
4707 : void
4708 1 : register_state_machine (std::unique_ptr<state_machine> sm)
4709 : const final override
4710 : {
4711 1 : LOG_SCOPE (m_logger);
4712 1 : m_checkers.push_back (std::move (sm));
4713 1 : }
4714 :
4715 : void
4716 107 : register_known_function (const char *name,
4717 : std::unique_ptr<known_function> kf)
4718 : const final override
4719 : {
4720 107 : LOG_SCOPE (m_logger);
4721 107 : m_known_fn_mgr.add (name, std::move (kf));
4722 107 : }
4723 :
4724 : logger *
4725 39 : get_logger () const final override
4726 : {
4727 39 : return m_logger;
4728 : }
4729 :
4730 : private:
4731 : std::vector<std::unique_ptr<state_machine>> &m_checkers;
4732 : known_function_manager &m_known_fn_mgr;
4733 : logger *m_logger;
4734 : };
4735 :
4736 : static void
4737 13900 : maybe_dump_supergraph (const supergraph &sg, const char *name,
4738 : const dot_annotator *annotator = nullptr,
4739 : const exploded_graph *eg = nullptr)
4740 : {
4741 13900 : static int dump_idx = 0;
4742 13900 : if (!flag_dump_analyzer_supergraph)
4743 13880 : return;
4744 :
4745 20 : auto_timevar tv (TV_ANALYZER_DUMP);
4746 20 : std::string filename (dump_base_name);
4747 20 : filename += ".supergraph.";
4748 40 : filename += std::to_string (dump_idx++);
4749 20 : filename += ".";
4750 20 : filename += name;
4751 20 : filename += ".dot";
4752 20 : supergraph::dump_args_t args
4753 : ((enum supergraph_dot_flags)SUPERGRAPH_DOT_SHOW_BBS,
4754 : annotator,
4755 20 : eg);
4756 20 : sg.dump_dot (filename.c_str (), args);
4757 20 : }
4758 :
4759 : /* Run the analysis "engine". */
4760 :
4761 : void
4762 3474 : impl_run_checkers (logger *logger)
4763 : {
4764 3474 : LOG_SCOPE (logger);
4765 :
4766 3474 : if (logger)
4767 : {
4768 5 : logger->log ("BITS_BIG_ENDIAN: %i", BITS_BIG_ENDIAN ? 1 : 0);
4769 5 : logger->log ("BYTES_BIG_ENDIAN: %i", BYTES_BIG_ENDIAN ? 1 : 0);
4770 5 : logger->log ("WORDS_BIG_ENDIAN: %i", WORDS_BIG_ENDIAN ? 1 : 0);
4771 5 : log_stashed_constants (logger);
4772 : }
4773 :
4774 : /* If using LTO, ensure that the cgraph nodes have function bodies. */
4775 3474 : cgraph_node *node;
4776 14266 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
4777 10792 : node->get_untransformed_body ();
4778 :
4779 3474 : region_model_manager mgr;
4780 :
4781 : /* Create the supergraph. */
4782 3474 : supergraph sg (mgr, logger);
4783 :
4784 3474 : maybe_dump_supergraph (sg, "original");
4785 :
4786 3474 : sg.fixup_locations (logger);
4787 :
4788 3474 : maybe_dump_supergraph (sg, "fixup-locations");
4789 :
4790 3474 : engine eng (mgr, &sg);
4791 :
4792 3474 : state_purge_map *purge_map = nullptr;
4793 3474 : if (flag_analyzer_state_purge)
4794 3463 : purge_map = new state_purge_map (sg, eng.get_model_manager (), logger);
4795 :
4796 3474 : if (flag_analyzer_simplify_supergraph)
4797 : {
4798 3474 : sg.simplify (logger);
4799 3474 : maybe_dump_supergraph (sg, "simplified");
4800 : }
4801 :
4802 3474 : sg.sort_nodes (logger);
4803 3474 : maybe_dump_supergraph (sg, "sorted");
4804 :
4805 3474 : if (flag_dump_analyzer_state_purge)
4806 : {
4807 4 : auto_timevar tv (TV_ANALYZER_DUMP);
4808 4 : state_purge_annotator a (purge_map);
4809 4 : char *filename = concat (dump_base_name, ".state-purge.dot", nullptr);
4810 4 : supergraph::dump_args_t args ((enum supergraph_dot_flags)0, &a, nullptr);
4811 4 : sg.dump_dot (filename, args);
4812 4 : free (filename);
4813 4 : }
4814 :
4815 3474 : auto checkers = make_checkers (logger);
4816 :
4817 3474 : register_known_functions (*eng.get_known_function_manager (),
4818 3474 : *eng.get_model_manager ());
4819 :
4820 3474 : if (auto channel
4821 3474 : = g->get_channels ().analyzer_events_channel.get_if_active ())
4822 38 : channel->publish (impl_on_ana_init (checkers,
4823 38 : *eng.get_known_function_manager (),
4824 38 : logger));
4825 :
4826 3474 : if (logger)
4827 : {
4828 5 : int i = 0;
4829 40 : for (auto &sm : checkers)
4830 35 : logger->log ("checkers[%i]: %s", ++i, sm->get_name ());
4831 : }
4832 :
4833 : /* Extrinsic state shared by nodes in the graph. */
4834 3474 : const extrinsic_state ext_state (std::move (checkers), &eng, logger);
4835 :
4836 3474 : const analysis_plan plan (sg, logger);
4837 :
4838 : /* The exploded graph. */
4839 3474 : exploded_graph eg (sg, logger, ext_state, purge_map, plan,
4840 3474 : analyzer_verbosity);
4841 :
4842 : /* Add entrypoints to the graph for externally-callable functions. */
4843 3474 : eg.build_initial_worklist ();
4844 :
4845 : /* Now process the worklist, exploring the <point, state> graph. */
4846 3474 : eg.process_worklist ();
4847 :
4848 3474 : if (warn_analyzer_infinite_loop)
4849 3474 : eg.detect_infinite_loops ();
4850 :
4851 3474 : if (flag_dump_analyzer_exploded_graph)
4852 : {
4853 4 : auto_timevar tv (TV_ANALYZER_DUMP);
4854 4 : char *filename
4855 4 : = concat (dump_base_name, ".eg.dot", nullptr);
4856 4 : exploded_graph::dump_args_t args (eg);
4857 4 : root_cluster c;
4858 4 : eg.dump_dot (filename, &c, args);
4859 4 : free (filename);
4860 4 : }
4861 :
4862 : /* Now emit any saved diagnostics. */
4863 3474 : eg.get_diagnostic_manager ().emit_saved_diagnostics (eg);
4864 :
4865 3474 : eg.dump_exploded_nodes ();
4866 :
4867 3474 : eg.log_stats ();
4868 :
4869 3474 : if (flag_dump_analyzer_supergraph)
4870 : {
4871 : /* Dump post-analysis form of supergraph. */
4872 4 : exploded_graph_annotator a (eg);
4873 4 : maybe_dump_supergraph (sg, "eg", &a, &eg);
4874 4 : }
4875 :
4876 3474 : if (flag_dump_analyzer_json)
4877 0 : dump_analyzer_json (sg, eg);
4878 :
4879 3474 : if (flag_dump_analyzer_untracked)
4880 23 : eng.get_model_manager ()->dump_untracked_regions ();
4881 :
4882 3474 : delete purge_map;
4883 :
4884 : /* Free up any dominance info that we may have created. */
4885 14266 : FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
4886 : {
4887 10792 : function *fun = node->get_fun ();
4888 10792 : free_dominance_info (fun, CDI_DOMINATORS);
4889 : }
4890 3474 : }
4891 :
4892 : /* Handle -fdump-analyzer and -fdump-analyzer-stderr. */
4893 : static FILE *dump_fout = nullptr;
4894 :
4895 : /* Track if we're responsible for closing dump_fout. */
4896 : static bool owns_dump_fout = false;
4897 :
4898 : /* If dumping is enabled, attempt to create dump_fout if it hasn't already
4899 : been opened. Return it. */
4900 :
4901 : FILE *
4902 6991 : get_or_create_any_logfile ()
4903 : {
4904 6991 : if (!dump_fout)
4905 : {
4906 6986 : if (flag_dump_analyzer_stderr)
4907 0 : dump_fout = stderr;
4908 6986 : else if (flag_dump_analyzer)
4909 : {
4910 5 : char *dump_filename = concat (dump_base_name, ".analyzer.txt", nullptr);
4911 5 : dump_fout = fopen (dump_filename, "w");
4912 5 : free (dump_filename);
4913 5 : if (dump_fout)
4914 5 : owns_dump_fout = true;
4915 : }
4916 : }
4917 6991 : return dump_fout;
4918 : }
4919 :
4920 : /* External entrypoint to the analysis "engine".
4921 : Set up any dumps, then call impl_run_checkers. */
4922 :
4923 : void
4924 3474 : run_checkers ()
4925 : {
4926 : /* Save input_location. */
4927 3474 : location_t saved_input_location = input_location;
4928 :
4929 3474 : {
4930 3474 : log_user the_logger (nullptr);
4931 3474 : get_or_create_any_logfile ();
4932 3474 : if (dump_fout)
4933 5 : the_logger.set_logger (new logger (dump_fout, 0, 0,
4934 5 : *global_dc->get_reference_printer ()));
4935 3474 : LOG_SCOPE (the_logger.get_logger ());
4936 :
4937 3474 : impl_run_checkers (the_logger.get_logger ());
4938 :
4939 : /* end of lifetime of the_logger (so that dump file is closed after the
4940 : various dtors run). */
4941 3474 : }
4942 :
4943 3474 : if (owns_dump_fout)
4944 : {
4945 5 : fclose (dump_fout);
4946 5 : owns_dump_fout = false;
4947 5 : dump_fout = nullptr;
4948 : }
4949 :
4950 : /* Restore input_location. Subsequent passes may assume that input_location
4951 : is some arbitrary value *not* in the block tree, which might be violated
4952 : if we didn't restore it. */
4953 3474 : input_location = saved_input_location;
4954 3474 : }
4955 :
4956 : } // namespace ana
4957 :
4958 : #endif /* #if ENABLE_ANALYZER */
|