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