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