Line data Source code
1 : /* Operations within the code being analyzed.
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 "gimple-pretty-print.h"
24 : #include "gimple-iterator.h"
25 : #include "tree-cfg.h"
26 : #include "tree-dfa.h"
27 : #include "fold-const.h"
28 : #include "cgraph.h"
29 : #include "text-art/dump.h"
30 : #include "text-art/tree-widget.h"
31 :
32 : #include "analyzer/ops.h"
33 : #include "analyzer/call-details.h"
34 : #include "analyzer/exploded-graph.h"
35 : #include "analyzer/checker-path.h"
36 : #include "analyzer/impl-sm-context.h"
37 : #include "analyzer/constraint-manager.h"
38 : #include "analyzer/call-summary.h"
39 : #include "analyzer/call-info.h"
40 : #include "analyzer/analysis-plan.h"
41 :
42 : #if ENABLE_ANALYZER
43 :
44 : namespace ana {
45 :
46 20475 : event_loc_info::event_loc_info (const exploded_node *enode)
47 : {
48 20475 : if (enode)
49 : {
50 20475 : m_loc = enode->get_location ();
51 20475 : m_fndecl = enode->get_point ().get_fndecl ();
52 40905 : m_depth = enode->get_stack_depth ();
53 : }
54 : else
55 : {
56 0 : m_loc = UNKNOWN_LOCATION;
57 0 : m_fndecl = NULL_TREE;
58 0 : m_depth = 0;
59 : }
60 20475 : }
61 :
62 425 : event_loc_info::event_loc_info (const program_point &point)
63 : {
64 425 : m_loc = point.get_location ();
65 425 : m_fndecl = point.get_fndecl ();
66 425 : m_depth = point.get_stack_depth ();
67 425 : }
68 :
69 : // struct operation_context
70 :
71 : void
72 0 : operation_context::dump () const
73 : {
74 0 : fprintf (stderr, "src enode: EN: %i\n", m_src_enode.m_index);
75 0 : m_src_enode.dump (m_eg.get_ext_state ());
76 :
77 0 : fprintf (stderr, "superedge\n");
78 0 : pretty_printer pp;
79 0 : pp.set_output_stream (stderr);
80 0 : m_sedge.dump (&pp);
81 0 : }
82 :
83 : logger *
84 454983 : operation_context::get_logger () const
85 : {
86 454983 : return m_eg.get_logger ();
87 : }
88 :
89 : const extrinsic_state &
90 297897 : operation_context::get_ext_state () const
91 : {
92 297897 : return m_eg.get_ext_state ();
93 : }
94 :
95 : const program_point &
96 24726 : operation_context::get_initial_point () const
97 : {
98 24726 : return m_src_enode.get_point ();
99 : }
100 :
101 : const program_state &
102 1207683 : operation_context::get_initial_state () const
103 : {
104 1207683 : return m_src_enode.get_state ();
105 : }
106 :
107 : const supergraph &
108 6796 : operation_context::get_supergraph () const
109 : {
110 6796 : return m_eg.get_supergraph ();
111 : }
112 :
113 : program_point
114 346848 : operation_context::get_next_intraprocedural_point () const
115 : {
116 : /* All edges are intraprocedural. */
117 346848 : gcc_assert (m_sedge.m_src->get_function ()
118 : == m_sedge.m_dest->get_function ());
119 346848 : return program_point (m_sedge.m_dest,
120 346848 : m_src_enode.get_point ().get_call_string ());
121 : }
122 :
123 : void
124 295997 : operation_context::add_outcome (const program_point &dst_point,
125 : program_state dst_state,
126 : bool could_do_work,
127 : uncertainty_t *uncertainty,
128 : std::unique_ptr<custom_edge_info> info)
129 : {
130 295997 : const program_state &src_state = get_initial_state ();
131 295997 : impl_region_model_context ctxt (m_eg, &m_src_enode,
132 : &src_state, &dst_state,
133 295997 : uncertainty, nullptr);
134 295997 : program_state::detect_leaks (src_state, dst_state, nullptr,
135 : get_ext_state (), &ctxt);
136 :
137 591994 : if (exploded_node *dst_enode
138 295997 : = m_eg.get_or_create_node (dst_point, dst_state, &m_src_enode))
139 : {
140 294946 : m_eg.add_edge (&m_src_enode, dst_enode, &m_sedge, could_do_work,
141 : std::move (info));
142 294946 : m_eg.detect_infinite_recursion (dst_enode);
143 : }
144 295997 : }
145 :
146 220578 : class op_region_model_context : public impl_region_model_context
147 : {
148 : public:
149 110289 : op_region_model_context (operation_context &op_ctxt,
150 : program_state &dst_state)
151 110289 : : impl_region_model_context (op_ctxt.m_eg,
152 110289 : &op_ctxt.m_src_enode,
153 110289 : &op_ctxt.get_initial_state (),
154 : &dst_state,
155 : nullptr,
156 110289 : &m_path_context)
157 : {
158 110289 : }
159 :
160 45025 : bool terminate_path_p () const
161 : {
162 45025 : return m_path_context.terminate_path_p ();
163 : }
164 :
165 : private:
166 59952 : class op_path_context : public path_context
167 : {
168 : public:
169 110289 : op_path_context ()
170 110289 : : m_terminate_path (false)
171 : {
172 : }
173 :
174 0 : void bifurcate (std::unique_ptr<custom_edge_info>) final override
175 : {
176 0 : gcc_unreachable ();
177 : }
178 :
179 42 : void terminate_path () final override
180 : {
181 42 : m_terminate_path = true;
182 42 : }
183 :
184 45025 : bool terminate_path_p () const final override
185 : {
186 45025 : return m_terminate_path;
187 : }
188 : private:
189 : bool m_terminate_path;
190 : } m_path_context;
191 : };
192 :
193 : // class gimple_stmt_op : public operation
194 :
195 : void
196 1105 : gimple_stmt_op::print_as_edge_label (pretty_printer *pp,
197 : bool /*user_facing*/) const
198 : {
199 1105 : pp_gimple_stmt_1 (pp, &m_stmt, 0, (dump_flags_t)0);
200 1105 : }
201 :
202 : bool
203 181718 : gimple_stmt_op::defines_ssa_name_p (const_tree ssa_name) const
204 : {
205 181718 : return &m_stmt == SSA_NAME_DEF_STMT (ssa_name);
206 : }
207 :
208 : bool
209 35266 : gimple_stmt_op::supports_bulk_merge_p () const
210 : {
211 35266 : return false;
212 : }
213 :
214 : /* Subclass of path_context for use within operation::execute implementations
215 : so that we can split states e.g. at "realloc" calls. */
216 :
217 : class impl_path_context : public path_context
218 : {
219 : public:
220 244256 : impl_path_context (const program_state *cur_state,
221 : logger *logger)
222 244256 : : m_cur_state (cur_state),
223 244256 : m_logger (logger),
224 244256 : m_terminate_path (false)
225 : {
226 : }
227 :
228 : bool bifurcation_p () const
229 : {
230 : return m_custom_eedge_infos.length () > 0;
231 : }
232 :
233 16482 : const program_state &get_state_at_bifurcation () const
234 : {
235 16482 : gcc_assert (m_state_at_bifurcation);
236 16482 : return *m_state_at_bifurcation;
237 : }
238 :
239 : void
240 8277 : bifurcate (std::unique_ptr<custom_edge_info> info) final override
241 : {
242 8277 : if (m_logger)
243 0 : m_logger->log ("bifurcating path");
244 :
245 8277 : if (m_state_at_bifurcation)
246 : /* Verify that the state at bifurcation is consistent when we
247 : split into multiple out-edges. */
248 1666 : gcc_assert (*m_state_at_bifurcation == *m_cur_state);
249 : else
250 : /* Take a copy of the cur_state at the moment when bifurcation
251 : happens. */
252 6611 : m_state_at_bifurcation
253 6611 : = std::unique_ptr<program_state> (new program_state (*m_cur_state));
254 :
255 : /* Take ownership of INFO. */
256 8277 : m_custom_eedge_infos.safe_push (info.release ());
257 8277 : }
258 :
259 1913 : void terminate_path () final override
260 : {
261 1913 : if (m_logger)
262 2 : m_logger->log ("terminating path");
263 1913 : m_terminate_path = true;
264 1913 : }
265 :
266 427941 : bool terminate_path_p () const final override
267 : {
268 427941 : return m_terminate_path;
269 : }
270 :
271 : const vec<custom_edge_info *> & get_custom_eedge_infos ()
272 : {
273 : return m_custom_eedge_infos;
274 : }
275 :
276 : private:
277 : const program_state *m_cur_state;
278 :
279 : logger *m_logger;
280 :
281 : /* Lazily-created copy of the state before the split. */
282 : std::unique_ptr<program_state> m_state_at_bifurcation;
283 :
284 : auto_vec <custom_edge_info *> m_custom_eedge_infos;
285 :
286 : bool m_terminate_path;
287 : };
288 :
289 : DEBUG_FUNCTION void
290 0 : operation::dump () const
291 : {
292 0 : tree_dump_pretty_printer pp (stderr);
293 0 : print_as_edge_label (&pp, false);
294 0 : pp_newline (&pp);
295 0 : }
296 :
297 : void
298 250746 : operation::handle_on_stmt_for_state_machines (operation_context &op_ctxt,
299 : program_state &dst_state,
300 : path_context *path_ctxt,
301 : bool &unknown_side_effects,
302 : const gimple &stmt)
303 : {
304 250746 : const program_state &old_state = op_ctxt.get_initial_state ();
305 250746 : int sm_idx;
306 250746 : sm_state_map *smap;
307 2005030 : FOR_EACH_VEC_ELT (old_state.m_checker_states, sm_idx, smap)
308 : {
309 1754284 : const state_machine &sm = op_ctxt.m_eg.get_ext_state ().get_sm (sm_idx);
310 1754284 : const sm_state_map *old_smap
311 1754284 : = old_state.m_checker_states[sm_idx];
312 1754284 : sm_state_map *new_smap = dst_state.m_checker_states[sm_idx];
313 1754284 : impl_sm_context sm_ctxt (op_ctxt.m_eg, sm_idx, sm,
314 1754284 : &op_ctxt.m_src_enode,
315 : &old_state,
316 : &dst_state,
317 : old_smap, new_smap, path_ctxt,
318 1754284 : unknown_side_effects);
319 :
320 : /* Allow the state_machine to handle the stmt. */
321 1754284 : if (sm.on_stmt (sm_ctxt, &stmt))
322 22122 : unknown_side_effects = false;
323 1754284 : }
324 250746 : }
325 :
326 : void
327 108865 : gimple_stmt_op::
328 : walk_load_store_addr_ops (void *data,
329 : walk_stmt_load_store_addr_fn load_cb,
330 : walk_stmt_load_store_addr_fn store_cb,
331 : walk_stmt_load_store_addr_fn addr_cb) const
332 : {
333 108865 : walk_stmt_load_store_addr_ops (const_cast<gimple *>(&m_stmt), data,
334 : load_cb, store_cb, addr_cb);
335 108865 : }
336 :
337 : void
338 156028 : gimple_stmt_op::execute (operation_context &op_ctxt) const
339 : {
340 156028 : auto logger = op_ctxt.get_logger ();
341 156028 : LOG_SCOPE (logger);
342 156028 : if (logger)
343 : {
344 94 : logger->start_log_line ();
345 94 : pp_gimple_stmt_1 (logger->get_printer (), &get_stmt (), 0,
346 : (dump_flags_t)0);
347 94 : logger->end_log_line ();
348 : }
349 156028 : execute_on_state (op_ctxt,
350 : /* Pass in a copy. */
351 : op_ctxt.get_initial_state ());
352 156028 : }
353 :
354 : void
355 205721 : gimple_stmt_op::execute_on_state (operation_context &op_ctxt,
356 : program_state dst_state) const
357 : {
358 205721 : auto logger = op_ctxt.get_logger ();
359 205721 : LOG_SCOPE (logger);
360 :
361 205721 : auto dst_point (op_ctxt.get_next_intraprocedural_point ());
362 205721 : const program_state &old_state = op_ctxt.get_initial_state ();
363 :
364 205721 : bool unknown_side_effects = false;
365 205721 : bool could_have_done_work = false;
366 :
367 205721 : impl_path_context path_ctxt (&dst_state, logger);
368 205721 : uncertainty_t uncertainty;
369 205721 : impl_region_model_context ctxt (op_ctxt.m_eg,
370 205721 : &op_ctxt.m_src_enode,
371 : &old_state,
372 : &dst_state,
373 : &uncertainty,
374 : &path_ctxt,
375 205721 : &m_stmt,
376 205721 : &could_have_done_work);
377 :
378 205721 : dst_state.m_region_model->on_stmt_pre (&get_stmt (),
379 : &unknown_side_effects,
380 : &ctxt);
381 :
382 205721 : handle_on_stmt_for_state_machines (op_ctxt,
383 : dst_state,
384 : &path_ctxt,
385 : unknown_side_effects,
386 : m_stmt);
387 :
388 205721 : if (path_ctxt.terminate_path_p ())
389 678 : return;
390 :
391 205043 : if (const gcall *call = dyn_cast <const gcall *> (&m_stmt))
392 49448 : dst_state.m_region_model->on_call_post (*call, unknown_side_effects, &ctxt);
393 :
394 205043 : if (!path_ctxt.terminate_path_p ())
395 203875 : op_ctxt.add_outcome (dst_point, dst_state, could_have_done_work,
396 : &uncertainty);
397 :
398 : /* If we have custom edge infos, "bifurcate" the state
399 : accordingly, potentially creating a new state/enode/eedge
400 : instances. For example, to handle a "realloc" call, we
401 : might split into 3 states, for the "failure",
402 : "resizing in place", and "moving to a new buffer" cases. */
403 226434 : for (auto edge_info_iter : path_ctxt.get_custom_eedge_infos ())
404 : {
405 : /* Take ownership of the edge infos from the path_ctxt. */
406 8241 : std::unique_ptr<custom_edge_info> edge_info (edge_info_iter);
407 8241 : if (logger)
408 : {
409 0 : logger->start_log_line ();
410 0 : logger->log_partial ("bifurcating for edge: ");
411 0 : edge_info->print (logger->get_printer ());
412 0 : logger->end_log_line ();
413 : }
414 8241 : program_state bifurcated_new_state
415 8241 : (path_ctxt.get_state_at_bifurcation ());
416 :
417 : /* Apply edge_info to state. */
418 8241 : impl_region_model_context
419 : bifurcation_ctxt (op_ctxt.m_eg,
420 8241 : &op_ctxt.m_src_enode,
421 8241 : &path_ctxt.get_state_at_bifurcation (),
422 : &bifurcated_new_state,
423 : nullptr, // uncertainty_t *uncertainty
424 : nullptr, // path_context *path_ctxt
425 8241 : &m_stmt);
426 8241 : if (edge_info->update_state (&bifurcated_new_state,
427 : nullptr, /* no exploded_edge yet. */
428 : &bifurcation_ctxt))
429 : {
430 7893 : if (exploded_node *next2
431 7893 : = edge_info->create_enode
432 7893 : (op_ctxt.m_eg,
433 : dst_point,
434 : std::move (bifurcated_new_state),
435 7893 : &op_ctxt.m_src_enode,
436 : &bifurcation_ctxt))
437 : {
438 7455 : op_ctxt.m_eg.add_edge (&op_ctxt.m_src_enode, next2, nullptr,
439 : true /* assume that work could be done */,
440 : std::move (edge_info));
441 : }
442 : }
443 8241 : }
444 411442 : }
445 :
446 : bool
447 106180 : gimple_stmt_op::
448 : execute_for_feasibility (const exploded_edge &,
449 : feasibility_state &fstate,
450 : region_model_context *ctxt,
451 : std::unique_ptr<rejected_constraint> */*out_rc*/) const
452 : {
453 106180 : region_model &model = fstate.get_model ();
454 106180 : bool unknown_side_effects;
455 106180 : model.on_stmt_pre (&m_stmt, &unknown_side_effects, ctxt);
456 :
457 106180 : if (const gcall *call = dyn_cast <const gcall *> (&m_stmt))
458 21053 : model.on_call_post (*call, unknown_side_effects, ctxt);
459 :
460 106180 : return true;
461 : }
462 :
463 : /* An sm_context for adding state_change_event on assignments to NULL,
464 : where the default state isn't m_start. Storing such state in the
465 : sm_state_map would lead to bloat of the exploded_graph, so we want
466 : to leave it as a default state, and inject state change events here
467 : when we have a diagnostic.
468 : Find transitions of constants, for handling on_zero_assignment. */
469 :
470 94117 : struct null_assignment_sm_context : public sm_context
471 : {
472 94117 : null_assignment_sm_context (int sm_idx,
473 : const state_machine &sm,
474 : const program_state *old_state,
475 : const program_state *new_state,
476 : const gimple *stmt,
477 : const program_point *point,
478 : checker_path *emission_path,
479 : const extrinsic_state &ext_state)
480 94117 : : sm_context (sm_idx, sm), m_old_state (old_state), m_new_state (new_state),
481 94117 : m_stmt (stmt), m_point (point), m_emission_path (emission_path),
482 94117 : m_ext_state (ext_state)
483 : {
484 : }
485 :
486 0 : tree get_fndecl_for_call (const gcall &/*call*/) final override
487 : {
488 0 : return NULL_TREE;
489 : }
490 :
491 4353 : state_machine::state_t get_state (tree var) final override
492 : {
493 4353 : const svalue *var_old_sval
494 4353 : = m_old_state->m_region_model->get_rvalue (var, nullptr);
495 4353 : const sm_state_map *old_smap = m_old_state->m_checker_states[m_sm_idx];
496 :
497 4353 : state_machine::state_t current
498 4353 : = old_smap->get_state (var_old_sval, m_ext_state);
499 :
500 4353 : return current;
501 : }
502 :
503 5 : state_machine::state_t get_state (const svalue *sval) final override
504 : {
505 5 : const sm_state_map *old_smap = m_old_state->m_checker_states[m_sm_idx];
506 5 : state_machine::state_t current = old_smap->get_state (sval, m_ext_state);
507 5 : return current;
508 : }
509 :
510 1325 : void set_next_state (tree var,
511 : state_machine::state_t to,
512 : tree origin ATTRIBUTE_UNUSED) final override
513 : {
514 1325 : state_machine::state_t from = get_state (var);
515 1325 : if (from != m_sm.get_start_state ())
516 906 : return;
517 1210 : if (!is_transition_to_null (to))
518 : return;
519 :
520 419 : const svalue *var_new_sval
521 419 : = m_new_state->m_region_model->get_rvalue (var, nullptr);
522 :
523 419 : m_emission_path->add_event
524 419 : (std::make_unique<state_change_event> (event_loc_info (*m_point),
525 419 : m_stmt,
526 : m_sm,
527 : var_new_sval,
528 : from, to,
529 838 : nullptr,
530 419 : *m_new_state,
531 838 : nullptr));
532 : }
533 :
534 0 : void set_next_state (const svalue *sval,
535 : state_machine::state_t to,
536 : tree origin ATTRIBUTE_UNUSED) final override
537 : {
538 0 : state_machine::state_t from = get_state (sval);
539 0 : if (from != m_sm.get_start_state ())
540 0 : return;
541 0 : if (!is_transition_to_null (to))
542 : return;
543 :
544 0 : m_emission_path->add_event
545 0 : (std::make_unique<state_change_event> (event_loc_info (*m_point),
546 0 : m_stmt,
547 : m_sm,
548 : sval,
549 : from, to,
550 0 : nullptr,
551 0 : *m_new_state,
552 0 : nullptr));
553 : }
554 :
555 115 : void warn (tree, std::unique_ptr<pending_diagnostic>) final override
556 : {
557 115 : }
558 0 : void warn (const svalue *, std::unique_ptr<pending_diagnostic>) final override
559 : {
560 0 : }
561 :
562 115 : tree get_diagnostic_tree (tree expr) final override
563 : {
564 115 : return expr;
565 : }
566 :
567 0 : tree get_diagnostic_tree (const svalue *sval) final override
568 : {
569 0 : return m_new_state->m_region_model->get_representative_tree (sval);
570 : }
571 :
572 13436 : state_machine::state_t get_global_state () const final override
573 : {
574 13436 : return 0;
575 : }
576 :
577 0 : void set_global_state (state_machine::state_t) final override
578 : {
579 : /* No-op. */
580 0 : }
581 :
582 0 : void clear_all_per_svalue_state () final override
583 : {
584 : /* No-op. */
585 0 : }
586 :
587 0 : void on_custom_transition (custom_transition *) final override
588 : {
589 0 : }
590 :
591 13483 : tree is_zero_assignment (const gimple *stmt) final override
592 : {
593 25694 : const gassign *assign_stmt = dyn_cast <const gassign *> (stmt);
594 13483 : if (!assign_stmt)
595 : return NULL_TREE;
596 26966 : if (const svalue *sval
597 13483 : = m_new_state->m_region_model->get_gassign_result (assign_stmt, nullptr))
598 13073 : if (tree cst = sval->maybe_get_constant ())
599 2401 : if (::zerop(cst))
600 1272 : return gimple_assign_lhs (assign_stmt);
601 : return NULL_TREE;
602 : }
603 :
604 1274 : const program_state *get_old_program_state () const final override
605 : {
606 1274 : return m_old_state;
607 : }
608 0 : const program_state *get_new_program_state () const final override
609 : {
610 0 : return m_new_state;
611 : }
612 :
613 0 : location_t get_emission_location () const final override
614 : {
615 0 : return UNKNOWN_LOCATION;
616 : }
617 :
618 : /* We only care about transitions to the "null" state
619 : within sm-malloc. Special-case this. */
620 1210 : static bool is_transition_to_null (state_machine::state_t s)
621 : {
622 1210 : return !strcmp (s->get_name (), "null");
623 : }
624 :
625 : const program_state *m_old_state;
626 : const program_state *m_new_state;
627 : const gimple *m_stmt;
628 : const program_point *m_point;
629 : checker_path *m_emission_path;
630 : const extrinsic_state &m_ext_state;
631 : };
632 :
633 : void
634 13623 : gimple_stmt_op::add_any_events_for_eedge (const exploded_edge &eedge,
635 : checker_path &out_path) const
636 : {
637 13623 : out_path.add_event
638 13623 : (std::make_unique<statement_event> (&get_stmt (),
639 13623 : eedge.m_dest->get_function ()->decl,
640 27246 : eedge.m_dest->get_stack_depth (),
641 13623 : eedge.m_dest->get_state ()));
642 :
643 : /* Create state change events for assignment to NULL.
644 : Iterate through the stmts in dst_enode, adding state change
645 : events for them. */
646 13623 : if (const gassign *assign = dyn_cast<const gassign *> (&m_stmt))
647 : {
648 13501 : const program_point &src_point = eedge.m_src->get_point ();
649 13501 : const extrinsic_state &ext_state = out_path.get_ext_state ();
650 107618 : for (unsigned i = 0; i < ext_state.get_num_checkers (); i++)
651 : {
652 94117 : const state_machine &sm = ext_state.get_sm (i);
653 94117 : null_assignment_sm_context sm_ctxt (i, sm,
654 94117 : &eedge.m_src->get_state (),
655 94117 : &eedge.m_dest->get_state (),
656 : assign,
657 : &src_point,
658 : &out_path,
659 94117 : ext_state);
660 94117 : sm.on_stmt (sm_ctxt, assign);
661 : // TODO: what about phi nodes?
662 94117 : }
663 : }
664 13623 : }
665 :
666 : // class gassign_op : public gimple_stmt_op
667 :
668 : // class greturn_op : public gimple_stmt_op
669 :
670 : void
671 17177 : greturn_op::execute (operation_context &op_ctxt) const
672 : {
673 17177 : auto logger = op_ctxt.get_logger ();
674 :
675 17177 : auto dst_point (op_ctxt.get_next_intraprocedural_point ());
676 17177 : const program_state &old_state = op_ctxt.get_initial_state ();
677 17177 : program_state dst_state (old_state);
678 :
679 17177 : impl_path_context path_ctxt (&dst_state, logger);
680 17177 : uncertainty_t uncertainty;
681 17177 : impl_region_model_context ctxt (op_ctxt.m_eg,
682 17177 : &op_ctxt.m_src_enode,
683 :
684 : /* TODO: should we be getting the ECs from the
685 : old state, rather than the new? */
686 17177 : &op_ctxt.get_initial_state (),
687 : &dst_state,
688 : &uncertainty,
689 : &path_ctxt,
690 : nullptr,
691 17177 : nullptr);
692 :
693 17177 : tree callee = op_ctxt.get_initial_point ().get_function ()->decl;
694 17177 : tree lhs = DECL_RESULT (callee);
695 :
696 34354 : if (lhs && get_retval ())
697 : {
698 8691 : region_model *dst_region_model = dst_state.m_region_model;
699 8691 : const svalue *sval
700 8691 : = dst_region_model->get_rvalue (get_retval (), &ctxt);
701 8691 : const region *ret_reg = dst_region_model->get_lvalue (lhs, &ctxt);
702 8691 : dst_region_model->set_value (ret_reg, sval, &ctxt);
703 : }
704 :
705 17177 : if (!path_ctxt.terminate_path_p ())
706 17162 : op_ctxt.add_outcome (dst_point, dst_state, false, &uncertainty);
707 34354 : }
708 :
709 : bool
710 3638 : greturn_op::
711 : execute_for_feasibility (const exploded_edge &eedge,
712 : feasibility_state &fstate,
713 : region_model_context *ctxt,
714 : std::unique_ptr<rejected_constraint> *) const
715 : {
716 3638 : tree callee = eedge.m_src->get_function ()->decl;
717 3638 : tree lhs = DECL_RESULT (callee);
718 :
719 7276 : if (lhs && get_retval ())
720 : {
721 1421 : region_model &model = fstate.get_model ();
722 1421 : const svalue *sval = model.get_rvalue (get_retval (), ctxt);
723 1421 : const region *ret_reg = model.get_lvalue (lhs, ctxt);
724 1421 : model.set_value (ret_reg, sval, ctxt);
725 : }
726 :
727 3638 : return true;
728 : }
729 :
730 : void
731 970 : greturn_op::add_any_events_for_eedge (const exploded_edge &,
732 : checker_path &) const
733 : {
734 : // No-op.
735 970 : }
736 :
737 : // class call_and_return_op : public gimple_stmt_op
738 :
739 : std::unique_ptr<operation>
740 41894 : call_and_return_op::make (const gcall &call_stmt)
741 : {
742 41894 : if (is_special_named_call_p (call_stmt, "__analyzer_dump", 0))
743 0 : return std::make_unique<dump_op> (call_stmt, dump_op::dump_kind::state);
744 41894 : else if (is_special_named_call_p (call_stmt, "__analyzer_dump_sarif", 0))
745 0 : return std::make_unique<dump_op> (call_stmt, dump_op::dump_kind::sarif);
746 41894 : else if (is_special_named_call_p (call_stmt, "__analyzer_dump_dot", 0))
747 0 : return std::make_unique<dump_op> (call_stmt, dump_op::dump_kind::dot);
748 41894 : else if (is_special_named_call_p (call_stmt, "__analyzer_dump_state", 2))
749 309 : return std::make_unique<dump_op> (call_stmt, dump_op::dump_kind::state_2);
750 41585 : else if (is_setjmp_call_p (call_stmt))
751 29 : return std::make_unique<setjmp_op> (call_stmt);
752 41556 : else if (is_longjmp_call_p (call_stmt))
753 41 : return std::make_unique<longjmp_op> (call_stmt);
754 41515 : else if (is_cxa_throw_p (call_stmt))
755 73 : return std::make_unique<cxa_throw_op> (call_stmt, false);
756 41442 : else if (is_cxa_rethrow_p (call_stmt))
757 39 : return std::make_unique<cxa_throw_op> (call_stmt, true);
758 :
759 41403 : return std::make_unique<call_and_return_op> (call_stmt);
760 : }
761 :
762 : /* Resolve a function call by one of:
763 :
764 : (a) using a call summary to add eedges to new enodes capturing
765 : the states after summarized outcomes of the call
766 :
767 : (b) adding an interprocedural_call edge, effectively "stepping into"
768 : the called function, for detailed analysis of that path
769 :
770 : (c) simulating the effect of the call, adding an eedge to a new
771 : enode for the outcome of the call. */
772 :
773 : void
774 57242 : call_and_return_op::execute (operation_context &op_ctxt) const
775 : {
776 : /* Can we turn this into an interprocedural call, and execute within
777 : the called fuction? */
778 57242 : const program_state &old_state = op_ctxt.get_initial_state ();
779 57242 : program_state dst_state (old_state);
780 57242 : op_region_model_context ctxt (op_ctxt, dst_state);
781 57242 : ctxt.m_stmt = &get_gcall ();
782 57242 : call_details cd (get_gcall (), old_state.m_region_model, &ctxt);
783 :
784 : /* Regardless of how we handle the call, check any known
785 : preconditions. */
786 57242 : {
787 : /* Check for any preconditions if it's a known_function. */
788 57242 : if (auto kf = maybe_get_known_function (cd))
789 33431 : kf->check_any_preconditions (cd);
790 :
791 : /* Check for any preconditions using sm-state. */
792 : {
793 : int sm_idx;
794 : sm_state_map *smap;
795 457573 : FOR_EACH_VEC_ELT (old_state.m_checker_states, sm_idx, smap)
796 : {
797 400331 : const state_machine &sm
798 800662 : = op_ctxt.m_eg.get_ext_state ().get_sm (sm_idx);
799 400331 : const sm_state_map *old_smap
800 400331 : = old_state.m_checker_states[sm_idx];
801 400331 : sm_state_map *new_smap = dst_state.m_checker_states[sm_idx];
802 400331 : impl_sm_context sm_ctxt (op_ctxt.m_eg, sm_idx, sm,
803 400331 : &op_ctxt.m_src_enode,
804 : &old_state, &dst_state,
805 400331 : old_smap, new_smap, nullptr);
806 400331 : sm.check_call_preconditions (sm_ctxt, cd);
807 400331 : }
808 : }
809 : }
810 :
811 57242 : if (tree callee_fndecl = cd.get_fndecl_for_call ())
812 : {
813 : // Consider using a call summary
814 53442 : if (function *called_fn = DECL_STRUCT_FUNCTION (callee_fndecl))
815 7549 : if (cgraph_edge *edge = get_any_cgraph_edge (op_ctxt))
816 7549 : if (op_ctxt.m_eg.get_analysis_plan ().use_summary_p (edge))
817 : {
818 791 : per_function_data *called_fn_data
819 791 : = op_ctxt.m_eg.get_per_function_data (called_fn);
820 791 : if (called_fn_data)
821 : {
822 753 : replay_call_summaries (op_ctxt, *called_fn,
823 : *called_fn_data, &ctxt);
824 753 : return;
825 : }
826 : }
827 :
828 : // Do we have an entry snode for this fndecl?
829 52689 : if (auto callee_fun = DECL_STRUCT_FUNCTION (callee_fndecl))
830 13592 : if (supernode *callee_entry_snode
831 6796 : = (op_ctxt.get_supergraph ()
832 6796 : .get_node_for_function_entry (*callee_fun)))
833 : {
834 6796 : const call_string *dst_call_string
835 6796 : (op_ctxt.m_src_enode
836 6796 : .get_point ()
837 6796 : .get_call_string ()
838 6796 : .push_call (op_ctxt.m_sedge, *this, *callee_fun));
839 6796 : const program_point dst_point
840 6796 : (callee_entry_snode, *dst_call_string);
841 6796 : auto edge_info
842 : = std::make_unique<interprocedural_call> (get_gcall (),
843 6796 : *callee_fun);
844 6796 : edge_info->update_state (&dst_state, nullptr, &ctxt);
845 6796 : op_ctxt.add_outcome (dst_point, dst_state, false, nullptr,
846 13592 : std::move (edge_info));
847 6796 : return;
848 6796 : }
849 : }
850 :
851 : /* Resolve intraprocedurally: execute the gcall, but using the
852 : dst_state from above so that any preconditions have been applied. */
853 49693 : gimple_stmt_op::execute_on_state (op_ctxt, std::move (dst_state));
854 57242 : }
855 :
856 : cgraph_edge *
857 7549 : call_and_return_op::get_any_cgraph_edge (operation_context &op_ctxt) const
858 : {
859 7549 : tree caller_fndecl = op_ctxt.get_initial_point ().get_fndecl ();
860 7549 : gcc_assert (caller_fndecl);
861 :
862 7549 : auto caller_cgnode = cgraph_node::get (caller_fndecl);
863 7549 : gcc_assert (caller_cgnode);
864 7549 : return caller_cgnode->get_edge (const_cast<gcall *> (&get_gcall ()));
865 : }
866 :
867 : void
868 7321 : call_and_return_op::
869 : add_any_events_for_eedge (const exploded_edge &,
870 : checker_path &) const
871 : {
872 7321 : }
873 :
874 : /* Given PARM_TO_FIND, a PARM_DECL, identify its index (writing it
875 : to *OUT if OUT is non-NULL), and return the corresponding argument
876 : at the callsite. */
877 :
878 : tree
879 247 : call_and_return_op::get_arg_for_parm (tree callee_fndecl,
880 : tree parm_to_find,
881 : callsite_expr *out) const
882 : {
883 247 : gcc_assert (TREE_CODE (parm_to_find) == PARM_DECL);
884 :
885 247 : const gcall &call_stmt = get_gcall ();
886 :
887 247 : unsigned i = 0;
888 262 : for (tree iter_parm = DECL_ARGUMENTS (callee_fndecl); iter_parm;
889 15 : iter_parm = DECL_CHAIN (iter_parm), ++i)
890 : {
891 230 : if (i >= gimple_call_num_args (&call_stmt))
892 : return NULL_TREE;
893 230 : if (iter_parm == parm_to_find)
894 : {
895 215 : if (out)
896 215 : *out = callsite_expr::from_zero_based_param (i);
897 215 : return gimple_call_arg (&call_stmt, i);
898 : }
899 : }
900 :
901 : /* Not found. */
902 : return NULL_TREE;
903 : }
904 :
905 : /* Look for a use of ARG_TO_FIND as an argument at this callsite.
906 : If found, return the default SSA def of the corresponding parm within
907 : the callee, and if OUT is non-NULL, write the index to *OUT.
908 : Only the first match is handled. */
909 :
910 : tree
911 431 : call_and_return_op::get_parm_for_arg (tree callee_fndecl,
912 : tree arg_to_find,
913 : callsite_expr *out) const
914 : {
915 431 : const gcall &call_stmt = get_gcall ();
916 :
917 431 : unsigned i = 0;
918 711 : for (tree iter_parm = DECL_ARGUMENTS (callee_fndecl); iter_parm;
919 280 : iter_parm = DECL_CHAIN (iter_parm), ++i)
920 : {
921 376 : if (i >= gimple_call_num_args (&call_stmt))
922 : return NULL_TREE;
923 375 : tree param = gimple_call_arg (&call_stmt, i);
924 375 : if (arg_to_find == param)
925 : {
926 95 : if (out)
927 95 : *out = callsite_expr::from_zero_based_param (i);
928 95 : return ssa_default_def (DECL_STRUCT_FUNCTION (callee_fndecl),
929 95 : iter_parm);
930 : }
931 : }
932 :
933 : /* Not found. */
934 : return NULL_TREE;
935 : }
936 :
937 : /* Map caller_expr back to an expr within the callee, or return NULL_TREE.
938 : If non-NULL is returned, populate OUT. */
939 :
940 : tree
941 431 : call_and_return_op::map_expr_from_caller_to_callee (tree callee_fndecl,
942 : tree caller_expr,
943 : callsite_expr *out) const
944 : {
945 : /* Is it an argument (actual param)? If so, convert to
946 : parameter (formal param). */
947 431 : tree parm = get_parm_for_arg (callee_fndecl, caller_expr, out);
948 431 : if (parm)
949 : return parm;
950 : /* Otherwise try return value. */
951 340 : if (caller_expr == gimple_call_lhs (&get_gcall ()))
952 : {
953 94 : if (out)
954 94 : *out = callsite_expr::from_return_value ();
955 94 : return DECL_RESULT (callee_fndecl);
956 : }
957 :
958 : return NULL_TREE;
959 : }
960 :
961 : /* Map callee_expr back to an expr within the caller, or return NULL_TREE.
962 : If non-NULL is returned, populate OUT. */
963 :
964 : tree
965 1193 : call_and_return_op::map_expr_from_callee_to_caller (tree callee_fndecl,
966 : tree callee_expr,
967 : callsite_expr *out) const
968 : {
969 1193 : if (callee_expr == NULL_TREE)
970 : return NULL_TREE;
971 :
972 : /* If it's a parameter (formal param), get the argument (actual param). */
973 384 : if (TREE_CODE (callee_expr) == PARM_DECL)
974 7 : return get_arg_for_parm (callee_fndecl, callee_expr, out);
975 :
976 : /* Similar for the default SSA name of the PARM_DECL. */
977 377 : if (TREE_CODE (callee_expr) == SSA_NAME
978 275 : && SSA_NAME_IS_DEFAULT_DEF (callee_expr)
979 617 : && TREE_CODE (SSA_NAME_VAR (callee_expr)) == PARM_DECL)
980 240 : return get_arg_for_parm (callee_fndecl, SSA_NAME_VAR (callee_expr), out);
981 :
982 : /* Otherwise try return value. */
983 137 : if (callee_expr == DECL_RESULT (callee_fndecl))
984 : {
985 0 : if (out)
986 0 : *out = callsite_expr::from_return_value ();
987 0 : return gimple_call_lhs (&get_gcall ());
988 : }
989 :
990 : return NULL_TREE;
991 : }
992 :
993 : const known_function *
994 57242 : call_and_return_op::maybe_get_known_function (const call_details &cd) const
995 : {
996 57242 : region_model_manager *mgr = cd.get_manager ();
997 57242 : known_function_manager *known_fn_mgr = mgr->get_known_function_manager ();
998 :
999 57242 : if (gimple_call_internal_p (&get_gcall ()))
1000 3430 : return known_fn_mgr->get_internal_fn
1001 3430 : (gimple_call_internal_fn (&get_gcall ()));
1002 :
1003 53812 : if (tree callee_fndecl = cd.get_fndecl_for_call ())
1004 53442 : return known_fn_mgr->get_match (callee_fndecl, cd);
1005 :
1006 : return nullptr;
1007 : }
1008 :
1009 : void
1010 753 : call_and_return_op::
1011 : replay_call_summaries (operation_context &op_ctxt,
1012 : function &called_fn,
1013 : per_function_data &called_fn_data,
1014 : region_model_context *ctxt) const
1015 : {
1016 753 : logger *logger = op_ctxt.get_logger ();
1017 753 : LOG_SCOPE (logger);
1018 :
1019 3802 : for (auto summary : called_fn_data.m_summaries)
1020 : {
1021 1543 : gcc_assert (summary);
1022 1543 : replay_call_summary (op_ctxt, called_fn, *summary, ctxt);
1023 : }
1024 753 : }
1025 :
1026 : /* A concrete call_info subclass representing a replay of a call summary. */
1027 :
1028 : class call_summary_edge_info : public call_info
1029 : {
1030 : public:
1031 1418 : call_summary_edge_info (const call_details &cd,
1032 : const function &called_fn,
1033 : call_summary &summary,
1034 : const extrinsic_state &ext_state)
1035 1418 : : call_info (cd, called_fn),
1036 1418 : m_called_fn (called_fn),
1037 1418 : m_summary (summary),
1038 1418 : m_ext_state (ext_state)
1039 : {}
1040 :
1041 0 : bool update_state (program_state *state,
1042 : const exploded_edge *,
1043 : region_model_context *ctxt) const final override
1044 : {
1045 : /* Update STATE based on summary_end_state. */
1046 0 : call_details cd (get_call_details (state->m_region_model, ctxt));
1047 0 : call_summary_replay r (cd, m_called_fn, m_summary, m_ext_state);
1048 0 : const program_state &summary_end_state = m_summary.get_state ();
1049 0 : return state->replay_call_summary (r, summary_end_state);
1050 0 : }
1051 :
1052 88 : bool update_model (region_model *model,
1053 : const exploded_edge *,
1054 : region_model_context *ctxt) const final override
1055 : {
1056 : /* Update STATE based on summary_end_state. */
1057 88 : call_details cd (get_call_details (model, ctxt));
1058 88 : call_summary_replay r (cd, m_called_fn, m_summary, m_ext_state);
1059 88 : const program_state &summary_end_state = m_summary.get_state ();
1060 88 : model->replay_call_summary (r, *summary_end_state.m_region_model);
1061 88 : return true;
1062 88 : }
1063 :
1064 62 : void print_desc (pretty_printer &pp) const final override
1065 : {
1066 62 : pp_string (&pp, m_summary.get_desc ().get ());
1067 62 : }
1068 :
1069 : private:
1070 : const function &m_called_fn;
1071 : call_summary &m_summary;
1072 : const extrinsic_state &m_ext_state;
1073 : };
1074 :
1075 : void
1076 1543 : call_and_return_op::
1077 : replay_call_summary (operation_context &op_ctxt,
1078 : function &called_fn,
1079 : call_summary &summary,
1080 : region_model_context *ctxt) const
1081 : {
1082 1543 : logger *logger = op_ctxt.get_logger ();
1083 1543 : LOG_SCOPE (logger);
1084 1543 : if (logger)
1085 0 : logger->log ("using %s as summary for call to %qE from %qE",
1086 0 : summary.get_desc ().get (),
1087 : called_fn.decl,
1088 0 : op_ctxt.get_initial_point ().get_function ()->decl);
1089 1543 : const extrinsic_state &ext_state = op_ctxt.get_ext_state ();
1090 1543 : const program_state &old_state = op_ctxt.get_initial_state ();
1091 1543 : const program_state &summary_end_state = summary.get_state ();
1092 1543 : if (logger)
1093 : {
1094 0 : pretty_printer *pp = logger->get_printer ();
1095 :
1096 0 : logger->start_log_line ();
1097 0 : pp_string (pp, "callsite state: ");
1098 0 : old_state.dump_to_pp (ext_state, true, false, pp);
1099 0 : logger->end_log_line ();
1100 :
1101 0 : logger->start_log_line ();
1102 0 : pp_string (pp, "summary end state: ");
1103 0 : summary_end_state.dump_to_pp (ext_state, true, false, pp);
1104 0 : logger->end_log_line ();
1105 : }
1106 :
1107 1543 : program_state new_state (old_state);
1108 :
1109 1543 : call_details cd (get_gcall (), new_state.m_region_model, ctxt);
1110 1543 : call_summary_replay r (cd, called_fn, summary, ext_state);
1111 :
1112 1543 : if (new_state.replay_call_summary (r, summary_end_state))
1113 1418 : op_ctxt.add_outcome
1114 1418 : (op_ctxt.get_next_intraprocedural_point (),
1115 : new_state,
1116 : true,
1117 : nullptr,
1118 2836 : std::make_unique<call_summary_edge_info> (cd,
1119 : called_fn,
1120 : summary,
1121 : ext_state));
1122 1543 : }
1123 :
1124 : // class dump_op : public call_and_return_op
1125 :
1126 : void
1127 357 : dump_op::execute (operation_context &op_ctxt) const
1128 : {
1129 357 : const program_state &state = op_ctxt.get_initial_state ();
1130 357 : switch (m_dump_kind)
1131 : {
1132 0 : default:
1133 0 : gcc_unreachable ();
1134 0 : case dump_kind::state:
1135 : /* Handle the builtin "__analyzer_dump" by dumping state
1136 : to stderr. */
1137 0 : state.dump (op_ctxt.get_ext_state (), true);
1138 0 : break;
1139 0 : case dump_kind::sarif:
1140 0 : state.dump_sarif (op_ctxt.get_ext_state ());
1141 0 : break;
1142 0 : case dump_kind::dot:
1143 0 : state.dump_dot (op_ctxt.get_ext_state ());
1144 0 : break;
1145 357 : case dump_kind::state_2:
1146 357 : {
1147 357 : program_state dst_state (state);
1148 357 : op_region_model_context ctxt (op_ctxt, dst_state);
1149 357 : dst_state.impl_call_analyzer_dump_state (get_gcall (),
1150 : op_ctxt.get_ext_state (),
1151 : &ctxt);
1152 357 : }
1153 357 : break;
1154 : }
1155 :
1156 357 : op_ctxt.add_outcome (op_ctxt.get_next_intraprocedural_point (),
1157 : state, false, nullptr);
1158 357 : }
1159 :
1160 : // class setjmp_op : public call_and_return_op
1161 :
1162 : void
1163 34 : setjmp_op::execute (operation_context &op_ctxt) const
1164 : {
1165 34 : program_state dst_state (op_ctxt.get_initial_state ());
1166 34 : op_region_model_context ctxt (op_ctxt, dst_state);
1167 34 : dst_state.m_region_model->on_setjmp (get_gcall (),
1168 34 : op_ctxt.m_src_enode,
1169 : op_ctxt.m_sedge,
1170 : &ctxt);
1171 34 : op_ctxt.add_outcome (op_ctxt.get_next_intraprocedural_point (),
1172 : dst_state, true, nullptr);
1173 34 : }
1174 :
1175 : void
1176 20 : setjmp_op::add_any_events_for_eedge (const exploded_edge &eedge,
1177 : checker_path &out_path) const
1178 : {
1179 20 : out_path.add_event
1180 20 : (std::make_unique<setjmp_event>
1181 40 : (event_loc_info (eedge.m_src),
1182 20 : eedge.m_src,
1183 : get_gcall ()));
1184 20 : }
1185 :
1186 : // class longjmp_op : public call_and_return_op
1187 :
1188 : void
1189 63 : longjmp_op::execute (operation_context &op_ctxt) const
1190 : {
1191 63 : program_state dst_state (op_ctxt.get_initial_state ());
1192 63 : op_region_model_context ctxt (op_ctxt, dst_state);
1193 63 : op_ctxt.m_src_enode.on_longjmp (op_ctxt.m_eg, get_gcall (), &dst_state,
1194 : &ctxt);
1195 63 : }
1196 :
1197 : // class cxa_throw_op : public call_and_return_op
1198 :
1199 : void
1200 190 : cxa_throw_op::execute (operation_context &op_ctxt) const
1201 : {
1202 190 : program_state dst_state (op_ctxt.get_initial_state ());
1203 190 : op_region_model_context ctxt (op_ctxt, dst_state);
1204 190 : program_point after_throw_point (op_ctxt.get_next_intraprocedural_point ());
1205 190 : op_ctxt.m_src_enode.on_throw (op_ctxt.m_eg,
1206 : get_gcall (),
1207 : after_throw_point,
1208 : &dst_state,
1209 190 : m_is_rethrow,
1210 : &ctxt);
1211 : // We don't continue along op_ctxt's superedge
1212 190 : }
1213 :
1214 : // class control_flow_op : public operation
1215 :
1216 : void
1217 18114 : control_flow_op::
1218 : walk_load_store_addr_ops (void *data,
1219 : walk_stmt_load_store_addr_fn load_cb,
1220 : walk_stmt_load_store_addr_fn store_cb,
1221 : walk_stmt_load_store_addr_fn addr_cb) const
1222 : {
1223 18114 : walk_stmt_load_store_addr_ops (const_cast <gimple *> (&m_ctrlflow_stmt),
1224 : data,
1225 : load_cb, store_cb, addr_cb);
1226 18114 : }
1227 :
1228 : void
1229 2608 : control_flow_op::add_any_events_for_eedge (const exploded_edge &eedge,
1230 : checker_path &out_path) const
1231 : {
1232 2608 : out_path.add_event
1233 2608 : (std::make_unique<start_cfg_edge_event> (eedge,
1234 5216 : event_loc_info (eedge.m_src),
1235 2608 : this));
1236 2608 : out_path.add_event
1237 2608 : (std::make_unique<end_cfg_edge_event> (eedge,
1238 5216 : event_loc_info (eedge.m_dest),
1239 2608 : this));
1240 2608 : }
1241 :
1242 : /* Attempt to generate a description of any condition that holds at this edge.
1243 :
1244 : The intent is to make the user-facing messages more clear, especially for
1245 : cases where there's a single or double-negative, such as
1246 : when describing the false branch of an inverted condition.
1247 :
1248 : For example, rather than printing just:
1249 :
1250 : | if (!ptr)
1251 : | ~
1252 : | |
1253 : | (1) following 'false' branch...
1254 :
1255 : it's clearer to spell out the condition that holds:
1256 :
1257 : | if (!ptr)
1258 : | ~
1259 : | |
1260 : | (1) following 'false' branch (when 'ptr' is non-NULL)...
1261 : ^^^^^^^^^^^^^^^^^^^^^^
1262 :
1263 : In the above example, this function would generate the highlighted
1264 : string: "when 'ptr' is non-NULL".
1265 :
1266 : If the edge is not a condition, or it's not clear that a description of
1267 : the condition would be helpful to the user, return NULL. */
1268 :
1269 : label_text
1270 441 : control_flow_op::maybe_describe_condition (bool ) const
1271 : {
1272 441 : return label_text::borrow (nullptr);
1273 : }
1274 :
1275 : void
1276 51867 : control_flow_op::execute (operation_context &op_ctxt) const
1277 : {
1278 51867 : auto logger = op_ctxt.get_logger ();
1279 51867 : LOG_SCOPE (logger);
1280 :
1281 51867 : program_state dst_state (op_ctxt.get_initial_state ());
1282 51867 : op_region_model_context ctxt (op_ctxt, dst_state);
1283 51867 : if (apply_constraints (&op_ctxt.m_sedge,
1284 51867 : *dst_state.m_region_model,
1285 : &ctxt,
1286 : nullptr))
1287 : {
1288 45025 : bool unknown_side_effects;
1289 45025 : handle_on_stmt_for_state_machines (op_ctxt,
1290 : dst_state,
1291 : nullptr,
1292 : unknown_side_effects,
1293 : m_ctrlflow_stmt);
1294 :
1295 45025 : if (!ctxt.terminate_path_p ())
1296 : {
1297 44997 : auto dst_point (op_ctxt.get_next_intraprocedural_point ());
1298 44997 : op_ctxt.add_outcome (dst_point, dst_state, false, nullptr);
1299 : }
1300 : }
1301 51867 : }
1302 :
1303 : bool
1304 29137 : control_flow_op::
1305 : execute_for_feasibility (const exploded_edge &eedge,
1306 : feasibility_state &fstate,
1307 : region_model_context *ctxt,
1308 : std::unique_ptr<rejected_constraint> *out_rc) const
1309 : {
1310 29137 : gcc_assert (eedge.m_sedge);
1311 29137 : return apply_constraints (eedge.m_sedge,
1312 : fstate.get_model (),
1313 : ctxt,
1314 29137 : out_rc);
1315 : }
1316 :
1317 : // class gcond_edge_op : public control_flow_op
1318 :
1319 14920 : gcond_edge_op::gcond_edge_op (::edge cfg_edge,
1320 14920 : const gcond &cond_stmt)
1321 : : control_flow_op (kind::cond_edge, cfg_edge, cond_stmt),
1322 14920 : m_true_value (get_flags () & EDGE_TRUE_VALUE)
1323 : {
1324 : /* Exactly one of EDGE_TRUE_VALUE and EDGE_FALSE_VALUE must
1325 : be set on CFG_EDGE. */
1326 14920 : gcc_assert (static_cast<bool> (get_flags () & EDGE_TRUE_VALUE)
1327 : ^ static_cast<bool> (get_flags () & EDGE_FALSE_VALUE));
1328 14920 : }
1329 :
1330 : void
1331 6929 : gcond_edge_op::print_as_edge_label (pretty_printer *pp,
1332 : bool user_facing) const
1333 : {
1334 6929 : if (!user_facing)
1335 228 : pp_gimple_stmt_1 (pp, &get_ctrlflow_stmt (), 0, (dump_flags_t)0);
1336 :
1337 6929 : if (m_true_value)
1338 3738 : pp_printf (pp, "true");
1339 : else
1340 3191 : pp_printf (pp, "false");
1341 6929 : }
1342 :
1343 : label_text
1344 6701 : gcond_edge_op::maybe_describe_condition (bool can_colorize) const
1345 : {
1346 6701 : const gcond &cond_stmt = get_gcond ();
1347 6701 : enum tree_code op = gimple_cond_code (&cond_stmt);
1348 6701 : tree lhs = gimple_cond_lhs (&cond_stmt);
1349 6701 : tree rhs = gimple_cond_rhs (&cond_stmt);
1350 6701 : if (!m_true_value)
1351 3087 : op = invert_tree_comparison (op, false /* honor_nans */);
1352 6701 : return maybe_describe_condition (can_colorize,
1353 6701 : lhs, op, rhs);
1354 : }
1355 :
1356 : /* Subroutine of gcond_edge_op::maybe_describe_condition above.
1357 :
1358 : Attempt to generate a user-facing description of the condition
1359 : LHS OP RHS, but only if it is likely to make it easier for the
1360 : user to understand a condition. */
1361 :
1362 : label_text
1363 6701 : gcond_edge_op::maybe_describe_condition (bool can_colorize,
1364 : tree lhs,
1365 : enum tree_code op,
1366 : tree rhs)
1367 : {
1368 : /* In theory we could just build a tree via
1369 : fold_build2 (op, boolean_type_node, lhs, rhs)
1370 : and print it with %qE on it, but this leads to warts such as
1371 : parenthesizing vars, such as '(i) <= 9', and uses of '<unknown>'. */
1372 :
1373 : /* Special-case: describe testing the result of strcmp, as figuring
1374 : out what the "true" or "false" path is can be confusing to the user. */
1375 6701 : if (TREE_CODE (lhs) == SSA_NAME
1376 6701 : && zerop (rhs))
1377 : {
1378 5100 : if (gcall *call = dyn_cast <gcall *> (SSA_NAME_DEF_STMT (lhs)))
1379 1872 : if (is_special_named_call_p (*call, "strcmp", 2))
1380 : {
1381 60 : if (op == EQ_EXPR)
1382 9 : return label_text::borrow ("when the strings are equal");
1383 51 : if (op == NE_EXPR)
1384 51 : return label_text::borrow ("when the strings are non-equal");
1385 : }
1386 : }
1387 :
1388 : /* Only attempt to generate text for sufficiently simple expressions. */
1389 6641 : if (!should_print_expr_p (lhs))
1390 2654 : return label_text::borrow (nullptr);
1391 3987 : if (!should_print_expr_p (rhs))
1392 47 : return label_text::borrow (nullptr);
1393 :
1394 : /* Special cases for pointer comparisons against NULL. */
1395 6357 : if (POINTER_TYPE_P (TREE_TYPE (lhs))
1396 1523 : && POINTER_TYPE_P (TREE_TYPE (rhs))
1397 5463 : && zerop (rhs))
1398 : {
1399 1425 : if (op == EQ_EXPR)
1400 369 : return make_label_text (can_colorize, "when %qE is NULL",
1401 369 : lhs);
1402 1056 : if (op == NE_EXPR)
1403 1056 : return make_label_text (can_colorize, "when %qE is non-NULL",
1404 1056 : lhs);
1405 : }
1406 :
1407 2515 : return make_label_text (can_colorize, "when %<%E %s %E%>",
1408 2515 : lhs, op_symbol_code (op), rhs);
1409 : }
1410 :
1411 : /* Subroutine of maybe_describe_condition.
1412 :
1413 : Return true if EXPR is we will get suitable user-facing output
1414 : from %E on it. */
1415 :
1416 : bool
1417 10628 : gcond_edge_op::should_print_expr_p (tree expr)
1418 : {
1419 14889 : if (TREE_CODE (expr) == SSA_NAME)
1420 : {
1421 6962 : if (SSA_NAME_VAR (expr))
1422 : return should_print_expr_p (SSA_NAME_VAR (expr));
1423 : else
1424 : return false;
1425 : }
1426 :
1427 7927 : if (DECL_P (expr))
1428 : return true;
1429 :
1430 3666 : if (CONSTANT_CLASS_P (expr))
1431 3666 : return true;
1432 :
1433 : return false;
1434 : }
1435 :
1436 : bool
1437 72515 : gcond_edge_op::
1438 : apply_constraints (const superedge *,
1439 : region_model &model,
1440 : region_model_context *ctxt,
1441 : std::unique_ptr<rejected_constraint> *out) const
1442 : {
1443 72515 : const gcond &cond_stmt = get_gcond ();
1444 72515 : enum tree_code op = gimple_cond_code (&cond_stmt);
1445 72515 : tree lhs = gimple_cond_lhs (&cond_stmt);
1446 72515 : tree rhs = gimple_cond_rhs (&cond_stmt);
1447 72515 : if (!m_true_value)
1448 32707 : op = invert_tree_comparison (op, false /* honor_nans */);
1449 72515 : return model.add_constraint (lhs, op, rhs, ctxt, out);
1450 : }
1451 :
1452 : // class ggoto_edge_op : public control_flow_op
1453 :
1454 36 : ggoto_edge_op::ggoto_edge_op (::edge cfg_edge,
1455 : const ggoto &goto_stmt,
1456 36 : tree dst_label)
1457 : : control_flow_op (kind::goto_edge, cfg_edge, goto_stmt),
1458 36 : m_dst_label (dst_label)
1459 : {
1460 36 : }
1461 :
1462 : void
1463 60 : ggoto_edge_op::print_as_edge_label (pretty_printer *pp,
1464 : bool user_facing) const
1465 : {
1466 60 : if (!user_facing)
1467 0 : pp_gimple_stmt_1 (pp, &get_ctrlflow_stmt (), 0, (dump_flags_t)0);
1468 :
1469 60 : if (m_dst_label)
1470 60 : pp_printf (pp, "%qD", m_dst_label);
1471 60 : }
1472 :
1473 : label_text
1474 60 : ggoto_edge_op::maybe_describe_condition (bool) const
1475 : {
1476 60 : return label_text::borrow ("");
1477 : }
1478 :
1479 : bool
1480 93 : ggoto_edge_op::
1481 : apply_constraints (const superedge *,
1482 : region_model &model,
1483 : region_model_context *ctxt,
1484 : std::unique_ptr<rejected_constraint> *out_rc) const
1485 : {
1486 93 : const ggoto &goto_stmt = get_ggoto ();
1487 93 : tree dest = gimple_goto_dest (&goto_stmt);
1488 93 : const svalue *dest_sval = model.get_rvalue (dest, ctxt);
1489 :
1490 : /* If we know we were jumping to a specific label. */
1491 93 : if (m_dst_label)
1492 : {
1493 93 : auto mgr = model.get_manager ();
1494 93 : const label_region *dst_label_reg
1495 93 : = mgr->get_region_for_label (m_dst_label);
1496 93 : const svalue *dst_label_ptr
1497 93 : = mgr->get_ptr_svalue (ptr_type_node, dst_label_reg);
1498 :
1499 93 : if (!model.add_constraint (dest_sval, EQ_EXPR, dst_label_ptr, ctxt))
1500 : {
1501 12 : if (out_rc)
1502 3 : *out_rc
1503 3 : = std::make_unique <rejected_op_constraint> (model,
1504 : dest_sval,
1505 6 : EQ_EXPR,
1506 3 : dst_label_ptr);
1507 12 : return false;
1508 : }
1509 : }
1510 :
1511 : return true;
1512 : }
1513 :
1514 : // class switch_case_op : public control_flow_op
1515 :
1516 2983 : switch_case_op::switch_case_op (function &fun,
1517 : ::edge cfg_edge,
1518 : const gswitch &switch_stmt,
1519 2983 : bounded_ranges_manager &mgr)
1520 2983 : : control_flow_op (kind::switch_edge, cfg_edge, switch_stmt)
1521 : {
1522 : /* Populate m_case_labels with all cases which go to DST. */
1523 542488 : for (unsigned i = 0; i < gimple_switch_num_labels (&switch_stmt); i++)
1524 : {
1525 539505 : tree case_ = gimple_switch_label (&switch_stmt, i);
1526 539505 : basic_block bb = label_to_block (&fun,
1527 539505 : CASE_LABEL (case_));
1528 539505 : if (bb == cfg_edge->dest)
1529 3552 : m_case_labels.push_back (case_);
1530 : }
1531 :
1532 2983 : auto_vec <const bounded_ranges *> case_ranges_vec
1533 2983 : (gimple_switch_num_labels (&switch_stmt));
1534 6535 : for (auto case_label : m_case_labels)
1535 : {
1536 : /* Get the ranges for this case label. */
1537 3552 : const bounded_ranges *case_ranges
1538 3552 : = mgr.make_case_label_ranges (&switch_stmt, case_label);
1539 3552 : case_ranges_vec.quick_push (case_ranges);
1540 : }
1541 :
1542 2983 : m_all_cases_ranges = mgr.get_or_create_union (case_ranges_vec);
1543 2983 : }
1544 :
1545 : /* Print "case VAL:", "case LOWER ... UPPER:", or "default:" to PP. */
1546 :
1547 : void
1548 448 : switch_case_op::print_as_edge_label (pretty_printer *pp,
1549 : bool user_facing) const
1550 : {
1551 448 : if (user_facing)
1552 : {
1553 888 : for (unsigned i = 0; i < m_case_labels.size (); ++i)
1554 : {
1555 447 : if (i > 0)
1556 6 : pp_string (pp, ", ");
1557 447 : tree case_label = m_case_labels[i];
1558 447 : gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
1559 447 : tree lower_bound = CASE_LOW (case_label);
1560 447 : tree upper_bound = CASE_HIGH (case_label);
1561 447 : if (lower_bound)
1562 : {
1563 162 : pp_printf (pp, "case ");
1564 162 : dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, false);
1565 162 : if (upper_bound)
1566 : {
1567 12 : pp_printf (pp, " ... ");
1568 12 : dump_generic_node (pp, upper_bound, 0, (dump_flags_t)0,
1569 : false);
1570 : }
1571 162 : pp_printf (pp, ":");
1572 : }
1573 : else
1574 285 : pp_printf (pp, "default:");
1575 : }
1576 : }
1577 : else
1578 : {
1579 7 : pp_character (pp, '{');
1580 15 : for (unsigned i = 0; i < m_case_labels.size (); ++i)
1581 : {
1582 8 : if (i > 0)
1583 1 : pp_string (pp, ", ");
1584 8 : tree case_label = m_case_labels[i];
1585 8 : gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
1586 8 : tree lower_bound = CASE_LOW (case_label);
1587 8 : tree upper_bound = CASE_HIGH (case_label);
1588 8 : if (lower_bound)
1589 : {
1590 6 : if (upper_bound)
1591 : {
1592 0 : pp_character (pp, '[');
1593 0 : dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0,
1594 : false);
1595 0 : pp_string (pp, ", ");
1596 0 : dump_generic_node (pp, upper_bound, 0, (dump_flags_t)0,
1597 : false);
1598 0 : pp_character (pp, ']');
1599 : }
1600 : else
1601 6 : dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, false);
1602 : }
1603 : else
1604 2 : pp_printf (pp, "default");
1605 : }
1606 7 : pp_character (pp, '}');
1607 7 : if (implicitly_created_default_p ())
1608 : {
1609 2 : pp_string (pp, " IMPLICITLY CREATED");
1610 : }
1611 : }
1612 448 : }
1613 :
1614 : /* Return true iff SWITCH_STMT has a non-default label that contains
1615 : INT_CST. */
1616 :
1617 : static bool
1618 142 : has_nondefault_case_for_value_p (const gswitch *switch_stmt, tree int_cst)
1619 : {
1620 : /* We expect the initial label to be the default; skip it. */
1621 142 : gcc_assert (CASE_LOW (gimple_switch_label (switch_stmt, 0)) == NULL_TREE);
1622 142 : unsigned min_idx = 1;
1623 142 : unsigned max_idx = gimple_switch_num_labels (switch_stmt) - 1;
1624 :
1625 : /* Binary search: try to find the label containing INT_CST.
1626 : This requires the cases to be sorted by CASE_LOW (done by the
1627 : gimplifier). */
1628 257 : while (max_idx >= min_idx)
1629 : {
1630 247 : unsigned case_idx = (min_idx + max_idx) / 2;
1631 247 : tree label = gimple_switch_label (switch_stmt, case_idx);
1632 247 : tree low = CASE_LOW (label);
1633 247 : gcc_assert (low);
1634 247 : tree high = CASE_HIGH (label);
1635 247 : if (!high)
1636 195 : high = low;
1637 247 : if (tree_int_cst_compare (int_cst, low) < 0)
1638 : {
1639 : /* INT_CST is below the range of this label. */
1640 27 : gcc_assert (case_idx > 0);
1641 27 : max_idx = case_idx - 1;
1642 : }
1643 220 : else if (tree_int_cst_compare (int_cst, high) > 0)
1644 : {
1645 : /* INT_CST is above the range of this case. */
1646 88 : min_idx = case_idx + 1;
1647 : }
1648 : else
1649 : /* This case contains INT_CST. */
1650 : return true;
1651 : }
1652 : /* Not found. */
1653 : return false;
1654 : }
1655 :
1656 : /* Return true iff SWITCH_STMT (which must be on an enum value)
1657 : has nondefault cases handling all values in the enum. */
1658 :
1659 : static bool
1660 45 : has_nondefault_cases_for_all_enum_values_p (const gswitch *switch_stmt,
1661 : tree type)
1662 : {
1663 45 : gcc_assert (switch_stmt);
1664 45 : gcc_assert (TREE_CODE (type) == ENUMERAL_TYPE);
1665 :
1666 45 : for (tree enum_val_iter = TYPE_VALUES (type);
1667 177 : enum_val_iter;
1668 132 : enum_val_iter = TREE_CHAIN (enum_val_iter))
1669 : {
1670 142 : tree enum_val = TREE_VALUE (enum_val_iter);
1671 142 : gcc_assert (TREE_CODE (enum_val) == CONST_DECL);
1672 142 : gcc_assert (TREE_CODE (DECL_INITIAL (enum_val)) == INTEGER_CST);
1673 142 : if (!has_nondefault_case_for_value_p (switch_stmt,
1674 142 : DECL_INITIAL (enum_val)))
1675 : return false;
1676 : }
1677 : return true;
1678 : }
1679 :
1680 : /* Given an EDGE guarded by SWITCH_STMT, determine appropriate constraints
1681 : for the edge to be taken.
1682 :
1683 : If they are feasible, add the constraints and return true.
1684 :
1685 : Return false if the constraints contradict existing knowledge
1686 : (and so the edge should not be taken).
1687 : When returning false, if OUT is non-NULL, write a new rejected_constraint
1688 : to it. */
1689 :
1690 : bool
1691 8072 : switch_case_op::
1692 : apply_constraints (const superedge *,
1693 : region_model &model,
1694 : region_model_context *ctxt,
1695 : std::unique_ptr<rejected_constraint> *out) const
1696 : {
1697 8072 : const gswitch *switch_stmt = &get_gswitch ();
1698 8072 : tree index = gimple_switch_index (switch_stmt);
1699 8072 : const svalue *index_sval = model.get_rvalue (index, ctxt);
1700 8072 : bool check_index_type = true;
1701 :
1702 : /* With -fshort-enum, there may be a type cast. */
1703 6812 : if (ctxt && index_sval->get_kind () == SK_UNARYOP
1704 8519 : && TREE_CODE (index_sval->get_type ()) == INTEGER_TYPE)
1705 : {
1706 429 : const unaryop_svalue *unaryop = as_a <const unaryop_svalue *> (index_sval);
1707 429 : if (unaryop->get_op () == NOP_EXPR
1708 429 : && is_a <const initial_svalue *> (unaryop->get_arg ()))
1709 411 : if (const initial_svalue *initvalop = (as_a <const initial_svalue *>
1710 411 : (unaryop->get_arg ())))
1711 411 : if (initvalop->get_type ()
1712 411 : && TREE_CODE (initvalop->get_type ()) == ENUMERAL_TYPE)
1713 : {
1714 : index_sval = initvalop;
1715 : check_index_type = false;
1716 : }
1717 : }
1718 :
1719 : /* If we're switching based on an enum type, assume that the user is only
1720 : working with values from the enum. Hence if this is an
1721 : implicitly-created "default", assume it doesn't get followed.
1722 : This fixes numerous "uninitialized" false positives where we otherwise
1723 : consider jumping past the initialization cases. */
1724 :
1725 8072 : if (/* Don't check during feasibility-checking (when ctxt is NULL). */
1726 : ctxt
1727 : /* Must be an enum value. */
1728 6812 : && index_sval->get_type ()
1729 6812 : && (!check_index_type
1730 6497 : || TREE_CODE (TREE_TYPE (index)) == ENUMERAL_TYPE)
1731 663 : && TREE_CODE (index_sval->get_type ()) == ENUMERAL_TYPE
1732 : /* If we have a constant, then we can check it directly. */
1733 663 : && index_sval->get_kind () != SK_CONSTANT
1734 634 : && implicitly_created_default_p ()
1735 45 : && has_nondefault_cases_for_all_enum_values_p (switch_stmt,
1736 : index_sval->get_type ())
1737 : /* Don't do this if there's a chance that the index is
1738 : attacker-controlled. */
1739 8107 : && !ctxt->possibly_tainted_p (index_sval))
1740 : {
1741 33 : if (out)
1742 0 : *out = std::make_unique <rejected_default_case> (model);
1743 33 : return false;
1744 : }
1745 :
1746 8039 : bool sat
1747 16078 : = model.get_constraints ()->add_bounded_ranges (index_sval,
1748 8039 : m_all_cases_ranges);
1749 8039 : if (!sat && out)
1750 48 : *out = std::make_unique <rejected_ranges_constraint>
1751 48 : (model, index, m_all_cases_ranges);
1752 8039 : if (sat && ctxt && !m_all_cases_ranges->empty_p ())
1753 6202 : ctxt->on_bounded_ranges (*index_sval, *m_all_cases_ranges);
1754 : return sat;
1755 : }
1756 :
1757 : /* Return true iff this op's edge is purely for an
1758 : implicitly-created "default". */
1759 :
1760 : bool
1761 659 : switch_case_op::implicitly_created_default_p () const
1762 : {
1763 659 : if (m_case_labels.size () != 1)
1764 : return false;
1765 :
1766 592 : tree case_label = m_case_labels[0];
1767 592 : gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
1768 592 : if (CASE_LOW (case_label))
1769 : return false;
1770 :
1771 : /* We have a single "default" case.
1772 : Assume that it was implicitly created if it has UNKNOWN_LOCATION. */
1773 172 : return EXPR_LOCATION (case_label) == UNKNOWN_LOCATION;
1774 : }
1775 :
1776 : /* Given an ERT_TRY region, get the eh_catch corresponding to
1777 : the label of DST_SNODE, if any. */
1778 :
1779 : static eh_catch
1780 179 : get_catch (eh_region eh_reg, supernode *dst_snode)
1781 : {
1782 179 : gcc_assert (eh_reg->type == ERT_TRY);
1783 :
1784 179 : tree dst_snode_label = dst_snode->get_label ();
1785 179 : if (!dst_snode_label)
1786 : return nullptr;
1787 :
1788 124 : for (eh_catch iter = eh_reg->u.eh_try.first_catch;
1789 184 : iter;
1790 60 : iter = iter->next_catch)
1791 184 : if (iter->label == dst_snode_label)
1792 : return iter;
1793 :
1794 : return nullptr;
1795 : }
1796 :
1797 : class rejected_eh_dispatch : public rejected_constraint
1798 : {
1799 : public:
1800 0 : rejected_eh_dispatch (const region_model &model)
1801 0 : : rejected_constraint (model)
1802 : {}
1803 :
1804 0 : void dump_to_pp (pretty_printer *pp) const final override
1805 : {
1806 0 : pp_printf (pp, "rejected_eh_dispatch");
1807 0 : }
1808 : };
1809 :
1810 : static bool
1811 273 : exception_matches_type_p (tree exception_type,
1812 : tree catch_type)
1813 : {
1814 0 : if (catch_type == exception_type)
1815 0 : return true;
1816 :
1817 : /* TODO (PR analyzer/119697): we should also handle subclasses etc;
1818 : see the rules in https://en.cppreference.com/w/cpp/language/catch
1819 :
1820 : It looks like we should be calling (or emulating)
1821 : can_convert_eh from the C++ FE, but that's specific to the C++ FE. */
1822 :
1823 : return false;
1824 : }
1825 :
1826 : static bool
1827 287 : matches_any_exception_type_p (eh_catch ehc, tree exception_type)
1828 : {
1829 287 : if (ehc->type_list == NULL_TREE)
1830 : /* All exceptions are caught here. */
1831 : return true;
1832 :
1833 371 : for (tree iter = ehc->type_list; iter; iter = TREE_CHAIN (iter))
1834 287 : if (exception_matches_type_p (TREE_VALUE (iter),
1835 : exception_type))
1836 : return true;
1837 : return false;
1838 : }
1839 :
1840 : // class eh_dispatch_edge_op : public control_flow_op
1841 :
1842 : std::unique_ptr<eh_dispatch_edge_op>
1843 191 : eh_dispatch_edge_op::make (supernode *src_snode,
1844 : supernode *dst_snode,
1845 : ::edge cfg_edge,
1846 : const geh_dispatch &eh_dispatch_stmt)
1847 : {
1848 191 : const eh_status *eh = src_snode->get_function ()->eh;
1849 191 : gcc_assert (eh);
1850 191 : int region_idx = gimple_eh_dispatch_region (&eh_dispatch_stmt);
1851 191 : gcc_assert (region_idx > 0);
1852 191 : gcc_assert ((*eh->region_array)[region_idx]);
1853 191 : eh_region eh_reg = (*eh->region_array)[region_idx];
1854 191 : gcc_assert (eh_reg);
1855 191 : switch (eh_reg->type)
1856 : {
1857 0 : default:
1858 0 : gcc_unreachable ();
1859 0 : case ERT_CLEANUP:
1860 : // TODO
1861 0 : gcc_unreachable ();
1862 179 : break;
1863 179 : case ERT_TRY:
1864 179 : {
1865 179 : eh_catch ehc = get_catch (eh_reg, dst_snode);
1866 179 : return std::make_unique<eh_dispatch_try_edge_op>
1867 179 : (src_snode,
1868 : cfg_edge, eh_dispatch_stmt,
1869 179 : eh_reg, ehc);
1870 : }
1871 12 : break;
1872 12 : case ERT_ALLOWED_EXCEPTIONS:
1873 12 : return std::make_unique<eh_dispatch_allowed_edge_op>
1874 12 : (src_snode, dst_snode,
1875 : cfg_edge, eh_dispatch_stmt,
1876 12 : eh_reg);
1877 0 : break;
1878 0 : case ERT_MUST_NOT_THROW:
1879 : // TODO
1880 0 : gcc_unreachable ();
1881 : break;
1882 : }
1883 : }
1884 :
1885 191 : eh_dispatch_edge_op::
1886 : eh_dispatch_edge_op (supernode *src_snode,
1887 : enum kind kind_,
1888 : ::edge cfg_edge,
1889 : const geh_dispatch &geh_dispatch_stmt,
1890 191 : eh_region eh_reg)
1891 : : control_flow_op (kind_, cfg_edge, geh_dispatch_stmt),
1892 191 : m_src_snode (src_snode),
1893 191 : m_eh_region (eh_reg)
1894 : {
1895 191 : }
1896 :
1897 : bool
1898 324 : eh_dispatch_edge_op::
1899 : apply_constraints (const superedge *sedge,
1900 : region_model &model,
1901 : region_model_context *ctxt,
1902 : std::unique_ptr<rejected_constraint> *out) const
1903 : {
1904 324 : const exception_node *current_node = model.get_current_thrown_exception ();
1905 :
1906 321 : if (!current_node)
1907 : return false;
1908 :
1909 321 : gcc_assert (current_node);
1910 321 : tree curr_exception_type = current_node->maybe_get_type ();
1911 321 : if (!curr_exception_type)
1912 : /* We don't know the specific type. */
1913 : return true;
1914 :
1915 243 : return apply_eh_constraints (sedge, model, ctxt, curr_exception_type, out);
1916 : }
1917 :
1918 : // class eh_dispatch_try_edge_op : public eh_dispatch_edge_op
1919 :
1920 179 : eh_dispatch_try_edge_op::
1921 : eh_dispatch_try_edge_op (supernode *src_snode,
1922 : ::edge cfg_edge,
1923 : const geh_dispatch &geh_dispatch_stmt,
1924 : eh_region eh_reg,
1925 179 : eh_catch ehc)
1926 : : eh_dispatch_edge_op (src_snode,
1927 : kind::eh_dispatch_try_edge,
1928 : cfg_edge, geh_dispatch_stmt, eh_reg),
1929 179 : m_eh_catch (ehc)
1930 : {
1931 179 : gcc_assert (eh_reg->type == ERT_TRY);
1932 179 : }
1933 :
1934 : void
1935 0 : eh_dispatch_try_edge_op::print_as_edge_label (pretty_printer *pp,
1936 : bool user_facing) const
1937 : {
1938 0 : if (!user_facing)
1939 0 : pp_string (pp, "ERT_TRY: ");
1940 0 : if (m_eh_catch)
1941 : {
1942 0 : bool first = true;
1943 0 : for (tree iter = m_eh_catch->type_list; iter; iter = TREE_CHAIN (iter))
1944 : {
1945 0 : if (!first)
1946 0 : pp_string (pp, ", ");
1947 0 : pp_printf (pp, "on catch %qT", TREE_VALUE (iter));
1948 0 : first = false;
1949 : }
1950 : }
1951 : else
1952 0 : pp_string (pp, "on uncaught exception");
1953 0 : }
1954 :
1955 : void
1956 66 : eh_dispatch_try_edge_op::add_any_events_for_eedge (const exploded_edge &eedge,
1957 : checker_path &out_path) const
1958 : {
1959 66 : if (m_eh_catch)
1960 : {
1961 63 : const region_model *model = eedge.m_src->get_state ().m_region_model;
1962 63 : auto curr_thrown_exception_node
1963 63 : = model->get_current_thrown_exception ();
1964 0 : gcc_assert (curr_thrown_exception_node);
1965 63 : tree type = curr_thrown_exception_node->maybe_get_type ();
1966 63 : out_path.add_event
1967 63 : (std::make_unique<catch_cfg_edge_event>
1968 63 : (eedge,
1969 126 : event_loc_info (eedge.m_dest),
1970 : *this,
1971 : type));
1972 : }
1973 : else
1974 : {
1975 : /* We have the "uncaught exception" sedge, from eh_dispatch
1976 : to a block containing resx.
1977 : Don't add any events for this, so that we can consolidate
1978 : adjacent stack unwinding events. */
1979 : }
1980 66 : }
1981 :
1982 : bool
1983 230 : eh_dispatch_try_edge_op::
1984 : apply_eh_constraints (const superedge *sedge,
1985 : region_model &model,
1986 : region_model_context */*ctxt*/,
1987 : tree exception_type,
1988 : std::unique_ptr<rejected_constraint> *out) const
1989 : {
1990 : /* TODO: can we rely on this ordering?
1991 : or do we need to iterate through prev_catch ? */
1992 : /* The exception must not match any of the previous edges. */
1993 768 : for (auto sibling_sedge : get_src_snode ()->m_succs)
1994 : {
1995 308 : if (sibling_sedge == sedge)
1996 : break;
1997 :
1998 139 : const eh_dispatch_try_edge_op *sibling_edge_op
1999 139 : = (const eh_dispatch_try_edge_op *)sibling_sedge->get_op ();
2000 139 : if (eh_catch ehc = sibling_edge_op->m_eh_catch)
2001 139 : if (matches_any_exception_type_p (ehc, exception_type))
2002 : {
2003 : /* The earlier sibling matches, so the "unhandled" edge is
2004 : not taken. */
2005 61 : if (out)
2006 0 : *out = std::make_unique<rejected_eh_dispatch> (model);
2007 61 : return false;
2008 : }
2009 : }
2010 :
2011 169 : if (eh_catch ehc = m_eh_catch)
2012 : {
2013 : /* We have an edge that tried to match one or more types. */
2014 :
2015 : /* The exception must not match any of the previous edges. */
2016 :
2017 : /* It must match this type. */
2018 148 : if (matches_any_exception_type_p (ehc, exception_type))
2019 : return true;
2020 : else
2021 : {
2022 : /* Exception type doesn't match. */
2023 33 : if (out)
2024 0 : *out = std::make_unique<rejected_eh_dispatch> (model);
2025 33 : return false;
2026 : }
2027 : }
2028 : else
2029 : {
2030 : /* This is the "unhandled exception" edge.
2031 : If we get here then no sibling edges matched;
2032 : we will follow this edge. */
2033 : return true;
2034 : }
2035 : }
2036 :
2037 : // class eh_dispatch_allowed_edge_op : public eh_dispatch_edge_op
2038 :
2039 12 : eh_dispatch_allowed_edge_op::
2040 : eh_dispatch_allowed_edge_op (supernode *src_snode,
2041 : supernode *dst_snode,
2042 : ::edge cfg_edge,
2043 : const geh_dispatch &geh_dispatch_stmt,
2044 12 : eh_region eh_reg)
2045 : : eh_dispatch_edge_op (src_snode,
2046 : kind::eh_dispatch_try_edge,
2047 12 : cfg_edge, geh_dispatch_stmt, eh_reg)
2048 : {
2049 12 : gcc_assert (eh_reg->type == ERT_ALLOWED_EXCEPTIONS);
2050 :
2051 : /* We expect two sibling out-edges at an eh_dispatch from such a region:
2052 :
2053 : - one to a bb without a gimple label, with a resx,
2054 : for exceptions of expected types
2055 :
2056 : - one to a bb with a gimple label, with a call to __cxa_unexpected,
2057 : for exceptions of unexpected types.
2058 :
2059 : Set m_kind for this edge accordingly. */
2060 12 : gcc_assert (cfg_edge->src->succs->length () == 2);
2061 12 : tree label_for_unexpected_exceptions = eh_reg->u.allowed.label;
2062 12 : tree label_for_dest_enode = dst_snode->get_label ();
2063 12 : if (label_for_dest_enode == label_for_unexpected_exceptions)
2064 6 : m_kind = eh_kind::unexpected;
2065 : else
2066 : {
2067 6 : gcc_assert (label_for_dest_enode == nullptr);
2068 6 : m_kind = eh_kind::expected;
2069 : }
2070 12 : }
2071 :
2072 : void
2073 3 : eh_dispatch_allowed_edge_op::print_as_edge_label (pretty_printer *pp,
2074 : bool user_facing) const
2075 : {
2076 3 : if (!user_facing)
2077 : {
2078 0 : switch (m_kind)
2079 : {
2080 0 : default:
2081 0 : gcc_unreachable ();
2082 0 : case eh_kind::expected:
2083 0 : pp_string (pp, "expected: ");
2084 0 : break;
2085 0 : case eh_kind::unexpected:
2086 0 : pp_string (pp, "unexpected: ");
2087 0 : break;
2088 : }
2089 0 : pp_string (pp, "ERT_ALLOWED_EXCEPTIONS: ");
2090 0 : eh_region eh_reg = get_eh_region ();
2091 0 : bool first = true;
2092 0 : for (tree iter = eh_reg->u.allowed.type_list; iter;
2093 0 : iter = TREE_CHAIN (iter))
2094 : {
2095 0 : if (!first)
2096 0 : pp_string (pp, ", ");
2097 0 : pp_printf (pp, "%qT", TREE_VALUE (iter));
2098 0 : first = false;
2099 : }
2100 : }
2101 3 : }
2102 :
2103 : bool
2104 13 : eh_dispatch_allowed_edge_op::
2105 : apply_eh_constraints (const superedge *,
2106 : region_model &model,
2107 : region_model_context */*ctxt*/,
2108 : tree exception_type,
2109 : std::unique_ptr<rejected_constraint> *out) const
2110 : {
2111 13 : auto curr_thrown_exception_node = model.get_current_thrown_exception ();
2112 0 : gcc_assert (curr_thrown_exception_node);
2113 13 : tree curr_exception_type = curr_thrown_exception_node->maybe_get_type ();
2114 13 : eh_region eh_reg = get_eh_region ();
2115 13 : tree type_list = eh_reg->u.allowed.type_list;
2116 :
2117 13 : switch (get_eh_kind ())
2118 : {
2119 0 : default:
2120 0 : gcc_unreachable ();
2121 5 : case eh_kind::expected:
2122 5 : if (!curr_exception_type)
2123 : {
2124 : /* We don't know the specific type;
2125 : assume we have one of an expected type. */
2126 : return true;
2127 : }
2128 8 : for (tree iter = type_list; iter; iter = TREE_CHAIN (iter))
2129 11 : if (exception_matches_type_p (TREE_VALUE (iter),
2130 : exception_type))
2131 : return true;
2132 3 : if (out)
2133 0 : *out = std::make_unique<rejected_eh_dispatch> (model);
2134 : return false;
2135 :
2136 8 : case eh_kind::unexpected:
2137 8 : if (!curr_exception_type)
2138 : {
2139 : /* We don't know the specific type;
2140 : assume we don't have one of an expected type. */
2141 0 : if (out)
2142 0 : *out = std::make_unique<rejected_eh_dispatch> (model);
2143 0 : return false;
2144 : }
2145 14 : for (tree iter = type_list; iter; iter = TREE_CHAIN (iter))
2146 8 : if (exception_matches_type_p (TREE_VALUE (iter),
2147 : exception_type))
2148 : {
2149 2 : if (out)
2150 0 : *out = std::make_unique<rejected_eh_dispatch> (model);
2151 2 : return false;
2152 : }
2153 : return true;
2154 : }
2155 : }
2156 :
2157 : // class phis_for_edge_op : public operation
2158 :
2159 : std::unique_ptr<operation>
2160 15572 : phis_for_edge_op::maybe_make (::edge cfg_in_edge)
2161 : {
2162 15572 : std::vector<pair> pairs = get_pairs_for_phi_along_in_edge (cfg_in_edge);
2163 15572 : if (pairs.empty ())
2164 6360 : return nullptr;
2165 :
2166 9212 : return std::make_unique <phis_for_edge_op> (std::move (pairs));
2167 15572 : }
2168 :
2169 9212 : phis_for_edge_op::phis_for_edge_op (std::vector<pair> &&pairs)
2170 : : operation (kind::phis),
2171 9212 : m_pairs (std::move (pairs))
2172 : {
2173 9212 : }
2174 :
2175 : std::vector<phis_for_edge_op::pair>
2176 15572 : phis_for_edge_op::get_pairs_for_phi_along_in_edge (::edge cfg_in_edge)
2177 : {
2178 15572 : std::vector<pair> result;
2179 :
2180 15572 : const size_t phi_arg_idx = cfg_in_edge->dest_idx;
2181 15572 : for (gphi_iterator gpi = gsi_start_phis (cfg_in_edge->dest);
2182 37935 : !gsi_end_p (gpi); gsi_next (&gpi))
2183 : {
2184 22363 : gphi * const phi = gpi.phi ();
2185 22363 : tree dst = gimple_phi_result (phi);
2186 :
2187 : /* We don't bother tracking the .MEM SSA names. */
2188 22363 : if (tree var = SSA_NAME_VAR (dst))
2189 17528 : if (TREE_CODE (var) == VAR_DECL)
2190 16935 : if (VAR_DECL_IS_VIRTUAL_OPERAND (var))
2191 11206 : continue;
2192 :
2193 11157 : tree src = gimple_phi_arg_def (phi, phi_arg_idx);
2194 :
2195 11157 : result.push_back ({dst, src});
2196 : }
2197 :
2198 15572 : return result;
2199 : }
2200 :
2201 : void
2202 87 : phis_for_edge_op::print_as_edge_label (pretty_printer *pp,
2203 : bool ) const
2204 : {
2205 87 : pp_printf (pp, "PHI(");
2206 87 : bool first = true;
2207 174 : for (auto &p : m_pairs)
2208 : {
2209 87 : if (first)
2210 : first = false;
2211 : else
2212 0 : pp_string (pp, ", ");
2213 :
2214 87 : pp_printf (pp, "%E = %E", p.m_dst, p.m_src);
2215 : }
2216 87 : pp_printf (pp, ");");
2217 87 : }
2218 :
2219 : void
2220 9212 : phis_for_edge_op::
2221 : walk_load_store_addr_ops (void */*data*/ ,
2222 : walk_stmt_load_store_addr_fn /*load_cb*/,
2223 : walk_stmt_load_store_addr_fn /*store_cb*/,
2224 : walk_stmt_load_store_addr_fn /*addr_cb*/) const
2225 : {
2226 9212 : }
2227 :
2228 : bool
2229 20655 : phis_for_edge_op::defines_ssa_name_p (const_tree ssa_name) const
2230 : {
2231 37876 : for (auto &p : m_pairs)
2232 28417 : if (p.m_dst == ssa_name)
2233 20655 : return true;
2234 : return false;
2235 : }
2236 :
2237 : void
2238 21358 : phis_for_edge_op::execute (operation_context &op_ctxt) const
2239 : {
2240 21358 : auto logger = op_ctxt.get_logger ();
2241 21358 : LOG_SCOPE (logger);
2242 :
2243 21358 : auto dst_point (op_ctxt.get_next_intraprocedural_point ());
2244 :
2245 21358 : const program_state &src_state (op_ctxt.get_initial_state ());
2246 21358 : program_state dst_state (src_state);
2247 :
2248 21358 : impl_path_context path_ctxt (&dst_state, logger);
2249 21358 : uncertainty_t uncertainty;
2250 21358 : impl_region_model_context ctxt (op_ctxt.m_eg,
2251 21358 : &op_ctxt.m_src_enode,
2252 :
2253 : /* TODO: should we be getting the ECs from the
2254 : old state, rather than the new? */
2255 21358 : &op_ctxt.get_initial_state (),
2256 : &dst_state,
2257 : &uncertainty,
2258 : &path_ctxt,
2259 : nullptr,
2260 21358 : nullptr);
2261 :
2262 21358 : update_state (src_state, dst_state, &ctxt);
2263 :
2264 21358 : op_ctxt.add_outcome (dst_point, dst_state, false, &uncertainty);
2265 42716 : }
2266 :
2267 : void
2268 21772 : phis_for_edge_op::update_state (const program_state &src_state,
2269 : program_state &dst_state,
2270 : region_model_context *ctxt) const
2271 : {
2272 21772 : const region_model &src_model = *src_state.m_region_model;
2273 21772 : region_model &dst_model = *dst_state.m_region_model;
2274 :
2275 21772 : hash_set<const svalue *> svals_changing_meaning;
2276 :
2277 : /* Get state from src_state so that all of the phi stmts for an edge
2278 : are effectively handled simultaneously. */
2279 51999 : for (auto &p : m_pairs)
2280 : {
2281 30227 : const svalue *src_sval = src_model.get_rvalue (p.m_src, nullptr);
2282 30227 : const region *dst_reg = src_model.get_lvalue (p.m_dst, nullptr);
2283 :
2284 30227 : const svalue *old_sval = src_model.get_rvalue (p.m_dst, nullptr);
2285 30227 : if (old_sval->get_kind () == SK_WIDENING)
2286 12 : svals_changing_meaning.add (old_sval);
2287 :
2288 30227 : dst_model.set_value (dst_reg, src_sval, ctxt);
2289 : }
2290 :
2291 43556 : for (auto iter : svals_changing_meaning)
2292 12 : dst_model.get_constraints ()->purge_state_involving (iter);
2293 21772 : }
2294 :
2295 : bool
2296 10936 : phis_for_edge_op::
2297 : execute_for_feasibility (const exploded_edge &eedge,
2298 : feasibility_state &fstate,
2299 : region_model_context *ctxt,
2300 : std::unique_ptr<rejected_constraint> */*out_rc*/) const
2301 : {
2302 10936 : hash_set<const svalue *> svals_changing_meaning;
2303 : /* Get state from src_state so that all of the phi stmts for an edge
2304 : are effectively handled simultaneously. */
2305 10936 : region_model &model = fstate.get_model ();
2306 10936 : region_model src_model (model);
2307 24152 : for (auto &p : m_pairs)
2308 : {
2309 13216 : const svalue *src_sval = src_model.get_rvalue (p.m_src, ctxt);
2310 13216 : const region *dst_reg = model.get_lvalue (p.m_dst, ctxt);
2311 :
2312 13216 : const svalue *sval = model.get_rvalue (p.m_dst, ctxt);
2313 13216 : if (sval->get_kind () == SK_WIDENING)
2314 24 : svals_changing_meaning.add (sval);
2315 :
2316 13216 : model.set_value (dst_reg, src_sval, ctxt);
2317 : }
2318 :
2319 10960 : for (auto iter : svals_changing_meaning)
2320 24 : model.get_constraints ()->purge_state_involving (iter);
2321 :
2322 10936 : {
2323 : /* If we've entering an snode that we've already visited on this
2324 : epath, then we need do fix things up for loops; see the
2325 : comment for store::loop_replay_fixup.
2326 : Perhaps we should probably also verify the callstring,
2327 : and track program_points, but hopefully doing it by supernode
2328 : is good enough. */
2329 10936 : const exploded_node &dst_enode = *eedge.m_dest;
2330 10936 : const unsigned dst_snode_idx = dst_enode.get_supernode ()->m_id;
2331 10936 : if (bitmap_bit_p (fstate.get_snodes_visited (), dst_snode_idx))
2332 4661 : model.loop_replay_fixup (dst_enode.get_state ().m_region_model);
2333 : }
2334 :
2335 21872 : return true;
2336 10936 : }
2337 :
2338 : void
2339 414 : phis_for_edge_op::
2340 : update_state_for_bulk_merger (const program_state &src_state,
2341 : program_state &dst_state) const
2342 : {
2343 414 : update_state (src_state, dst_state, nullptr);
2344 414 : }
2345 :
2346 : void
2347 1259 : phis_for_edge_op::add_any_events_for_eedge (const exploded_edge &,
2348 : checker_path &) const
2349 : {
2350 : // No-op
2351 1259 : }
2352 :
2353 : // class resx_op : public gimple_stmt_op
2354 :
2355 : void
2356 536 : resx_op::execute (operation_context &op_ctxt) const
2357 : {
2358 536 : auto logger = op_ctxt.get_logger ();
2359 536 : LOG_SCOPE (logger);
2360 :
2361 536 : program_point dst_point (op_ctxt.get_next_intraprocedural_point ());
2362 536 : program_state dst_state (op_ctxt.get_initial_state ());
2363 536 : op_region_model_context ctxt (op_ctxt, dst_state);
2364 :
2365 1072 : if (exploded_node *dst_enode
2366 536 : = op_ctxt.m_eg.get_or_create_node (dst_point, dst_state,
2367 536 : &op_ctxt.m_src_enode,
2368 : // Don't add to worklist:
2369 : false))
2370 : {
2371 536 : op_ctxt.m_eg.add_edge (&op_ctxt.m_src_enode,
2372 : dst_enode,
2373 536 : &op_ctxt.m_sedge,
2374 : false,
2375 536 : nullptr);
2376 : /* Try to adding eedges and enodes that unwind to the next
2377 : eh_dispatch statement, if any.
2378 : Only the final enode is added to the worklist. */
2379 536 : op_ctxt.m_eg.unwind_from_exception (*dst_enode,
2380 : nullptr,
2381 : &ctxt);
2382 : }
2383 536 : }
2384 :
2385 : void
2386 18 : resx_op::add_any_events_for_eedge (const exploded_edge &,
2387 : checker_path &) const
2388 : {
2389 18 : }
2390 :
2391 : } // namespace ana
2392 :
2393 : #endif /* #if ENABLE_ANALYZER */
|