1 /* Classes for representing locations within the program.
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/>. */
23 #include "coretypes.h"
25 #include "gimple-pretty-print.h"
26 #include "gcc-rich-location.h"
27 #include "analyzer/call-string.h"
28 #include "ordered-hash-map.h"
33 #include "basic-block.h"
35 #include "gimple-iterator.h"
37 #include "analyzer/analyzer.h"
38 #include "analyzer/analyzer-logging.h"
39 #include "analyzer/supergraph.h"
40 #include "analyzer/program-point.h"
44 #include "analyzer/region-model.h"
45 #include "analyzer/sm.h"
46 #include "analyzer/program-state.h"
47 #include "alloc-pool.h"
48 #include "fibonacci_heap.h"
49 #include "diagnostic-event-id.h"
50 #include "analyzer/pending-diagnostic.h"
51 #include "analyzer/diagnostic-manager.h"
52 #include "shortest-paths.h"
53 #include "analyzer/exploded-graph.h"
54 #include "analyzer/analysis-plan.h"
60 /* Get a string for PK. */
63 point_kind_to_string (enum point_kind pk
)
71 case PK_BEFORE_SUPERNODE
:
72 return "PK_BEFORE_SUPERNODE";
74 return "PK_BEFORE_STMT";
75 case PK_AFTER_SUPERNODE
:
76 return "PK_AFTER_SUPERNODE";
84 /* class function_point. */
86 /* Print this function_point to PP. */
89 function_point::print (pretty_printer
*pp
, const format
&f
) const
97 pp_printf (pp
, "origin");
100 case PK_BEFORE_SUPERNODE
:
103 pp_printf (pp
, "before SN: %i (from SN: %i)",
104 m_supernode
->m_index
, m_from_edge
->m_src
->m_index
);
106 pp_printf (pp
, "before SN: %i (NULL from-edge)",
107 m_supernode
->m_index
);
109 for (gphi_iterator gpi
110 = const_cast<supernode
*>(get_supernode ())->start_phis ();
111 !gsi_end_p (gpi
); gsi_next (&gpi
))
113 const gphi
*phi
= gpi
.phi ();
114 pp_gimple_stmt_1 (pp
, phi
, 0, (dump_flags_t
)0);
120 pp_printf (pp
, "before (SN: %i stmt: %i): ", m_supernode
->m_index
,
123 pp_gimple_stmt_1 (pp
, get_stmt (), 0, (dump_flags_t
)0);
127 print_source_line (pp
);
131 case PK_AFTER_SUPERNODE
:
132 pp_printf (pp
, "after SN: %i", m_supernode
->m_index
);
137 /* Generate a hash value for this function_point. */
140 function_point::hash () const
142 inchash::hash hstate
;
144 hstate
.add_int (m_supernode
->m_index
);
145 hstate
.add_ptr (m_from_edge
);
146 hstate
.add_int (m_stmt_idx
);
147 hstate
.add_int (m_kind
);
148 return hstate
.end ();
151 /* Get the gimple stmt for this function_point, if any. */
154 function_point::get_stmt () const
156 if (m_kind
== PK_BEFORE_STMT
)
157 return m_supernode
->m_stmts
[m_stmt_idx
];
158 else if (m_kind
== PK_AFTER_SUPERNODE
)
159 return m_supernode
->get_last_stmt ();
164 /* Get a location for this function_point, if any. */
167 function_point::get_location () const
169 const gimple
*stmt
= get_stmt ();
171 return stmt
->location
;
173 return UNKNOWN_LOCATION
;
176 /* A subclass of diagnostic_context for use by
177 program_point::print_source_line. */
179 class debug_diagnostic_context
: public diagnostic_context
182 debug_diagnostic_context ()
184 diagnostic_initialize (this, 0);
185 show_line_numbers_p
= true;
188 ~debug_diagnostic_context ()
190 diagnostic_finish (this);
194 /* Print the source line (if any) for this function_point to PP. */
197 function_point::print_source_line (pretty_printer
*pp
) const
199 const gimple
*stmt
= get_stmt ();
202 // TODO: monospace font
203 debug_diagnostic_context tmp_dc
;
204 gcc_rich_location
richloc (stmt
->location
);
205 diagnostic_show_locus (&tmp_dc
, &richloc
, DK_ERROR
);
206 pp_string (pp
, pp_formatted_text (tmp_dc
.printer
));
209 /* class program_point. */
211 /* Print this program_point to PP. */
214 program_point::print (pretty_printer
*pp
, const format
&f
) const
216 pp_string (pp
, "callstring: ");
217 m_call_string
.print (pp
);
220 m_function_point
.print (pp
, f
);
223 /* Dump this point to stderr. */
226 program_point::dump () const
229 pp_show_color (&pp
) = pp_show_color (global_dc
->printer
);
230 pp
.buffer
->stream
= stderr
;
231 print (&pp
, format (true));
235 /* Generate a hash value for this program_point. */
238 program_point::hash () const
240 inchash::hash hstate
;
241 hstate
.merge_hash (m_function_point
.hash ());
242 hstate
.merge_hash (m_call_string
.hash ());
243 return hstate
.end ();
246 /* Get the function * at DEPTH within the call stack. */
249 program_point::get_function_at_depth (unsigned depth
) const
251 gcc_assert (depth
<= m_call_string
.length ());
252 if (depth
== m_call_string
.length ())
253 return m_function_point
.get_function ();
255 return m_call_string
[depth
]->get_caller_function ();
258 /* Assert that this object is sane. */
261 program_point::validate () const
263 /* Skip this in a release build. */
268 m_call_string
.validate ();
269 /* The "callee" of the final entry in the callstring should be the
270 function of the m_function_point. */
271 if (m_call_string
.length () > 0)
272 gcc_assert (m_call_string
[m_call_string
.length () - 1]->get_callee_function ()
276 /* Check to see if SUCC is a valid edge to take (ensuring that we have
277 interprocedurally valid paths in the exploded graph, and enforcing
280 Update the call string if SUCC is a call or a return.
282 Return true if SUCC can be taken, or false otherwise.
284 This is the "point" half of exploded_node::on_edge. */
287 program_point::on_edge (exploded_graph
&eg
,
288 const superedge
*succ
)
290 logger
* const logger
= eg
.get_logger ();
292 switch (succ
->m_kind
)
294 case SUPEREDGE_CFG_EDGE
:
296 const cfg_superedge
*cfg_sedge
= as_a
<const cfg_superedge
*> (succ
);
298 /* Reject abnormal edges; we special-case setjmp/longjmp. */
299 if (cfg_sedge
->get_flags () & EDGE_ABNORMAL
)
306 const call_superedge
*call_sedge
= as_a
<const call_superedge
*> (succ
);
308 if (eg
.get_analysis_plan ().use_summary_p (call_sedge
->m_cedge
))
311 logger
->log ("rejecting call edge: using summary instead");
315 /* Add the callsite to the call string. */
316 m_call_string
.push_call (eg
.get_supergraph (), call_sedge
);
318 /* Impose a maximum recursion depth and don't analyze paths
319 that exceed it further.
320 This is something of a blunt workaround, but it only
321 applies to recursion (and mutual recursion), not to
322 general call stacks. */
323 if (m_call_string
.calc_recursion_depth ()
324 > param_analyzer_max_recursion_depth
)
327 logger
->log ("rejecting call edge: recursion limit exceeded");
328 // TODO: issue a sorry for this?
334 case SUPEREDGE_RETURN
:
336 /* Require that we return to the call site in the call string. */
337 if (m_call_string
.empty_p ())
340 logger
->log ("rejecting return edge: empty call string");
343 const return_superedge
*top_of_stack
= m_call_string
.pop ();
344 if (top_of_stack
!= succ
)
347 logger
->log ("rejecting return edge: return to wrong callsite");
353 case SUPEREDGE_INTRAPROCEDURAL_CALL
:
355 const callgraph_superedge
*cg_sedge
356 = as_a
<const callgraph_superedge
*> (succ
);
357 /* Consider turning this edge into a use of an
358 interprocedural summary. */
359 if (eg
.get_analysis_plan ().use_summary_p (cg_sedge
->m_cedge
))
362 logger
->log ("using function summary for %qE in %qE",
363 cg_sedge
->get_callee_decl (),
364 cg_sedge
->get_caller_decl ());
369 /* Otherwise, we ignore these edges */
371 logger
->log ("rejecting interprocedural edge");
380 /* Comparator for program points within the same supernode,
381 for implementing worklist::key_t comparison operators.
382 Return negative if POINT_A is before POINT_B
383 Return positive if POINT_A is after POINT_B
384 Return 0 if they are equal. */
387 function_point::cmp_within_supernode_1 (const function_point
&point_a
,
388 const function_point
&point_b
)
390 gcc_assert (point_a
.get_supernode () == point_b
.get_supernode ());
392 switch (point_a
.m_kind
)
396 case PK_BEFORE_SUPERNODE
:
397 switch (point_b
.m_kind
)
401 case PK_BEFORE_SUPERNODE
:
405 if (point_a
.m_from_edge
)
406 a_src_idx
= point_a
.m_from_edge
->m_src
->m_index
;
407 if (point_b
.m_from_edge
)
408 b_src_idx
= point_b
.m_from_edge
->m_src
->m_index
;
409 return a_src_idx
- b_src_idx
;
414 case PK_AFTER_SUPERNODE
:
419 switch (point_b
.m_kind
)
423 case PK_BEFORE_SUPERNODE
:
427 return point_a
.m_stmt_idx
- point_b
.m_stmt_idx
;
429 case PK_AFTER_SUPERNODE
:
433 case PK_AFTER_SUPERNODE
:
434 switch (point_b
.m_kind
)
438 case PK_BEFORE_SUPERNODE
:
442 case PK_AFTER_SUPERNODE
:
449 /* Comparator for program points within the same supernode,
450 for implementing worklist::key_t comparison operators.
451 Return negative if POINT_A is before POINT_B
452 Return positive if POINT_A is after POINT_B
453 Return 0 if they are equal. */
456 function_point::cmp_within_supernode (const function_point
&point_a
,
457 const function_point
&point_b
)
459 int result
= cmp_within_supernode_1 (point_a
, point_b
);
461 /* Check that the ordering is symmetric */
463 int reversed
= cmp_within_supernode_1 (point_b
, point_a
);
464 gcc_assert (reversed
== -result
);
474 /* Verify that function_point::operator== works as expected. */
477 test_function_point_equality ()
479 const supernode
*snode
= NULL
;
481 function_point a
= function_point (snode
, NULL
, 0,
482 PK_BEFORE_SUPERNODE
);
483 function_point b
= function_point::before_supernode (snode
, NULL
);
487 /* Verify that function_point::cmp_within_supernode works as expected. */
490 test_function_point_ordering ()
492 const supernode
*snode
= NULL
;
493 const call_string call_string
;
495 /* Populate an array with various points within the same
497 auto_vec
<function_point
> points
;
498 points
.safe_push (function_point::before_supernode (snode
, NULL
));
499 points
.safe_push (function_point::before_stmt (snode
, 0));
500 points
.safe_push (function_point::before_stmt (snode
, 1));
501 points
.safe_push (function_point::after_supernode (snode
));
503 /* Check all pairs. */
505 function_point
*point_a
;
506 FOR_EACH_VEC_ELT (points
, i
, point_a
)
509 function_point
*point_b
;
510 FOR_EACH_VEC_ELT (points
, j
, point_b
)
512 int cmp
= function_point::cmp_within_supernode (*point_a
, *point_b
);
516 ASSERT_TRUE (cmp
< 0);
518 ASSERT_TRUE (cmp
> 0);
523 /* Verify that program_point::operator== works as expected. */
526 test_program_point_equality ()
528 const supernode
*snode
= NULL
;
530 const call_string cs
;
532 program_point a
= program_point::before_supernode (snode
, NULL
,
535 program_point b
= program_point::before_supernode (snode
, NULL
,
539 // TODO: verify with non-empty callstrings, with different edges
542 /* Run all of the selftests within this file. */
545 analyzer_program_point_cc_tests ()
547 test_function_point_equality ();
548 test_function_point_ordering ();
549 test_program_point_equality ();
552 } // namespace selftest
554 #endif /* CHECKING_P */
558 #endif /* #if ENABLE_ANALYZER */