1 /* Classes for managing a directed graph of <point, state> pairs.
2 Copyright (C) 2019-2020 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
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)
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.
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/>. */
21 #ifndef GCC_ANALYZER_EXPLODED_GRAPH_H
22 #define GCC_ANALYZER_EXPLODED_GRAPH_H
26 /* Concrete implementation of region_model_context, wiring it up to the
27 rest of the analysis engine. */
29 class impl_region_model_context
: public region_model_context
32 impl_region_model_context (exploded_graph
&eg
,
33 const exploded_node
*enode_for_diag
,
35 /* TODO: should we be getting the ECs from the
36 old state, rather than the new? */
37 const program_state
*old_state
,
38 program_state
*new_state
,
42 stmt_finder
*stmt_finder
= NULL
);
44 impl_region_model_context (program_state
*state
,
46 const extrinsic_state
&ext_state
,
47 logger
*logger
= NULL
);
49 void warn (pending_diagnostic
*d
) FINAL OVERRIDE
;
51 void remap_svalue_ids (const svalue_id_map
&map
) FINAL OVERRIDE
;
53 int on_svalue_purge (svalue_id first_unused_sid
,
54 const svalue_id_map
&map
) FINAL OVERRIDE
;
56 logger
*get_logger () FINAL OVERRIDE
58 return m_logger
.get_logger ();
61 void on_state_leak (const state_machine
&sm
,
64 svalue_id first_unused_sid
,
65 const svalue_id_map
&map
,
66 state_machine::state_t state
);
68 void on_inherited_svalue (svalue_id parent_sid
,
69 svalue_id child_sid
) FINAL OVERRIDE
;
71 void on_cast (svalue_id src_sid
,
72 svalue_id dst_sid
) FINAL OVERRIDE
;
74 void on_condition (tree lhs
, enum tree_code op
, tree rhs
) FINAL OVERRIDE
;
76 void on_unknown_change (svalue_id sid ATTRIBUTE_UNUSED
) FINAL OVERRIDE
;
78 void on_phi (const gphi
*phi
, tree rhs
) FINAL OVERRIDE
;
80 void on_unexpected_tree_code (tree t
,
81 const dump_location_t
&loc
) FINAL OVERRIDE
;
85 const exploded_node
*m_enode_for_diag
;
86 const program_state
*m_old_state
;
87 program_state
*m_new_state
;
88 state_change
*m_change
;
90 stmt_finder
*m_stmt_finder
;
91 const extrinsic_state
&m_ext_state
;
94 /* A <program_point, program_state> pair, used internally by
95 exploded_node as its immutable data, and as a key for identifying
96 exploded_nodes we've already seen in the graph. */
101 point_and_state (const program_point
&point
,
102 const program_state
&state
)
105 m_hash (m_point
.hash () ^ m_state
.hash ())
107 /* We shouldn't be building point_and_states and thus exploded_nodes
108 for states that aren't valid. */
109 gcc_assert (state
.m_valid
);
112 hashval_t
hash () const
116 bool operator== (const point_and_state
&other
) const
118 return m_point
== other
.m_point
&& m_state
== other
.m_state
;
121 const program_point
&get_point () const { return m_point
; }
122 const program_state
&get_state () const { return m_state
; }
124 void set_state (const program_state
&state
)
127 m_hash
= m_point
.hash () ^ m_state
.hash ();
130 void validate (const extrinsic_state
&ext_state
) const;
133 program_point m_point
;
134 program_state m_state
;
138 /* A traits class for exploded graphs and their nodes and edges. */
142 typedef exploded_node node_t
;
143 typedef exploded_edge edge_t
;
144 typedef exploded_graph graph_t
;
147 dump_args_t (const exploded_graph
&eg
) : m_eg (eg
) {}
148 const exploded_graph
&m_eg
;
150 typedef exploded_cluster cluster_t
;
153 /* An exploded_node is a unique, immutable <point, state> pair within the
155 Each exploded_node has a unique index within the graph
156 (for ease of debugging). */
158 class exploded_node
: public dnode
<eg_traits
>
161 /* Has this enode had exploded_graph::process_node called on it?
162 This allows us to distinguish enodes that were merged during
163 worklist-handling, and thus never had process_node called on them
164 (in favor of processing the merged node). */
167 /* Node is in the worklist. */
170 /* Node has had exploded_graph::process_node called on it. */
173 /* Node was left unprocessed due to merger; it won't have had
174 exploded_graph::process_node called on it. */
178 exploded_node (const point_and_state
&ps
, int index
);
180 hashval_t
hash () const { return m_ps
.hash (); }
182 void dump_dot (graphviz_out
*gv
, const dump_args_t
&args
)
183 const FINAL OVERRIDE
;
184 void dump_dot_id (pretty_printer
*pp
) const;
186 void dump_to_pp (pretty_printer
*pp
, const extrinsic_state
&ext_state
) const;
187 void dump (FILE *fp
, const extrinsic_state
&ext_state
) const;
188 void dump (const extrinsic_state
&ext_state
) const;
190 /* The result of on_stmt. */
193 on_stmt_flags (bool sm_changes
)
194 : m_sm_changes (sm_changes
),
195 m_terminate_path (false)
198 static on_stmt_flags
terminate_path ()
200 return on_stmt_flags (true, true);
203 static on_stmt_flags
state_change (bool any_sm_changes
)
205 return on_stmt_flags (any_sm_changes
, false);
208 /* Did any sm-changes occur handling the stmt. */
209 bool m_sm_changes
: 1;
211 /* Should we stop analyzing this path (on_stmt may have already
212 added nodes/edges, e.g. when handling longjmp). */
213 bool m_terminate_path
: 1;
216 on_stmt_flags (bool sm_changes
,
218 : m_sm_changes (sm_changes
),
219 m_terminate_path (terminate_path
)
223 on_stmt_flags
on_stmt (exploded_graph
&eg
,
224 const supernode
*snode
,
226 program_state
*state
,
227 state_change
*change
) const;
228 bool on_edge (exploded_graph
&eg
,
229 const superedge
*succ
,
230 program_point
*next_point
,
231 program_state
*next_state
,
232 state_change
*change
) const;
233 void on_longjmp (exploded_graph
&eg
,
235 program_state
*new_state
,
236 region_model_context
*ctxt
) const;
238 void detect_leaks (exploded_graph
&eg
) const;
240 const program_point
&get_point () const { return m_ps
.get_point (); }
241 const supernode
*get_supernode () const
243 return get_point ().get_supernode ();
245 function
*get_function () const
247 return get_point ().get_function ();
249 int get_stack_depth () const
251 return get_point ().get_stack_depth ();
253 const gimple
*get_stmt () const { return get_point ().get_stmt (); }
255 const program_state
&get_state () const { return m_ps
.get_state (); }
257 const point_and_state
*get_ps_key () const { return &m_ps
; }
258 const program_point
*get_point_key () const { return &m_ps
.get_point (); }
260 void dump_succs_and_preds (FILE *outf
) const;
262 enum status
get_status () const { return m_status
; }
263 void set_status (enum status status
)
265 gcc_assert (m_status
== STATUS_WORKLIST
);
270 DISABLE_COPY_AND_ASSIGN (exploded_node
);
272 const char * get_dot_fillcolor () const;
274 /* The <program_point, program_state> pair. This is const, as it
275 is immutable once the exploded_node has been created. */
276 const point_and_state m_ps
;
278 enum status m_status
;
281 /* The index of this exploded_node. */
285 /* An edge within the exploded graph.
286 Some exploded_edges have an underlying superedge; others don't. */
288 class exploded_edge
: public dedge
<eg_traits
>
291 /* Abstract base class for associating custom data with an
292 exploded_edge, for handling non-standard edges such as
293 rewinding from a longjmp, signal handlers, etc. */
297 virtual ~custom_info_t () {}
299 /* Hook for making .dot label more readable . */
300 virtual void print (pretty_printer
*pp
) = 0;
302 /* Hook for updating MODEL within exploded_path::feasible_p. */
303 virtual void update_model (region_model
*model
,
304 const exploded_edge
&eedge
) = 0;
306 virtual void add_events_to_path (checker_path
*emission_path
,
307 const exploded_edge
&eedge
) = 0;
310 exploded_edge (exploded_node
*src
, exploded_node
*dest
,
311 const extrinsic_state
&ext_state
,
312 const superedge
*sedge
,
313 const state_change
&change
,
314 custom_info_t
*custom_info
);
316 void dump_dot (graphviz_out
*gv
, const dump_args_t
&args
)
317 const FINAL OVERRIDE
;
320 const superedge
*const m_sedge
;
322 const state_change m_change
;
324 /* NULL for most edges; will be non-NULL for special cases
325 such as an unwind from a longjmp to a setjmp, or when
326 a signal is delivered to a signal-handler.
328 Owned by this class. */
329 custom_info_t
*m_custom_info
;
332 DISABLE_COPY_AND_ASSIGN (exploded_edge
);
335 /* Extra data for an exploded_edge that represents a rewind from a
336 longjmp to a setjmp (or from a siglongjmp to a sigsetjmp). */
338 class rewind_info_t
: public exploded_edge::custom_info_t
341 rewind_info_t (const setjmp_record
&setjmp_record
,
342 const gcall
*longjmp_call
)
343 : m_setjmp_record (setjmp_record
),
344 m_longjmp_call (longjmp_call
)
347 void print (pretty_printer
*pp
) FINAL OVERRIDE
349 pp_string (pp
, "rewind");
352 void update_model (region_model
*model
,
353 const exploded_edge
&eedge
) FINAL OVERRIDE
;
355 void add_events_to_path (checker_path
*emission_path
,
356 const exploded_edge
&eedge
) FINAL OVERRIDE
;
358 const program_point
&get_setjmp_point () const
360 const program_point
&origin_point
= get_enode_origin ()->get_point ();
362 /* "origin_point" ought to be before the call to "setjmp". */
363 gcc_assert (origin_point
.get_kind () == PK_BEFORE_STMT
);
365 /* TODO: assert that it's the final stmt in its supernode. */
370 const gcall
*get_setjmp_call () const
372 return m_setjmp_record
.m_setjmp_call
;
375 const gcall
*get_longjmp_call () const
377 return m_longjmp_call
;
380 const exploded_node
*get_enode_origin () const
382 return m_setjmp_record
.m_enode
;
386 setjmp_record m_setjmp_record
;
387 const gcall
*m_longjmp_call
;
390 /* Statistics about aspects of an exploded_graph. */
394 stats (int num_supernodes
);
395 void log (logger
*logger
) const;
396 void dump (FILE *out
) const;
398 int get_total_enodes () const;
400 int m_num_nodes
[NUM_POINT_KINDS
];
401 int m_node_reuse_count
;
402 int m_node_reuse_after_merge_count
;
403 int m_num_supernodes
;
406 /* Traits class for ensuring uniqueness of point_and_state data within
407 an exploded_graph. */
409 struct eg_hash_map_traits
411 typedef const point_and_state
*key_type
;
412 typedef exploded_node
*value_type
;
413 typedef exploded_node
*compare_type
;
415 static inline hashval_t
hash (const key_type
&k
)
417 gcc_assert (k
!= NULL
);
418 gcc_assert (k
!= reinterpret_cast<key_type
> (1));
421 static inline bool equal_keys (const key_type
&k1
, const key_type
&k2
)
423 gcc_assert (k1
!= NULL
);
424 gcc_assert (k2
!= NULL
);
425 gcc_assert (k1
!= reinterpret_cast<key_type
> (1));
426 gcc_assert (k2
!= reinterpret_cast<key_type
> (1));
430 /* Otherwise they must both be non-NULL. */
433 template <typename T
>
434 static inline void remove (T
&)
436 /* empty; the nodes are handled elsewhere. */
438 template <typename T
>
439 static inline void mark_deleted (T
&entry
)
441 entry
.m_key
= reinterpret_cast<key_type
> (1);
443 template <typename T
>
444 static inline void mark_empty (T
&entry
)
448 template <typename T
>
449 static inline bool is_deleted (const T
&entry
)
451 return entry
.m_key
== reinterpret_cast<key_type
> (1);
453 template <typename T
>
454 static inline bool is_empty (const T
&entry
)
456 return entry
.m_key
== NULL
;
458 static const bool empty_zero_p
= false;
461 /* Per-program_point data for an exploded_graph. */
463 struct per_program_point_data
465 per_program_point_data (const program_point
&key
)
466 : m_key (key
), m_excess_enodes (0)
469 const program_point m_key
;
470 auto_vec
<exploded_node
*> m_enodes
;
471 /* The number of attempts to create an enode for this point
472 after exceeding --param=analyzer-max-enodes-per-program-point. */
476 /* Traits class for storing per-program_point data within
477 an exploded_graph. */
479 struct eg_point_hash_map_traits
481 typedef const program_point
*key_type
;
482 typedef per_program_point_data
*value_type
;
483 typedef per_program_point_data
*compare_type
;
485 static inline hashval_t
hash (const key_type
&k
)
487 gcc_assert (k
!= NULL
);
488 gcc_assert (k
!= reinterpret_cast<key_type
> (1));
491 static inline bool equal_keys (const key_type
&k1
, const key_type
&k2
)
493 gcc_assert (k1
!= NULL
);
494 gcc_assert (k2
!= NULL
);
495 gcc_assert (k1
!= reinterpret_cast<key_type
> (1));
496 gcc_assert (k2
!= reinterpret_cast<key_type
> (1));
500 /* Otherwise they must both be non-NULL. */
503 template <typename T
>
504 static inline void remove (T
&)
506 /* empty; the nodes are handled elsewhere. */
508 template <typename T
>
509 static inline void mark_deleted (T
&entry
)
511 entry
.m_key
= reinterpret_cast<key_type
> (1);
513 template <typename T
>
514 static inline void mark_empty (T
&entry
)
518 template <typename T
>
519 static inline bool is_deleted (const T
&entry
)
521 return entry
.m_key
== reinterpret_cast<key_type
> (1);
523 template <typename T
>
524 static inline bool is_empty (const T
&entry
)
526 return entry
.m_key
== NULL
;
528 static const bool empty_zero_p
= false;
531 /* Data about a particular call_string within an exploded_graph. */
533 struct per_call_string_data
535 per_call_string_data (const call_string
&key
, int num_supernodes
)
536 : m_key (key
), m_stats (num_supernodes
)
539 const call_string m_key
;
543 /* Traits class for storing per-call_string data within
544 an exploded_graph. */
546 struct eg_call_string_hash_map_traits
548 typedef const call_string
*key_type
;
549 typedef per_call_string_data
*value_type
;
550 typedef per_call_string_data
*compare_type
;
552 static inline hashval_t
hash (const key_type
&k
)
554 gcc_assert (k
!= NULL
);
555 gcc_assert (k
!= reinterpret_cast<key_type
> (1));
558 static inline bool equal_keys (const key_type
&k1
, const key_type
&k2
)
560 gcc_assert (k1
!= NULL
);
561 gcc_assert (k2
!= NULL
);
562 gcc_assert (k1
!= reinterpret_cast<key_type
> (1));
563 gcc_assert (k2
!= reinterpret_cast<key_type
> (1));
567 /* Otherwise they must both be non-NULL. */
570 template <typename T
>
571 static inline void remove (T
&)
573 /* empty; the nodes are handled elsewhere. */
575 template <typename T
>
576 static inline void mark_deleted (T
&entry
)
578 entry
.m_key
= reinterpret_cast<key_type
> (1);
580 template <typename T
>
581 static inline void mark_empty (T
&entry
)
585 template <typename T
>
586 static inline bool is_deleted (const T
&entry
)
588 return entry
.m_key
== reinterpret_cast<key_type
> (1);
590 template <typename T
>
591 static inline bool is_empty (const T
&entry
)
593 return entry
.m_key
== NULL
;
595 static const bool empty_zero_p
= false;
598 /* Data about a particular function within an exploded_graph. */
600 struct per_function_data
602 per_function_data () {}
604 void add_call_summary (exploded_node
*node
)
606 m_summaries
.safe_push (node
);
609 auto_vec
<exploded_node
*> m_summaries
;
613 /* The strongly connected components of a supergraph.
614 In particular, this allows us to compute a partial ordering
617 class strongly_connected_components
620 strongly_connected_components (const supergraph
&sg
, logger
*logger
);
622 int get_scc_id (int node_index
) const
624 return m_per_node
[node_index
].m_lowlink
;
633 : m_index (-1), m_lowlink (-1), m_on_stack (false)
641 void strong_connect (unsigned index
);
643 const supergraph
&m_sg
;
644 auto_vec
<unsigned> m_stack
;
645 auto_vec
<per_node_data
> m_per_node
;
648 /* The worklist of exploded_node instances that have been added to
649 an exploded_graph, but that haven't yet been processed to find
650 their successors (or warnings).
652 The enodes are stored in a priority queue, ordered by a topological
653 sort of the SCCs in the supergraph, so that enodes for the same
654 program_point should appear at the front of the queue together.
655 This allows for state-merging at CFG join-points, so that
656 sufficiently-similar enodes can be merged into one. */
661 worklist (const exploded_graph
&eg
, const analysis_plan
&plan
);
662 unsigned length () const;
663 exploded_node
*take_next ();
664 exploded_node
*peek_next ();
665 void add_node (exploded_node
*enode
);
671 key_t (const worklist
&w
, exploded_node
*enode
)
672 : m_worklist (w
), m_enode (enode
)
675 bool operator< (const key_t
&other
) const
677 return cmp (*this, other
) < 0;
680 bool operator== (const key_t
&other
) const
682 return cmp (*this, other
) == 0;
685 bool operator> (const key_t
&other
) const
687 return !(*this == other
|| *this < other
);
691 static int cmp (const key_t
&ka
, const key_t
&kb
);
693 int get_scc_id (const exploded_node
*enode
) const
695 const supernode
*snode
= enode
->get_supernode ();
698 return m_worklist
.m_scc
.get_scc_id (snode
->m_index
);
701 const worklist
&m_worklist
;
702 exploded_node
*m_enode
;
705 /* The order is important here: m_scc needs to stick around
706 until after m_queue has finished being cleaned up (the dtor
707 calls the ordering fns). */
708 strongly_connected_components m_scc
;
709 const analysis_plan
&m_plan
;
711 /* Priority queue, backed by a fibonacci_heap. */
712 typedef fibonacci_heap
<key_t
, exploded_node
> queue_t
;
716 /* An exploded_graph is a directed graph of unique <point, state> pairs.
717 It also has a worklist of nodes that are waiting for their successors
718 to be added to the graph. */
720 class exploded_graph
: public digraph
<eg_traits
>
723 typedef hash_map
<const call_string
*, per_call_string_data
*,
724 eg_call_string_hash_map_traits
> call_string_data_map_t
;
726 exploded_graph (const supergraph
&sg
, logger
*logger
,
727 const extrinsic_state
&ext_state
,
728 const state_purge_map
*purge_map
,
729 const analysis_plan
&plan
,
733 logger
*get_logger () const { return m_logger
.get_logger (); }
735 const supergraph
&get_supergraph () const { return m_sg
; }
736 const extrinsic_state
&get_ext_state () const { return m_ext_state
; }
737 const state_purge_map
*get_purge_map () const { return m_purge_map
; }
738 const analysis_plan
&get_analysis_plan () const { return m_plan
; }
740 exploded_node
*get_origin () const { return m_origin
; }
742 exploded_node
*add_function_entry (function
*fun
);
744 void build_initial_worklist ();
745 void process_worklist ();
746 void process_node (exploded_node
*node
);
748 exploded_node
*get_or_create_node (const program_point
&point
,
749 const program_state
&state
,
750 state_change
*change
);
751 exploded_edge
*add_edge (exploded_node
*src
, exploded_node
*dest
,
752 const superedge
*sedge
,
753 const state_change
&change
,
754 exploded_edge::custom_info_t
*custom
= NULL
);
756 per_program_point_data
*
757 get_or_create_per_program_point_data (const program_point
&);
759 per_call_string_data
*
760 get_or_create_per_call_string_data (const call_string
&);
763 get_or_create_per_function_data (function
*);
764 per_function_data
*get_per_function_data (function
*) const;
766 void save_diagnostic (const state_machine
&sm
,
767 const exploded_node
*enode
,
768 const supernode
*node
, const gimple
*stmt
,
770 tree var
, state_machine::state_t state
,
771 pending_diagnostic
*d
);
773 diagnostic_manager
&get_diagnostic_manager ()
775 return m_diagnostic_manager
;
777 const diagnostic_manager
&get_diagnostic_manager () const
779 return m_diagnostic_manager
;
782 stats
*get_global_stats () { return &m_global_stats
; }
783 stats
*get_or_create_function_stats (function
*fn
);
784 void log_stats () const;
785 void dump_stats (FILE *) const;
786 void dump_states_for_supernode (FILE *, const supernode
*snode
) const;
787 void dump_exploded_nodes () const;
789 const call_string_data_map_t
*get_per_call_string_data () const
790 { return &m_per_call_string_data
; }
793 void print_bar_charts (pretty_printer
*pp
) const;
795 DISABLE_COPY_AND_ASSIGN (exploded_graph
);
797 const supergraph
&m_sg
;
801 /* Map from point/state to exploded node.
802 To avoid duplication we store point_and_state
803 *pointers* as keys, rather than point_and_state, using the
804 instance from within the exploded_node, with a custom hasher. */
805 typedef hash_map
<const point_and_state
*, exploded_node
*,
806 eg_hash_map_traits
> map_t
;
807 map_t m_point_and_state_to_node
;
809 /* Map from program_point to per-program_point data. */
810 typedef hash_map
<const program_point
*, per_program_point_data
*,
811 eg_point_hash_map_traits
> point_map_t
;
812 point_map_t m_per_point_data
;
816 exploded_node
*m_origin
;
818 const extrinsic_state
&m_ext_state
;
820 const state_purge_map
*const m_purge_map
;
822 const analysis_plan
&m_plan
;
824 typedef hash_map
<function
*, per_function_data
*> per_function_data_t
;
825 per_function_data_t m_per_function_data
;
827 diagnostic_manager m_diagnostic_manager
;
830 stats m_global_stats
;
831 typedef ordered_hash_map
<function
*, stats
*> function_stat_map_t
;
832 function_stat_map_t m_per_function_stats
;
833 stats m_functionless_stats
;
835 call_string_data_map_t m_per_call_string_data
;
837 auto_vec
<int> m_PK_AFTER_SUPERNODE_per_snode
;
840 /* A path within an exploded_graph: a sequence of edges. */
845 exploded_path () : m_edges () {}
846 exploded_path (const exploded_path
&other
);
847 exploded_path
& operator= (const exploded_path
&other
);
849 unsigned length () const { return m_edges
.length (); }
851 bool find_stmt_backwards (const gimple
*search_stmt
,
854 exploded_node
*get_final_enode () const;
856 void dump_to_pp (pretty_printer
*pp
) const;
857 void dump (FILE *fp
) const;
860 bool feasible_p (logger
*logger
) const;
862 auto_vec
<const exploded_edge
*> m_edges
;
865 /* Finding the shortest exploded_path within an exploded_graph. */
867 typedef shortest_paths
<eg_traits
, exploded_path
> shortest_exploded_paths
;
869 /* Abstract base class for use when passing NULL as the stmt for
870 a possible warning, allowing the choice of stmt to be deferred
871 until after we have an emission path (and know we're emitting a
877 virtual ~stmt_finder () {}
878 virtual stmt_finder
*clone () const = 0;
879 virtual const gimple
*find_stmt (const exploded_path
&epath
) = 0;
882 // TODO: split the above up?
886 #endif /* GCC_ANALYZER_EXPLODED_GRAPH_H */