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 20468 : event_loc_info::event_loc_info (const exploded_node *enode)
47 : {
48 20468 : if (enode)
49 : {
50 20468 : m_loc = enode->get_location ();
51 20468 : m_fndecl = enode->get_point ().get_fndecl ();
52 40891 : 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 20468 : }
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 454760 : operation_context::get_logger () const
85 : {
86 454760 : return m_eg.get_logger ();
87 : }
88 :
89 : const extrinsic_state &
90 297745 : operation_context::get_ext_state () const
91 : {
92 297745 : return m_eg.get_ext_state ();
93 : }
94 :
95 : const program_point &
96 24699 : operation_context::get_initial_point () const
97 : {
98 24699 : return m_src_enode.get_point ();
99 : }
100 :
101 : const program_state &
102 1207096 : operation_context::get_initial_state () const
103 : {
104 1207096 : 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 346599 : operation_context::get_next_intraprocedural_point () const
115 : {
116 : /* All edges are intraprocedural. */
117 346599 : gcc_assert (m_sedge.m_src->get_function ()
118 : == m_sedge.m_dest->get_function ());
119 346599 : return program_point (m_sedge.m_dest,
120 346599 : m_src_enode.get_point ().get_call_string ());
121 : }
122 :
123 : void
124 295847 : 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 295847 : const program_state &src_state = get_initial_state ();
131 295847 : impl_region_model_context ctxt (m_eg, &m_src_enode,
132 : &src_state, &dst_state,
133 295847 : uncertainty, nullptr);
134 295847 : program_state::detect_leaks (src_state, dst_state, nullptr,
135 : get_ext_state (), &ctxt);
136 :
137 591694 : if (exploded_node *dst_enode
138 295847 : = m_eg.get_or_create_node (dst_point, dst_state, &m_src_enode))
139 : {
140 294796 : m_eg.add_edge (&m_src_enode, dst_enode, &m_sedge, could_do_work,
141 : std::move (info));
142 294796 : m_eg.detect_infinite_recursion (dst_enode);
143 : }
144 295847 : }
145 :
146 220482 : class op_region_model_context : public impl_region_model_context
147 : {
148 : public:
149 110241 : op_region_model_context (operation_context &op_ctxt,
150 : program_state &dst_state)
151 110241 : : impl_region_model_context (op_ctxt.m_eg,
152 110241 : &op_ctxt.m_src_enode,
153 110241 : &op_ctxt.get_initial_state (),
154 : &dst_state,
155 : nullptr,
156 110241 : &m_path_context)
157 : {
158 110241 : }
159 :
160 44995 : bool terminate_path_p () const
161 : {
162 44995 : return m_path_context.terminate_path_p ();
163 : }
164 :
165 : private:
166 59921 : class op_path_context : public path_context
167 : {
168 : public:
169 110241 : op_path_context ()
170 110241 : : 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 44995 : bool terminate_path_p () const final override
185 : {
186 44995 : 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 181649 : gimple_stmt_op::defines_ssa_name_p (const_tree ssa_name) const
204 : {
205 181649 : return &m_stmt == SSA_NAME_DEF_STMT (ssa_name);
206 : }
207 :
208 : bool
209 35247 : gimple_stmt_op::supports_bulk_merge_p () const
210 : {
211 35247 : 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 244137 : impl_path_context (const program_state *cur_state,
221 : logger *logger)
222 244137 : : m_cur_state (cur_state),
223 244137 : m_logger (logger),
224 244137 : 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 427739 : bool terminate_path_p () const final override
267 : {
268 427739 : 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 250628 : 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 250628 : const program_state &old_state = op_ctxt.get_initial_state ();
305 250628 : int sm_idx;
306 250628 : sm_state_map *smap;
307 2004086 : FOR_EACH_VEC_ELT (old_state.m_checker_states, sm_idx, smap)
308 : {
309 1753458 : const state_machine &sm = op_ctxt.m_eg.get_ext_state ().get_sm (sm_idx);
310 1753458 : const sm_state_map *old_smap
311 1753458 : = old_state.m_checker_states[sm_idx];
312 1753458 : sm_state_map *new_smap = dst_state.m_checker_states[sm_idx];
313 1753458 : impl_sm_context sm_ctxt (op_ctxt.m_eg, sm_idx, sm,
314 1753458 : &op_ctxt.m_src_enode,
315 : &old_state,
316 : &dst_state,
317 : old_smap, new_smap, path_ctxt,
318 1753458 : unknown_side_effects);
319 :
320 : /* Allow the state_machine to handle the stmt. */
321 1753458 : if (sm.on_stmt (sm_ctxt, &stmt))
322 22105 : unknown_side_effects = false;
323 1753458 : }
324 250628 : }
325 :
326 : void
327 108776 : 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 108776 : walk_stmt_load_store_addr_ops (const_cast<gimple *>(&m_stmt), data,
334 : load_cb, store_cb, addr_cb);
335 108776 : }
336 :
337 : void
338 155957 : gimple_stmt_op::execute (operation_context &op_ctxt) const
339 : {
340 155957 : auto logger = op_ctxt.get_logger ();
341 155957 : LOG_SCOPE (logger);
342 155957 : 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 155957 : execute_on_state (op_ctxt,
350 : /* Pass in a copy. */
351 : op_ctxt.get_initial_state ());
352 155957 : }
353 :
354 : void
355 205633 : gimple_stmt_op::execute_on_state (operation_context &op_ctxt,
356 : program_state dst_state) const
357 : {
358 205633 : auto logger = op_ctxt.get_logger ();
359 205633 : LOG_SCOPE (logger);
360 :
361 205633 : auto dst_point (op_ctxt.get_next_intraprocedural_point ());
362 205633 : const program_state &old_state = op_ctxt.get_initial_state ();
363 :
364 205633 : bool unknown_side_effects = false;
365 205633 : bool could_have_done_work = false;
366 :
367 205633 : impl_path_context path_ctxt (&dst_state, logger);
368 205633 : uncertainty_t uncertainty;
369 205633 : impl_region_model_context ctxt (op_ctxt.m_eg,
370 205633 : &op_ctxt.m_src_enode,
371 : &old_state,
372 : &dst_state,
373 : &uncertainty,
374 : &path_ctxt,
375 205633 : &m_stmt,
376 205633 : &could_have_done_work);
377 :
378 205633 : dst_state.m_region_model->on_stmt_pre (&get_stmt (),
379 : &unknown_side_effects,
380 : &ctxt);
381 :
382 205633 : handle_on_stmt_for_state_machines (op_ctxt,
383 : dst_state,
384 : &path_ctxt,
385 : unknown_side_effects,
386 : m_stmt);
387 :
388 205633 : if (path_ctxt.terminate_path_p ())
389 678 : return;
390 :
391 204955 : if (const gcall *call = dyn_cast <const gcall *> (&m_stmt))
392 49431 : dst_state.m_region_model->on_call_post (*call, unknown_side_effects, &ctxt);
393 :
394 204955 : if (!path_ctxt.terminate_path_p ())
395 203787 : 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 226346 : 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 411266 : }
445 :
446 : bool
447 106127 : 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 106127 : region_model &model = fstate.get_model ();
454 106127 : bool unknown_side_effects;
455 106127 : model.on_stmt_pre (&m_stmt, &unknown_side_effects, ctxt);
456 :
457 106127 : if (const gcall *call = dyn_cast <const gcall *> (&m_stmt))
458 21053 : model.on_call_post (*call, unknown_side_effects, ctxt);
459 :
460 106127 : 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 94047 : struct null_assignment_sm_context : public sm_context
471 : {
472 94047 : 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 94047 : : sm_context (sm_idx, sm), m_old_state (old_state), m_new_state (new_state),
481 94047 : m_stmt (stmt), m_point (point), m_emission_path (emission_path),
482 94047 : 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 13426 : state_machine::state_t get_global_state () const final override
573 : {
574 13426 : 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 13473 : tree is_zero_assignment (const gimple *stmt) final override
592 : {
593 25675 : const gassign *assign_stmt = dyn_cast <const gassign *> (stmt);
594 13473 : if (!assign_stmt)
595 : return NULL_TREE;
596 26946 : if (const svalue *sval
597 13473 : = m_new_state->m_region_model->get_gassign_result (assign_stmt, nullptr))
598 13063 : if (tree cst = sval->maybe_get_constant ())
599 2400 : if (::zerop(cst))
600 1271 : 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 13613 : gimple_stmt_op::add_any_events_for_eedge (const exploded_edge &eedge,
635 : checker_path &out_path) const
636 : {
637 13613 : out_path.add_event
638 13613 : (std::make_unique<statement_event> (&get_stmt (),
639 13613 : eedge.m_dest->get_function ()->decl,
640 27226 : eedge.m_dest->get_stack_depth (),
641 13613 : 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 13613 : if (const gassign *assign = dyn_cast<const gassign *> (&m_stmt))
647 : {
648 13491 : const program_point &src_point = eedge.m_src->get_point ();
649 13491 : const extrinsic_state &ext_state = out_path.get_ext_state ();
650 107538 : for (unsigned i = 0; i < ext_state.get_num_checkers (); i++)
651 : {
652 94047 : const state_machine &sm = ext_state.get_sm (i);
653 94047 : null_assignment_sm_context sm_ctxt (i, sm,
654 94047 : &eedge.m_src->get_state (),
655 94047 : &eedge.m_dest->get_state (),
656 : assign,
657 : &src_point,
658 : &out_path,
659 94047 : ext_state);
660 94047 : sm.on_stmt (sm_ctxt, assign);
661 : // TODO: what about phi nodes?
662 94047 : }
663 : }
664 13613 : }
665 :
666 : // class gassign_op : public gimple_stmt_op
667 :
668 : // class greturn_op : public gimple_stmt_op
669 :
670 : void
671 17151 : greturn_op::execute (operation_context &op_ctxt) const
672 : {
673 17151 : auto logger = op_ctxt.get_logger ();
674 :
675 17151 : auto dst_point (op_ctxt.get_next_intraprocedural_point ());
676 17151 : const program_state &old_state = op_ctxt.get_initial_state ();
677 17151 : program_state dst_state (old_state);
678 :
679 17151 : impl_path_context path_ctxt (&dst_state, logger);
680 17151 : uncertainty_t uncertainty;
681 17151 : impl_region_model_context ctxt (op_ctxt.m_eg,
682 17151 : &op_ctxt.m_src_enode,
683 :
684 : /* TODO: should we be getting the ECs from the
685 : old state, rather than the new? */
686 17151 : &op_ctxt.get_initial_state (),
687 : &dst_state,
688 : &uncertainty,
689 : &path_ctxt,
690 : nullptr,
691 17151 : nullptr);
692 :
693 17151 : tree callee = op_ctxt.get_initial_point ().get_function ()->decl;
694 17151 : tree lhs = DECL_RESULT (callee);
695 :
696 34302 : if (lhs && get_retval ())
697 : {
698 8682 : region_model *dst_region_model = dst_state.m_region_model;
699 8682 : const svalue *sval
700 8682 : = dst_region_model->get_rvalue (get_retval (), &ctxt);
701 8682 : const region *ret_reg = dst_region_model->get_lvalue (lhs, &ctxt);
702 8682 : dst_region_model->set_value (ret_reg, sval, &ctxt);
703 : }
704 :
705 17151 : if (!path_ctxt.terminate_path_p ())
706 17136 : op_ctxt.add_outcome (dst_point, dst_state, false, &uncertainty);
707 34302 : }
708 :
709 : bool
710 3636 : 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 3636 : tree callee = eedge.m_src->get_function ()->decl;
717 3636 : tree lhs = DECL_RESULT (callee);
718 :
719 7272 : 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 3636 : return true;
728 : }
729 :
730 : void
731 969 : greturn_op::add_any_events_for_eedge (const exploded_edge &,
732 : checker_path &) const
733 : {
734 : // No-op.
735 969 : }
736 :
737 : // class call_and_return_op : public gimple_stmt_op
738 :
739 : std::unique_ptr<operation>
740 41878 : call_and_return_op::make (const gcall &call_stmt)
741 : {
742 41878 : 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 41878 : 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 41878 : 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 41878 : 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 41569 : else if (is_setjmp_call_p (call_stmt))
751 29 : return std::make_unique<setjmp_op> (call_stmt);
752 41540 : else if (is_longjmp_call_p (call_stmt))
753 41 : return std::make_unique<longjmp_op> (call_stmt);
754 41499 : else if (is_cxa_throw_p (call_stmt))
755 73 : return std::make_unique<cxa_throw_op> (call_stmt, false);
756 41426 : else if (is_cxa_rethrow_p (call_stmt))
757 39 : return std::make_unique<cxa_throw_op> (call_stmt, true);
758 :
759 41387 : 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 57224 : 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 57224 : const program_state &old_state = op_ctxt.get_initial_state ();
779 57224 : program_state dst_state (old_state);
780 57224 : op_region_model_context ctxt (op_ctxt, dst_state);
781 57224 : ctxt.m_stmt = &get_gcall ();
782 57224 : 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 57224 : {
787 : /* Check for any preconditions if it's a known_function. */
788 57224 : if (auto kf = maybe_get_known_function (cd))
789 33414 : 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 457429 : FOR_EACH_VEC_ELT (old_state.m_checker_states, sm_idx, smap)
796 : {
797 400205 : const state_machine &sm
798 800410 : = op_ctxt.m_eg.get_ext_state ().get_sm (sm_idx);
799 400205 : const sm_state_map *old_smap
800 400205 : = old_state.m_checker_states[sm_idx];
801 400205 : sm_state_map *new_smap = dst_state.m_checker_states[sm_idx];
802 400205 : impl_sm_context sm_ctxt (op_ctxt.m_eg, sm_idx, sm,
803 400205 : &op_ctxt.m_src_enode,
804 : &old_state, &dst_state,
805 400205 : old_smap, new_smap, nullptr);
806 400205 : sm.check_call_preconditions (sm_ctxt, cd);
807 400205 : }
808 : }
809 : }
810 :
811 57224 : if (tree callee_fndecl = cd.get_fndecl_for_call ())
812 : {
813 : // Consider using a call summary
814 53424 : if (function *called_fn = DECL_STRUCT_FUNCTION (callee_fndecl))
815 7548 : if (cgraph_edge *edge = get_any_cgraph_edge (op_ctxt))
816 7548 : if (op_ctxt.m_eg.get_analysis_plan ().use_summary_p (edge))
817 : {
818 790 : per_function_data *called_fn_data
819 790 : = op_ctxt.m_eg.get_per_function_data (called_fn);
820 790 : if (called_fn_data)
821 : {
822 752 : replay_call_summaries (op_ctxt, *called_fn,
823 : *called_fn_data, &ctxt);
824 752 : return;
825 : }
826 : }
827 :
828 : // Do we have an entry snode for this fndecl?
829 52672 : 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 49676 : gimple_stmt_op::execute_on_state (op_ctxt, std::move (dst_state));
854 57224 : }
855 :
856 : cgraph_edge *
857 7548 : call_and_return_op::get_any_cgraph_edge (operation_context &op_ctxt) const
858 : {
859 7548 : tree caller_fndecl = op_ctxt.get_initial_point ().get_fndecl ();
860 7548 : gcc_assert (caller_fndecl);
861 :
862 7548 : auto caller_cgnode = cgraph_node::get (caller_fndecl);
863 7548 : gcc_assert (caller_cgnode);
864 7548 : 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 57224 : call_and_return_op::maybe_get_known_function (const call_details &cd) const
995 : {
996 57224 : region_model_manager *mgr = cd.get_manager ();
997 57224 : known_function_manager *known_fn_mgr = mgr->get_known_function_manager ();
998 :
999 57224 : 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 53794 : if (tree callee_fndecl = cd.get_fndecl_for_call ())
1004 53424 : return known_fn_mgr->get_match (callee_fndecl, cd);
1005 :
1006 : return nullptr;
1007 : }
1008 :
1009 : void
1010 752 : 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 752 : logger *logger = op_ctxt.get_logger ();
1017 752 : LOG_SCOPE (logger);
1018 :
1019 3797 : for (auto summary : called_fn_data.m_summaries)
1020 : {
1021 1541 : gcc_assert (summary);
1022 1541 : replay_call_summary (op_ctxt, called_fn, *summary, ctxt);
1023 : }
1024 752 : }
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 1417 : call_summary_edge_info (const call_details &cd,
1032 : const function &called_fn,
1033 : call_summary &summary,
1034 : const extrinsic_state &ext_state)
1035 1417 : : call_info (cd, called_fn),
1036 1417 : m_called_fn (called_fn),
1037 1417 : m_summary (summary),
1038 1417 : 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 1541 : 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 1541 : logger *logger = op_ctxt.get_logger ();
1083 1541 : LOG_SCOPE (logger);
1084 1541 : 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 1541 : const extrinsic_state &ext_state = op_ctxt.get_ext_state ();
1090 1541 : const program_state &old_state = op_ctxt.get_initial_state ();
1091 1541 : const program_state &summary_end_state = summary.get_state ();
1092 1541 : 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 1541 : program_state new_state (old_state);
1108 :
1109 1541 : call_details cd (get_gcall (), new_state.m_region_model, ctxt);
1110 1541 : call_summary_replay r (cd, called_fn, summary, ext_state);
1111 :
1112 1541 : if (new_state.replay_call_summary (r, summary_end_state))
1113 1417 : op_ctxt.add_outcome
1114 1417 : (op_ctxt.get_next_intraprocedural_point (),
1115 : new_state,
1116 : true,
1117 : nullptr,
1118 2834 : std::make_unique<call_summary_edge_info> (cd,
1119 : called_fn,
1120 : summary,
1121 : ext_state));
1122 1541 : }
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 18088 : 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 18088 : walk_stmt_load_store_addr_ops (const_cast <gimple *> (&m_ctrlflow_stmt),
1224 : data,
1225 : load_cb, store_cb, addr_cb);
1226 18088 : }
1227 :
1228 : void
1229 2607 : control_flow_op::add_any_events_for_eedge (const exploded_edge &eedge,
1230 : checker_path &out_path) const
1231 : {
1232 2607 : out_path.add_event
1233 2607 : (std::make_unique<start_cfg_edge_event> (eedge,
1234 5214 : event_loc_info (eedge.m_src),
1235 2607 : this));
1236 2607 : out_path.add_event
1237 2607 : (std::make_unique<end_cfg_edge_event> (eedge,
1238 5214 : event_loc_info (eedge.m_dest),
1239 2607 : this));
1240 2607 : }
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 51837 : control_flow_op::execute (operation_context &op_ctxt) const
1277 : {
1278 51837 : auto logger = op_ctxt.get_logger ();
1279 51837 : LOG_SCOPE (logger);
1280 :
1281 51837 : program_state dst_state (op_ctxt.get_initial_state ());
1282 51837 : op_region_model_context ctxt (op_ctxt, dst_state);
1283 51837 : if (apply_constraints (&op_ctxt.m_sedge,
1284 51837 : *dst_state.m_region_model,
1285 : &ctxt,
1286 : nullptr))
1287 : {
1288 44995 : bool unknown_side_effects;
1289 44995 : handle_on_stmt_for_state_machines (op_ctxt,
1290 : dst_state,
1291 : nullptr,
1292 : unknown_side_effects,
1293 : m_ctrlflow_stmt);
1294 :
1295 44995 : if (!ctxt.terminate_path_p ())
1296 : {
1297 44967 : auto dst_point (op_ctxt.get_next_intraprocedural_point ());
1298 44967 : op_ctxt.add_outcome (dst_point, dst_state, false, nullptr);
1299 : }
1300 : }
1301 51837 : }
1302 :
1303 : bool
1304 29116 : 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 29116 : gcc_assert (eedge.m_sedge);
1311 29116 : return apply_constraints (eedge.m_sedge,
1312 : fstate.get_model (),
1313 : ctxt,
1314 29116 : out_rc);
1315 : }
1316 :
1317 : // class gcond_edge_op : public control_flow_op
1318 :
1319 14898 : gcond_edge_op::gcond_edge_op (::edge cfg_edge,
1320 14898 : const gcond &cond_stmt)
1321 : : control_flow_op (kind::cond_edge, cfg_edge, cond_stmt),
1322 14898 : 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 14898 : gcc_assert (static_cast<bool> (get_flags () & EDGE_TRUE_VALUE)
1327 : ^ static_cast<bool> (get_flags () & EDGE_FALSE_VALUE));
1328 14898 : }
1329 :
1330 : void
1331 6926 : gcond_edge_op::print_as_edge_label (pretty_printer *pp,
1332 : bool user_facing) const
1333 : {
1334 6926 : if (!user_facing)
1335 228 : pp_gimple_stmt_1 (pp, &get_ctrlflow_stmt (), 0, (dump_flags_t)0);
1336 :
1337 6926 : if (m_true_value)
1338 3735 : pp_printf (pp, "true");
1339 : else
1340 3191 : pp_printf (pp, "false");
1341 6926 : }
1342 :
1343 : label_text
1344 6698 : gcond_edge_op::maybe_describe_condition (bool can_colorize) const
1345 : {
1346 6698 : const gcond &cond_stmt = get_gcond ();
1347 6698 : enum tree_code op = gimple_cond_code (&cond_stmt);
1348 6698 : tree lhs = gimple_cond_lhs (&cond_stmt);
1349 6698 : tree rhs = gimple_cond_rhs (&cond_stmt);
1350 6698 : if (!m_true_value)
1351 3087 : op = invert_tree_comparison (op, false /* honor_nans */);
1352 6698 : return maybe_describe_condition (can_colorize,
1353 6698 : 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 6698 : 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 6698 : if (TREE_CODE (lhs) == SSA_NAME
1376 6698 : && zerop (rhs))
1377 : {
1378 5097 : 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 6638 : if (!should_print_expr_p (lhs))
1390 2651 : 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 10625 : gcond_edge_op::should_print_expr_p (tree expr)
1418 : {
1419 14886 : if (TREE_CODE (expr) == SSA_NAME)
1420 : {
1421 6959 : 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 72478 : 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 72478 : const gcond &cond_stmt = get_gcond ();
1444 72478 : enum tree_code op = gimple_cond_code (&cond_stmt);
1445 72478 : tree lhs = gimple_cond_lhs (&cond_stmt);
1446 72478 : tree rhs = gimple_cond_rhs (&cond_stmt);
1447 72478 : if (!m_true_value)
1448 32691 : op = invert_tree_comparison (op, false /* honor_nans */);
1449 72478 : return model.add_constraint (lhs, op, rhs, ctxt, out);
1450 : }
1451 :
1452 : // class ggoto_edge_op : public control_flow_op
1453 :
1454 32 : ggoto_edge_op::ggoto_edge_op (::edge cfg_edge,
1455 : const ggoto &goto_stmt,
1456 32 : tree dst_label)
1457 : : control_flow_op (kind::goto_edge, cfg_edge, goto_stmt),
1458 32 : m_dst_label (dst_label)
1459 : {
1460 32 : }
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 79 : 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 79 : const ggoto &goto_stmt = get_ggoto ();
1487 79 : tree dest = gimple_goto_dest (&goto_stmt);
1488 79 : const svalue *dest_sval = model.get_rvalue (dest, ctxt);
1489 :
1490 : /* If we know we were jumping to a specific label. */
1491 79 : if (m_dst_label)
1492 : {
1493 79 : auto mgr = model.get_manager ();
1494 79 : const label_region *dst_label_reg
1495 79 : = mgr->get_region_for_label (m_dst_label);
1496 79 : const svalue *dst_label_ptr
1497 79 : = mgr->get_ptr_svalue (ptr_type_node, dst_label_reg);
1498 :
1499 79 : if (!model.add_constraint (dest_sval, EQ_EXPR, dst_label_ptr, ctxt))
1500 : return false;
1501 : }
1502 :
1503 : return true;
1504 : }
1505 :
1506 : // class switch_case_op : public control_flow_op
1507 :
1508 2983 : switch_case_op::switch_case_op (function &fun,
1509 : ::edge cfg_edge,
1510 : const gswitch &switch_stmt,
1511 2983 : bounded_ranges_manager &mgr)
1512 2983 : : control_flow_op (kind::switch_edge, cfg_edge, switch_stmt)
1513 : {
1514 : /* Populate m_case_labels with all cases which go to DST. */
1515 542488 : for (unsigned i = 0; i < gimple_switch_num_labels (&switch_stmt); i++)
1516 : {
1517 539505 : tree case_ = gimple_switch_label (&switch_stmt, i);
1518 539505 : basic_block bb = label_to_block (&fun,
1519 539505 : CASE_LABEL (case_));
1520 539505 : if (bb == cfg_edge->dest)
1521 3552 : m_case_labels.push_back (case_);
1522 : }
1523 :
1524 2983 : auto_vec <const bounded_ranges *> case_ranges_vec
1525 2983 : (gimple_switch_num_labels (&switch_stmt));
1526 6535 : for (auto case_label : m_case_labels)
1527 : {
1528 : /* Get the ranges for this case label. */
1529 3552 : const bounded_ranges *case_ranges
1530 3552 : = mgr.make_case_label_ranges (&switch_stmt, case_label);
1531 3552 : case_ranges_vec.quick_push (case_ranges);
1532 : }
1533 :
1534 2983 : m_all_cases_ranges = mgr.get_or_create_union (case_ranges_vec);
1535 2983 : }
1536 :
1537 : /* Print "case VAL:", "case LOWER ... UPPER:", or "default:" to PP. */
1538 :
1539 : void
1540 448 : switch_case_op::print_as_edge_label (pretty_printer *pp,
1541 : bool user_facing) const
1542 : {
1543 448 : if (user_facing)
1544 : {
1545 888 : for (unsigned i = 0; i < m_case_labels.size (); ++i)
1546 : {
1547 447 : if (i > 0)
1548 6 : pp_string (pp, ", ");
1549 447 : tree case_label = m_case_labels[i];
1550 447 : gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
1551 447 : tree lower_bound = CASE_LOW (case_label);
1552 447 : tree upper_bound = CASE_HIGH (case_label);
1553 447 : if (lower_bound)
1554 : {
1555 162 : pp_printf (pp, "case ");
1556 162 : dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, false);
1557 162 : if (upper_bound)
1558 : {
1559 12 : pp_printf (pp, " ... ");
1560 12 : dump_generic_node (pp, upper_bound, 0, (dump_flags_t)0,
1561 : false);
1562 : }
1563 162 : pp_printf (pp, ":");
1564 : }
1565 : else
1566 285 : pp_printf (pp, "default:");
1567 : }
1568 : }
1569 : else
1570 : {
1571 7 : pp_character (pp, '{');
1572 15 : for (unsigned i = 0; i < m_case_labels.size (); ++i)
1573 : {
1574 8 : if (i > 0)
1575 1 : pp_string (pp, ", ");
1576 8 : tree case_label = m_case_labels[i];
1577 8 : gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
1578 8 : tree lower_bound = CASE_LOW (case_label);
1579 8 : tree upper_bound = CASE_HIGH (case_label);
1580 8 : if (lower_bound)
1581 : {
1582 6 : if (upper_bound)
1583 : {
1584 0 : pp_character (pp, '[');
1585 0 : dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0,
1586 : false);
1587 0 : pp_string (pp, ", ");
1588 0 : dump_generic_node (pp, upper_bound, 0, (dump_flags_t)0,
1589 : false);
1590 0 : pp_character (pp, ']');
1591 : }
1592 : else
1593 6 : dump_generic_node (pp, lower_bound, 0, (dump_flags_t)0, false);
1594 : }
1595 : else
1596 2 : pp_printf (pp, "default");
1597 : }
1598 7 : pp_character (pp, '}');
1599 7 : if (implicitly_created_default_p ())
1600 : {
1601 2 : pp_string (pp, " IMPLICITLY CREATED");
1602 : }
1603 : }
1604 448 : }
1605 :
1606 : /* Return true iff SWITCH_STMT has a non-default label that contains
1607 : INT_CST. */
1608 :
1609 : static bool
1610 142 : has_nondefault_case_for_value_p (const gswitch *switch_stmt, tree int_cst)
1611 : {
1612 : /* We expect the initial label to be the default; skip it. */
1613 142 : gcc_assert (CASE_LOW (gimple_switch_label (switch_stmt, 0)) == NULL_TREE);
1614 142 : unsigned min_idx = 1;
1615 142 : unsigned max_idx = gimple_switch_num_labels (switch_stmt) - 1;
1616 :
1617 : /* Binary search: try to find the label containing INT_CST.
1618 : This requires the cases to be sorted by CASE_LOW (done by the
1619 : gimplifier). */
1620 257 : while (max_idx >= min_idx)
1621 : {
1622 247 : unsigned case_idx = (min_idx + max_idx) / 2;
1623 247 : tree label = gimple_switch_label (switch_stmt, case_idx);
1624 247 : tree low = CASE_LOW (label);
1625 247 : gcc_assert (low);
1626 247 : tree high = CASE_HIGH (label);
1627 247 : if (!high)
1628 195 : high = low;
1629 247 : if (tree_int_cst_compare (int_cst, low) < 0)
1630 : {
1631 : /* INT_CST is below the range of this label. */
1632 27 : gcc_assert (case_idx > 0);
1633 27 : max_idx = case_idx - 1;
1634 : }
1635 220 : else if (tree_int_cst_compare (int_cst, high) > 0)
1636 : {
1637 : /* INT_CST is above the range of this case. */
1638 88 : min_idx = case_idx + 1;
1639 : }
1640 : else
1641 : /* This case contains INT_CST. */
1642 : return true;
1643 : }
1644 : /* Not found. */
1645 : return false;
1646 : }
1647 :
1648 : /* Return true iff SWITCH_STMT (which must be on an enum value)
1649 : has nondefault cases handling all values in the enum. */
1650 :
1651 : static bool
1652 45 : has_nondefault_cases_for_all_enum_values_p (const gswitch *switch_stmt,
1653 : tree type)
1654 : {
1655 45 : gcc_assert (switch_stmt);
1656 45 : gcc_assert (TREE_CODE (type) == ENUMERAL_TYPE);
1657 :
1658 45 : for (tree enum_val_iter = TYPE_VALUES (type);
1659 177 : enum_val_iter;
1660 132 : enum_val_iter = TREE_CHAIN (enum_val_iter))
1661 : {
1662 142 : tree enum_val = TREE_VALUE (enum_val_iter);
1663 142 : gcc_assert (TREE_CODE (enum_val) == CONST_DECL);
1664 142 : gcc_assert (TREE_CODE (DECL_INITIAL (enum_val)) == INTEGER_CST);
1665 142 : if (!has_nondefault_case_for_value_p (switch_stmt,
1666 142 : DECL_INITIAL (enum_val)))
1667 : return false;
1668 : }
1669 : return true;
1670 : }
1671 :
1672 : /* Given an EDGE guarded by SWITCH_STMT, determine appropriate constraints
1673 : for the edge to be taken.
1674 :
1675 : If they are feasible, add the constraints and return true.
1676 :
1677 : Return false if the constraints contradict existing knowledge
1678 : (and so the edge should not be taken).
1679 : When returning false, if OUT is non-NULL, write a new rejected_constraint
1680 : to it. */
1681 :
1682 : bool
1683 8072 : switch_case_op::
1684 : apply_constraints (const superedge *,
1685 : region_model &model,
1686 : region_model_context *ctxt,
1687 : std::unique_ptr<rejected_constraint> *out) const
1688 : {
1689 8072 : const gswitch *switch_stmt = &get_gswitch ();
1690 8072 : tree index = gimple_switch_index (switch_stmt);
1691 8072 : const svalue *index_sval = model.get_rvalue (index, ctxt);
1692 8072 : bool check_index_type = true;
1693 :
1694 : /* With -fshort-enum, there may be a type cast. */
1695 6812 : if (ctxt && index_sval->get_kind () == SK_UNARYOP
1696 8519 : && TREE_CODE (index_sval->get_type ()) == INTEGER_TYPE)
1697 : {
1698 429 : const unaryop_svalue *unaryop = as_a <const unaryop_svalue *> (index_sval);
1699 429 : if (unaryop->get_op () == NOP_EXPR
1700 429 : && is_a <const initial_svalue *> (unaryop->get_arg ()))
1701 411 : if (const initial_svalue *initvalop = (as_a <const initial_svalue *>
1702 411 : (unaryop->get_arg ())))
1703 411 : if (initvalop->get_type ()
1704 411 : && TREE_CODE (initvalop->get_type ()) == ENUMERAL_TYPE)
1705 : {
1706 : index_sval = initvalop;
1707 : check_index_type = false;
1708 : }
1709 : }
1710 :
1711 : /* If we're switching based on an enum type, assume that the user is only
1712 : working with values from the enum. Hence if this is an
1713 : implicitly-created "default", assume it doesn't get followed.
1714 : This fixes numerous "uninitialized" false positives where we otherwise
1715 : consider jumping past the initialization cases. */
1716 :
1717 8072 : if (/* Don't check during feasibility-checking (when ctxt is NULL). */
1718 : ctxt
1719 : /* Must be an enum value. */
1720 6812 : && index_sval->get_type ()
1721 6812 : && (!check_index_type
1722 6497 : || TREE_CODE (TREE_TYPE (index)) == ENUMERAL_TYPE)
1723 663 : && TREE_CODE (index_sval->get_type ()) == ENUMERAL_TYPE
1724 : /* If we have a constant, then we can check it directly. */
1725 663 : && index_sval->get_kind () != SK_CONSTANT
1726 634 : && implicitly_created_default_p ()
1727 45 : && has_nondefault_cases_for_all_enum_values_p (switch_stmt,
1728 : index_sval->get_type ())
1729 : /* Don't do this if there's a chance that the index is
1730 : attacker-controlled. */
1731 8107 : && !ctxt->possibly_tainted_p (index_sval))
1732 : {
1733 33 : if (out)
1734 0 : *out = std::make_unique <rejected_default_case> (model);
1735 33 : return false;
1736 : }
1737 :
1738 8039 : bool sat
1739 16078 : = model.get_constraints ()->add_bounded_ranges (index_sval,
1740 8039 : m_all_cases_ranges);
1741 8039 : if (!sat && out)
1742 48 : *out = std::make_unique <rejected_ranges_constraint>
1743 48 : (model, index, m_all_cases_ranges);
1744 8039 : if (sat && ctxt && !m_all_cases_ranges->empty_p ())
1745 6202 : ctxt->on_bounded_ranges (*index_sval, *m_all_cases_ranges);
1746 : return sat;
1747 : }
1748 :
1749 : /* Return true iff this op's edge is purely for an
1750 : implicitly-created "default". */
1751 :
1752 : bool
1753 659 : switch_case_op::implicitly_created_default_p () const
1754 : {
1755 659 : if (m_case_labels.size () != 1)
1756 : return false;
1757 :
1758 592 : tree case_label = m_case_labels[0];
1759 592 : gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
1760 592 : if (CASE_LOW (case_label))
1761 : return false;
1762 :
1763 : /* We have a single "default" case.
1764 : Assume that it was implicitly created if it has UNKNOWN_LOCATION. */
1765 172 : return EXPR_LOCATION (case_label) == UNKNOWN_LOCATION;
1766 : }
1767 :
1768 : /* Given an ERT_TRY region, get the eh_catch corresponding to
1769 : the label of DST_SNODE, if any. */
1770 :
1771 : static eh_catch
1772 179 : get_catch (eh_region eh_reg, supernode *dst_snode)
1773 : {
1774 179 : gcc_assert (eh_reg->type == ERT_TRY);
1775 :
1776 179 : tree dst_snode_label = dst_snode->get_label ();
1777 179 : if (!dst_snode_label)
1778 : return nullptr;
1779 :
1780 124 : for (eh_catch iter = eh_reg->u.eh_try.first_catch;
1781 184 : iter;
1782 60 : iter = iter->next_catch)
1783 184 : if (iter->label == dst_snode_label)
1784 : return iter;
1785 :
1786 : return nullptr;
1787 : }
1788 :
1789 : class rejected_eh_dispatch : public rejected_constraint
1790 : {
1791 : public:
1792 0 : rejected_eh_dispatch (const region_model &model)
1793 0 : : rejected_constraint (model)
1794 : {}
1795 :
1796 0 : void dump_to_pp (pretty_printer *pp) const final override
1797 : {
1798 0 : pp_printf (pp, "rejected_eh_dispatch");
1799 0 : }
1800 : };
1801 :
1802 : static bool
1803 273 : exception_matches_type_p (tree exception_type,
1804 : tree catch_type)
1805 : {
1806 0 : if (catch_type == exception_type)
1807 0 : return true;
1808 :
1809 : /* TODO (PR analyzer/119697): we should also handle subclasses etc;
1810 : see the rules in https://en.cppreference.com/w/cpp/language/catch
1811 :
1812 : It looks like we should be calling (or emulating)
1813 : can_convert_eh from the C++ FE, but that's specific to the C++ FE. */
1814 :
1815 : return false;
1816 : }
1817 :
1818 : static bool
1819 287 : matches_any_exception_type_p (eh_catch ehc, tree exception_type)
1820 : {
1821 287 : if (ehc->type_list == NULL_TREE)
1822 : /* All exceptions are caught here. */
1823 : return true;
1824 :
1825 371 : for (tree iter = ehc->type_list; iter; iter = TREE_CHAIN (iter))
1826 287 : if (exception_matches_type_p (TREE_VALUE (iter),
1827 : exception_type))
1828 : return true;
1829 : return false;
1830 : }
1831 :
1832 : // class eh_dispatch_edge_op : public control_flow_op
1833 :
1834 : std::unique_ptr<eh_dispatch_edge_op>
1835 191 : eh_dispatch_edge_op::make (supernode *src_snode,
1836 : supernode *dst_snode,
1837 : ::edge cfg_edge,
1838 : const geh_dispatch &eh_dispatch_stmt)
1839 : {
1840 191 : const eh_status *eh = src_snode->get_function ()->eh;
1841 191 : gcc_assert (eh);
1842 191 : int region_idx = gimple_eh_dispatch_region (&eh_dispatch_stmt);
1843 191 : gcc_assert (region_idx > 0);
1844 191 : gcc_assert ((*eh->region_array)[region_idx]);
1845 191 : eh_region eh_reg = (*eh->region_array)[region_idx];
1846 191 : gcc_assert (eh_reg);
1847 191 : switch (eh_reg->type)
1848 : {
1849 0 : default:
1850 0 : gcc_unreachable ();
1851 0 : case ERT_CLEANUP:
1852 : // TODO
1853 0 : gcc_unreachable ();
1854 179 : break;
1855 179 : case ERT_TRY:
1856 179 : {
1857 179 : eh_catch ehc = get_catch (eh_reg, dst_snode);
1858 179 : return std::make_unique<eh_dispatch_try_edge_op>
1859 179 : (src_snode,
1860 : cfg_edge, eh_dispatch_stmt,
1861 179 : eh_reg, ehc);
1862 : }
1863 12 : break;
1864 12 : case ERT_ALLOWED_EXCEPTIONS:
1865 12 : return std::make_unique<eh_dispatch_allowed_edge_op>
1866 12 : (src_snode, dst_snode,
1867 : cfg_edge, eh_dispatch_stmt,
1868 12 : eh_reg);
1869 0 : break;
1870 0 : case ERT_MUST_NOT_THROW:
1871 : // TODO
1872 0 : gcc_unreachable ();
1873 : break;
1874 : }
1875 : }
1876 :
1877 191 : eh_dispatch_edge_op::
1878 : eh_dispatch_edge_op (supernode *src_snode,
1879 : enum kind kind_,
1880 : ::edge cfg_edge,
1881 : const geh_dispatch &geh_dispatch_stmt,
1882 191 : eh_region eh_reg)
1883 : : control_flow_op (kind_, cfg_edge, geh_dispatch_stmt),
1884 191 : m_src_snode (src_snode),
1885 191 : m_eh_region (eh_reg)
1886 : {
1887 191 : }
1888 :
1889 : bool
1890 324 : eh_dispatch_edge_op::
1891 : apply_constraints (const superedge *sedge,
1892 : region_model &model,
1893 : region_model_context *ctxt,
1894 : std::unique_ptr<rejected_constraint> *out) const
1895 : {
1896 324 : const exception_node *current_node = model.get_current_thrown_exception ();
1897 :
1898 321 : if (!current_node)
1899 : return false;
1900 :
1901 321 : gcc_assert (current_node);
1902 321 : tree curr_exception_type = current_node->maybe_get_type ();
1903 321 : if (!curr_exception_type)
1904 : /* We don't know the specific type. */
1905 : return true;
1906 :
1907 243 : return apply_eh_constraints (sedge, model, ctxt, curr_exception_type, out);
1908 : }
1909 :
1910 : // class eh_dispatch_try_edge_op : public eh_dispatch_edge_op
1911 :
1912 179 : eh_dispatch_try_edge_op::
1913 : eh_dispatch_try_edge_op (supernode *src_snode,
1914 : ::edge cfg_edge,
1915 : const geh_dispatch &geh_dispatch_stmt,
1916 : eh_region eh_reg,
1917 179 : eh_catch ehc)
1918 : : eh_dispatch_edge_op (src_snode,
1919 : kind::eh_dispatch_try_edge,
1920 : cfg_edge, geh_dispatch_stmt, eh_reg),
1921 179 : m_eh_catch (ehc)
1922 : {
1923 179 : gcc_assert (eh_reg->type == ERT_TRY);
1924 179 : }
1925 :
1926 : void
1927 0 : eh_dispatch_try_edge_op::print_as_edge_label (pretty_printer *pp,
1928 : bool user_facing) const
1929 : {
1930 0 : if (!user_facing)
1931 0 : pp_string (pp, "ERT_TRY: ");
1932 0 : if (m_eh_catch)
1933 : {
1934 0 : bool first = true;
1935 0 : for (tree iter = m_eh_catch->type_list; iter; iter = TREE_CHAIN (iter))
1936 : {
1937 0 : if (!first)
1938 0 : pp_string (pp, ", ");
1939 0 : pp_printf (pp, "on catch %qT", TREE_VALUE (iter));
1940 0 : first = false;
1941 : }
1942 : }
1943 : else
1944 0 : pp_string (pp, "on uncaught exception");
1945 0 : }
1946 :
1947 : void
1948 66 : eh_dispatch_try_edge_op::add_any_events_for_eedge (const exploded_edge &eedge,
1949 : checker_path &out_path) const
1950 : {
1951 66 : if (m_eh_catch)
1952 : {
1953 63 : const region_model *model = eedge.m_src->get_state ().m_region_model;
1954 63 : auto curr_thrown_exception_node
1955 63 : = model->get_current_thrown_exception ();
1956 0 : gcc_assert (curr_thrown_exception_node);
1957 63 : tree type = curr_thrown_exception_node->maybe_get_type ();
1958 63 : out_path.add_event
1959 63 : (std::make_unique<catch_cfg_edge_event>
1960 63 : (eedge,
1961 126 : event_loc_info (eedge.m_dest),
1962 : *this,
1963 : type));
1964 : }
1965 : else
1966 : {
1967 : /* We have the "uncaught exception" sedge, from eh_dispatch
1968 : to a block containing resx.
1969 : Don't add any events for this, so that we can consolidate
1970 : adjacent stack unwinding events. */
1971 : }
1972 66 : }
1973 :
1974 : bool
1975 230 : eh_dispatch_try_edge_op::
1976 : apply_eh_constraints (const superedge *sedge,
1977 : region_model &model,
1978 : region_model_context */*ctxt*/,
1979 : tree exception_type,
1980 : std::unique_ptr<rejected_constraint> *out) const
1981 : {
1982 : /* TODO: can we rely on this ordering?
1983 : or do we need to iterate through prev_catch ? */
1984 : /* The exception must not match any of the previous edges. */
1985 768 : for (auto sibling_sedge : get_src_snode ()->m_succs)
1986 : {
1987 308 : if (sibling_sedge == sedge)
1988 : break;
1989 :
1990 139 : const eh_dispatch_try_edge_op *sibling_edge_op
1991 139 : = (const eh_dispatch_try_edge_op *)sibling_sedge->get_op ();
1992 139 : if (eh_catch ehc = sibling_edge_op->m_eh_catch)
1993 139 : if (matches_any_exception_type_p (ehc, exception_type))
1994 : {
1995 : /* The earlier sibling matches, so the "unhandled" edge is
1996 : not taken. */
1997 61 : if (out)
1998 0 : *out = std::make_unique<rejected_eh_dispatch> (model);
1999 61 : return false;
2000 : }
2001 : }
2002 :
2003 169 : if (eh_catch ehc = m_eh_catch)
2004 : {
2005 : /* We have an edge that tried to match one or more types. */
2006 :
2007 : /* The exception must not match any of the previous edges. */
2008 :
2009 : /* It must match this type. */
2010 148 : if (matches_any_exception_type_p (ehc, exception_type))
2011 : return true;
2012 : else
2013 : {
2014 : /* Exception type doesn't match. */
2015 33 : if (out)
2016 0 : *out = std::make_unique<rejected_eh_dispatch> (model);
2017 33 : return false;
2018 : }
2019 : }
2020 : else
2021 : {
2022 : /* This is the "unhandled exception" edge.
2023 : If we get here then no sibling edges matched;
2024 : we will follow this edge. */
2025 : return true;
2026 : }
2027 : }
2028 :
2029 : // class eh_dispatch_allowed_edge_op : public eh_dispatch_edge_op
2030 :
2031 12 : eh_dispatch_allowed_edge_op::
2032 : eh_dispatch_allowed_edge_op (supernode *src_snode,
2033 : supernode *dst_snode,
2034 : ::edge cfg_edge,
2035 : const geh_dispatch &geh_dispatch_stmt,
2036 12 : eh_region eh_reg)
2037 : : eh_dispatch_edge_op (src_snode,
2038 : kind::eh_dispatch_try_edge,
2039 12 : cfg_edge, geh_dispatch_stmt, eh_reg)
2040 : {
2041 12 : gcc_assert (eh_reg->type == ERT_ALLOWED_EXCEPTIONS);
2042 :
2043 : /* We expect two sibling out-edges at an eh_dispatch from such a region:
2044 :
2045 : - one to a bb without a gimple label, with a resx,
2046 : for exceptions of expected types
2047 :
2048 : - one to a bb with a gimple label, with a call to __cxa_unexpected,
2049 : for exceptions of unexpected types.
2050 :
2051 : Set m_kind for this edge accordingly. */
2052 12 : gcc_assert (cfg_edge->src->succs->length () == 2);
2053 12 : tree label_for_unexpected_exceptions = eh_reg->u.allowed.label;
2054 12 : tree label_for_dest_enode = dst_snode->get_label ();
2055 12 : if (label_for_dest_enode == label_for_unexpected_exceptions)
2056 6 : m_kind = eh_kind::unexpected;
2057 : else
2058 : {
2059 6 : gcc_assert (label_for_dest_enode == nullptr);
2060 6 : m_kind = eh_kind::expected;
2061 : }
2062 12 : }
2063 :
2064 : void
2065 3 : eh_dispatch_allowed_edge_op::print_as_edge_label (pretty_printer *pp,
2066 : bool user_facing) const
2067 : {
2068 3 : if (!user_facing)
2069 : {
2070 0 : switch (m_kind)
2071 : {
2072 0 : default:
2073 0 : gcc_unreachable ();
2074 0 : case eh_kind::expected:
2075 0 : pp_string (pp, "expected: ");
2076 0 : break;
2077 0 : case eh_kind::unexpected:
2078 0 : pp_string (pp, "unexpected: ");
2079 0 : break;
2080 : }
2081 0 : pp_string (pp, "ERT_ALLOWED_EXCEPTIONS: ");
2082 0 : eh_region eh_reg = get_eh_region ();
2083 0 : bool first = true;
2084 0 : for (tree iter = eh_reg->u.allowed.type_list; iter;
2085 0 : iter = TREE_CHAIN (iter))
2086 : {
2087 0 : if (!first)
2088 0 : pp_string (pp, ", ");
2089 0 : pp_printf (pp, "%qT", TREE_VALUE (iter));
2090 0 : first = false;
2091 : }
2092 : }
2093 3 : }
2094 :
2095 : bool
2096 13 : eh_dispatch_allowed_edge_op::
2097 : apply_eh_constraints (const superedge *,
2098 : region_model &model,
2099 : region_model_context */*ctxt*/,
2100 : tree exception_type,
2101 : std::unique_ptr<rejected_constraint> *out) const
2102 : {
2103 13 : auto curr_thrown_exception_node = model.get_current_thrown_exception ();
2104 0 : gcc_assert (curr_thrown_exception_node);
2105 13 : tree curr_exception_type = curr_thrown_exception_node->maybe_get_type ();
2106 13 : eh_region eh_reg = get_eh_region ();
2107 13 : tree type_list = eh_reg->u.allowed.type_list;
2108 :
2109 13 : switch (get_eh_kind ())
2110 : {
2111 0 : default:
2112 0 : gcc_unreachable ();
2113 5 : case eh_kind::expected:
2114 5 : if (!curr_exception_type)
2115 : {
2116 : /* We don't know the specific type;
2117 : assume we have one of an expected type. */
2118 : return true;
2119 : }
2120 8 : for (tree iter = type_list; iter; iter = TREE_CHAIN (iter))
2121 11 : if (exception_matches_type_p (TREE_VALUE (iter),
2122 : exception_type))
2123 : return true;
2124 3 : if (out)
2125 0 : *out = std::make_unique<rejected_eh_dispatch> (model);
2126 : return false;
2127 :
2128 8 : case eh_kind::unexpected:
2129 8 : if (!curr_exception_type)
2130 : {
2131 : /* We don't know the specific type;
2132 : assume we don't have one of an expected type. */
2133 0 : if (out)
2134 0 : *out = std::make_unique<rejected_eh_dispatch> (model);
2135 0 : return false;
2136 : }
2137 14 : for (tree iter = type_list; iter; iter = TREE_CHAIN (iter))
2138 8 : if (exception_matches_type_p (TREE_VALUE (iter),
2139 : exception_type))
2140 : {
2141 2 : if (out)
2142 0 : *out = std::make_unique<rejected_eh_dispatch> (model);
2143 2 : return false;
2144 : }
2145 : return true;
2146 : }
2147 : }
2148 :
2149 : // class phis_for_edge_op : public operation
2150 :
2151 : std::unique_ptr<operation>
2152 15548 : phis_for_edge_op::maybe_make (::edge cfg_in_edge)
2153 : {
2154 15548 : std::vector<pair> pairs = get_pairs_for_phi_along_in_edge (cfg_in_edge);
2155 15548 : if (pairs.empty ())
2156 6340 : return nullptr;
2157 :
2158 9208 : return std::make_unique <phis_for_edge_op> (std::move (pairs));
2159 15548 : }
2160 :
2161 9208 : phis_for_edge_op::phis_for_edge_op (std::vector<pair> &&pairs)
2162 : : operation (kind::phis),
2163 9208 : m_pairs (std::move (pairs))
2164 : {
2165 9208 : }
2166 :
2167 : std::vector<phis_for_edge_op::pair>
2168 15548 : phis_for_edge_op::get_pairs_for_phi_along_in_edge (::edge cfg_in_edge)
2169 : {
2170 15548 : std::vector<pair> result;
2171 :
2172 15548 : const size_t phi_arg_idx = cfg_in_edge->dest_idx;
2173 15548 : for (gphi_iterator gpi = gsi_start_phis (cfg_in_edge->dest);
2174 37887 : !gsi_end_p (gpi); gsi_next (&gpi))
2175 : {
2176 22339 : gphi * const phi = gpi.phi ();
2177 22339 : tree dst = gimple_phi_result (phi);
2178 :
2179 : /* We don't bother tracking the .MEM SSA names. */
2180 22339 : if (tree var = SSA_NAME_VAR (dst))
2181 17508 : if (TREE_CODE (var) == VAR_DECL)
2182 16915 : if (VAR_DECL_IS_VIRTUAL_OPERAND (var))
2183 11186 : continue;
2184 :
2185 11153 : tree src = gimple_phi_arg_def (phi, phi_arg_idx);
2186 :
2187 11153 : result.push_back ({dst, src});
2188 : }
2189 :
2190 15548 : return result;
2191 : }
2192 :
2193 : void
2194 87 : phis_for_edge_op::print_as_edge_label (pretty_printer *pp,
2195 : bool ) const
2196 : {
2197 87 : pp_printf (pp, "PHI(");
2198 87 : bool first = true;
2199 174 : for (auto &p : m_pairs)
2200 : {
2201 87 : if (first)
2202 : first = false;
2203 : else
2204 0 : pp_string (pp, ", ");
2205 :
2206 87 : pp_printf (pp, "%E = %E", p.m_dst, p.m_src);
2207 : }
2208 87 : pp_printf (pp, ");");
2209 87 : }
2210 :
2211 : void
2212 9208 : phis_for_edge_op::
2213 : walk_load_store_addr_ops (void */*data*/ ,
2214 : walk_stmt_load_store_addr_fn /*load_cb*/,
2215 : walk_stmt_load_store_addr_fn /*store_cb*/,
2216 : walk_stmt_load_store_addr_fn /*addr_cb*/) const
2217 : {
2218 9208 : }
2219 :
2220 : bool
2221 20651 : phis_for_edge_op::defines_ssa_name_p (const_tree ssa_name) const
2222 : {
2223 37872 : for (auto &p : m_pairs)
2224 28413 : if (p.m_dst == ssa_name)
2225 20651 : return true;
2226 : return false;
2227 : }
2228 :
2229 : void
2230 21353 : phis_for_edge_op::execute (operation_context &op_ctxt) const
2231 : {
2232 21353 : auto logger = op_ctxt.get_logger ();
2233 21353 : LOG_SCOPE (logger);
2234 :
2235 21353 : auto dst_point (op_ctxt.get_next_intraprocedural_point ());
2236 :
2237 21353 : const program_state &src_state (op_ctxt.get_initial_state ());
2238 21353 : program_state dst_state (src_state);
2239 :
2240 21353 : impl_path_context path_ctxt (&dst_state, logger);
2241 21353 : uncertainty_t uncertainty;
2242 21353 : impl_region_model_context ctxt (op_ctxt.m_eg,
2243 21353 : &op_ctxt.m_src_enode,
2244 :
2245 : /* TODO: should we be getting the ECs from the
2246 : old state, rather than the new? */
2247 21353 : &op_ctxt.get_initial_state (),
2248 : &dst_state,
2249 : &uncertainty,
2250 : &path_ctxt,
2251 : nullptr,
2252 21353 : nullptr);
2253 :
2254 21353 : update_state (src_state, dst_state, &ctxt);
2255 :
2256 21353 : op_ctxt.add_outcome (dst_point, dst_state, false, &uncertainty);
2257 42706 : }
2258 :
2259 : void
2260 21767 : phis_for_edge_op::update_state (const program_state &src_state,
2261 : program_state &dst_state,
2262 : region_model_context *ctxt) const
2263 : {
2264 21767 : const region_model &src_model = *src_state.m_region_model;
2265 21767 : region_model &dst_model = *dst_state.m_region_model;
2266 :
2267 21767 : hash_set<const svalue *> svals_changing_meaning;
2268 :
2269 : /* Get state from src_state so that all of the phi stmts for an edge
2270 : are effectively handled simultaneously. */
2271 51989 : for (auto &p : m_pairs)
2272 : {
2273 30222 : const svalue *src_sval = src_model.get_rvalue (p.m_src, nullptr);
2274 30222 : const region *dst_reg = src_model.get_lvalue (p.m_dst, nullptr);
2275 :
2276 30222 : const svalue *old_sval = src_model.get_rvalue (p.m_dst, nullptr);
2277 30222 : if (old_sval->get_kind () == SK_WIDENING)
2278 12 : svals_changing_meaning.add (old_sval);
2279 :
2280 30222 : dst_model.set_value (dst_reg, src_sval, ctxt);
2281 : }
2282 :
2283 43546 : for (auto iter : svals_changing_meaning)
2284 12 : dst_model.get_constraints ()->purge_state_involving (iter);
2285 21767 : }
2286 :
2287 : bool
2288 10929 : phis_for_edge_op::
2289 : execute_for_feasibility (const exploded_edge &eedge,
2290 : feasibility_state &fstate,
2291 : region_model_context *ctxt,
2292 : std::unique_ptr<rejected_constraint> */*out_rc*/) const
2293 : {
2294 10929 : hash_set<const svalue *> svals_changing_meaning;
2295 : /* Get state from src_state so that all of the phi stmts for an edge
2296 : are effectively handled simultaneously. */
2297 10929 : region_model &model = fstate.get_model ();
2298 10929 : region_model src_model (model);
2299 24138 : for (auto &p : m_pairs)
2300 : {
2301 13209 : const svalue *src_sval = src_model.get_rvalue (p.m_src, ctxt);
2302 13209 : const region *dst_reg = model.get_lvalue (p.m_dst, ctxt);
2303 :
2304 13209 : const svalue *sval = model.get_rvalue (p.m_dst, ctxt);
2305 13209 : if (sval->get_kind () == SK_WIDENING)
2306 24 : svals_changing_meaning.add (sval);
2307 :
2308 13209 : model.set_value (dst_reg, src_sval, ctxt);
2309 : }
2310 :
2311 10953 : for (auto iter : svals_changing_meaning)
2312 24 : model.get_constraints ()->purge_state_involving (iter);
2313 :
2314 10929 : {
2315 : /* If we've entering an snode that we've already visited on this
2316 : epath, then we need do fix things up for loops; see the
2317 : comment for store::loop_replay_fixup.
2318 : Perhaps we should probably also verify the callstring,
2319 : and track program_points, but hopefully doing it by supernode
2320 : is good enough. */
2321 10929 : const exploded_node &dst_enode = *eedge.m_dest;
2322 10929 : const unsigned dst_snode_idx = dst_enode.get_supernode ()->m_id;
2323 10929 : if (bitmap_bit_p (fstate.get_snodes_visited (), dst_snode_idx))
2324 4661 : model.loop_replay_fixup (dst_enode.get_state ().m_region_model);
2325 : }
2326 :
2327 21858 : return true;
2328 10929 : }
2329 :
2330 : void
2331 414 : phis_for_edge_op::
2332 : update_state_for_bulk_merger (const program_state &src_state,
2333 : program_state &dst_state) const
2334 : {
2335 414 : update_state (src_state, dst_state, nullptr);
2336 414 : }
2337 :
2338 : void
2339 1258 : phis_for_edge_op::add_any_events_for_eedge (const exploded_edge &,
2340 : checker_path &) const
2341 : {
2342 : // No-op
2343 1258 : }
2344 :
2345 : // class resx_op : public gimple_stmt_op
2346 :
2347 : void
2348 536 : resx_op::execute (operation_context &op_ctxt) const
2349 : {
2350 536 : auto logger = op_ctxt.get_logger ();
2351 536 : LOG_SCOPE (logger);
2352 :
2353 536 : program_point dst_point (op_ctxt.get_next_intraprocedural_point ());
2354 536 : program_state dst_state (op_ctxt.get_initial_state ());
2355 536 : op_region_model_context ctxt (op_ctxt, dst_state);
2356 :
2357 1072 : if (exploded_node *dst_enode
2358 536 : = op_ctxt.m_eg.get_or_create_node (dst_point, dst_state,
2359 536 : &op_ctxt.m_src_enode,
2360 : // Don't add to worklist:
2361 : false))
2362 : {
2363 536 : op_ctxt.m_eg.add_edge (&op_ctxt.m_src_enode,
2364 : dst_enode,
2365 536 : &op_ctxt.m_sedge,
2366 : false,
2367 536 : nullptr);
2368 : /* Try to adding eedges and enodes that unwind to the next
2369 : eh_dispatch statement, if any.
2370 : Only the final enode is added to the worklist. */
2371 536 : op_ctxt.m_eg.unwind_from_exception (*dst_enode,
2372 : nullptr,
2373 : &ctxt);
2374 : }
2375 536 : }
2376 :
2377 : void
2378 18 : resx_op::add_any_events_for_eedge (const exploded_edge &,
2379 : checker_path &) const
2380 : {
2381 18 : }
2382 :
2383 : } // namespace ana
2384 :
2385 : #endif /* #if ENABLE_ANALYZER */
|