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>.
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 "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "tree.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"
29 #include "options.h"
30 #include "cgraph.h"
31 #include "function.h"
32 #include "cfg.h"
33 #include "basic-block.h"
34 #include "gimple.h"
35 #include "gimple-iterator.h"
36 #include "digraph.h"
37 #include "analyzer/analyzer.h"
38 #include "analyzer/analyzer-logging.h"
39 #include "analyzer/supergraph.h"
40 #include "analyzer/program-point.h"
41 #include "sbitmap.h"
42 #include "bitmap.h"
43 #include "tristate.h"
44 #include "selftest.h"
45 #include "analyzer/region-model.h"
46 #include "analyzer/sm.h"
47 #include "analyzer/program-state.h"
48 #include "alloc-pool.h"
49 #include "fibonacci_heap.h"
50 #include "diagnostic-event-id.h"
51 #include "analyzer/pending-diagnostic.h"
52 #include "analyzer/diagnostic-manager.h"
53 #include "shortest-paths.h"
54 #include "analyzer/exploded-graph.h"
55 #include "analyzer/analysis-plan.h"
56
57 #if ENABLE_ANALYZER
58
59 namespace ana {
60
61 /* Get a string for PK. */
62
63 const char *
point_kind_to_string(enum point_kind pk)64 point_kind_to_string (enum point_kind pk)
65 {
66 switch (pk)
67 {
68 default:
69 gcc_unreachable ();
70 case PK_ORIGIN:
71 return "PK_ORIGIN";
72 case PK_BEFORE_SUPERNODE:
73 return "PK_BEFORE_SUPERNODE";
74 case PK_BEFORE_STMT:
75 return "PK_BEFORE_STMT";
76 case PK_AFTER_SUPERNODE:
77 return "PK_AFTER_SUPERNODE";
78 case PK_EMPTY:
79 return "PK_EMPTY";
80 case PK_DELETED:
81 return "PK_DELETED";
82 }
83 }
84
85 /* class function_point. */
86
87 /* Print this function_point to PP. */
88
89 void
print(pretty_printer * pp,const format & f) const90 function_point::print (pretty_printer *pp, const format &f) const
91 {
92 switch (get_kind ())
93 {
94 default:
95 gcc_unreachable ();
96
97 case PK_ORIGIN:
98 pp_printf (pp, "origin");
99 break;
100
101 case PK_BEFORE_SUPERNODE:
102 {
103 if (m_from_edge)
104 pp_printf (pp, "before SN: %i (from SN: %i)",
105 m_supernode->m_index, m_from_edge->m_src->m_index);
106 else
107 pp_printf (pp, "before SN: %i (NULL from-edge)",
108 m_supernode->m_index);
109 f.spacer (pp);
110 for (gphi_iterator gpi
111 = const_cast<supernode *>(get_supernode ())->start_phis ();
112 !gsi_end_p (gpi); gsi_next (&gpi))
113 {
114 const gphi *phi = gpi.phi ();
115 pp_gimple_stmt_1 (pp, phi, 0, (dump_flags_t)0);
116 }
117 }
118 break;
119
120 case PK_BEFORE_STMT:
121 pp_printf (pp, "before (SN: %i stmt: %i): ", m_supernode->m_index,
122 m_stmt_idx);
123 f.spacer (pp);
124 pp_gimple_stmt_1 (pp, get_stmt (), 0, (dump_flags_t)0);
125 if (f.m_newlines)
126 {
127 pp_newline (pp);
128 print_source_line (pp);
129 }
130 break;
131
132 case PK_AFTER_SUPERNODE:
133 pp_printf (pp, "after SN: %i", m_supernode->m_index);
134 break;
135 }
136 }
137
138 /* Generate a hash value for this function_point. */
139
140 hashval_t
hash() const141 function_point::hash () const
142 {
143 inchash::hash hstate;
144 if (m_supernode)
145 hstate.add_int (m_supernode->m_index);
146 hstate.add_ptr (m_from_edge);
147 hstate.add_int (m_stmt_idx);
148 hstate.add_int (m_kind);
149 return hstate.end ();
150 }
151
152 /* Get the gimple stmt for this function_point, if any. */
153
154 const gimple *
get_stmt() const155 function_point::get_stmt () const
156 {
157 if (m_kind == PK_BEFORE_STMT)
158 return m_supernode->m_stmts[m_stmt_idx];
159 else if (m_kind == PK_AFTER_SUPERNODE)
160 return m_supernode->get_last_stmt ();
161 else
162 return NULL;
163 }
164
165 /* Get a location for this function_point, if any. */
166
167 location_t
get_location() const168 function_point::get_location () const
169 {
170 const gimple *stmt = get_stmt ();
171 if (stmt)
172 return stmt->location;
173
174 return UNKNOWN_LOCATION;
175 }
176
177 /* A subclass of diagnostic_context for use by
178 program_point::print_source_line. */
179
180 class debug_diagnostic_context : public diagnostic_context
181 {
182 public:
debug_diagnostic_context()183 debug_diagnostic_context ()
184 {
185 diagnostic_initialize (this, 0);
186 show_line_numbers_p = true;
187 show_caret = true;
188 }
~debug_diagnostic_context()189 ~debug_diagnostic_context ()
190 {
191 diagnostic_finish (this);
192 }
193 };
194
195 /* Print the source line (if any) for this function_point to PP. */
196
197 void
print_source_line(pretty_printer * pp) const198 function_point::print_source_line (pretty_printer *pp) const
199 {
200 const gimple *stmt = get_stmt ();
201 if (!stmt)
202 return;
203 // TODO: monospace font
204 debug_diagnostic_context tmp_dc;
205 gcc_rich_location richloc (stmt->location);
206 diagnostic_show_locus (&tmp_dc, &richloc, DK_ERROR);
207 pp_string (pp, pp_formatted_text (tmp_dc.printer));
208 }
209
210 /* class program_point. */
211
212 /* Print this program_point to PP. */
213
214 void
print(pretty_printer * pp,const format & f) const215 program_point::print (pretty_printer *pp, const format &f) const
216 {
217 pp_string (pp, "callstring: ");
218 m_call_string.print (pp);
219 f.spacer (pp);
220
221 m_function_point.print (pp, f);
222 }
223
224 /* Dump this point to stderr. */
225
226 DEBUG_FUNCTION void
dump() const227 program_point::dump () const
228 {
229 pretty_printer pp;
230 pp_show_color (&pp) = pp_show_color (global_dc->printer);
231 pp.buffer->stream = stderr;
232 print (&pp, format (true));
233 pp_flush (&pp);
234 }
235
236 /* Generate a hash value for this program_point. */
237
238 hashval_t
hash() const239 program_point::hash () const
240 {
241 inchash::hash hstate;
242 hstate.merge_hash (m_function_point.hash ());
243 hstate.merge_hash (m_call_string.hash ());
244 return hstate.end ();
245 }
246
247 /* Get the function * at DEPTH within the call stack. */
248
249 function *
get_function_at_depth(unsigned depth) const250 program_point::get_function_at_depth (unsigned depth) const
251 {
252 gcc_assert (depth <= m_call_string.length ());
253 if (depth == m_call_string.length ())
254 return m_function_point.get_function ();
255 else
256 return m_call_string[depth]->get_caller_function ();
257 }
258
259 /* Assert that this object is sane. */
260
261 void
validate() const262 program_point::validate () const
263 {
264 /* Skip this in a release build. */
265 #if !CHECKING_P
266 return;
267 #endif
268
269 m_call_string.validate ();
270 /* The "callee" of the final entry in the callstring should be the
271 function of the m_function_point. */
272 if (m_call_string.length () > 0)
273 gcc_assert (m_call_string[m_call_string.length () - 1]->get_callee_function ()
274 == get_function ());
275 }
276
277 /* Check to see if SUCC is a valid edge to take (ensuring that we have
278 interprocedurally valid paths in the exploded graph, and enforcing
279 recursion limits).
280
281 Update the call string if SUCC is a call or a return.
282
283 Return true if SUCC can be taken, or false otherwise.
284
285 This is the "point" half of exploded_node::on_edge. */
286
287 bool
on_edge(exploded_graph & eg,const superedge * succ)288 program_point::on_edge (exploded_graph &eg,
289 const superedge *succ)
290 {
291 logger * const logger = eg.get_logger ();
292 LOG_FUNC (logger);
293 switch (succ->m_kind)
294 {
295 case SUPEREDGE_CFG_EDGE:
296 {
297 const cfg_superedge *cfg_sedge = as_a <const cfg_superedge *> (succ);
298
299 /* Reject abnormal edges; we special-case setjmp/longjmp. */
300 if (cfg_sedge->get_flags () & EDGE_ABNORMAL)
301 return false;
302 }
303 break;
304
305 case SUPEREDGE_CALL:
306 {
307 const call_superedge *call_sedge = as_a <const call_superedge *> (succ);
308
309 if (eg.get_analysis_plan ().use_summary_p (call_sedge->m_cedge))
310 {
311 if (logger)
312 logger->log ("rejecting call edge: using summary instead");
313 return false;
314 }
315
316 /* Add the callsite to the call string. */
317 m_call_string.push_call (eg.get_supergraph (), call_sedge);
318
319 /* Impose a maximum recursion depth and don't analyze paths
320 that exceed it further.
321 This is something of a blunt workaround, but it only
322 applies to recursion (and mutual recursion), not to
323 general call stacks. */
324 if (m_call_string.calc_recursion_depth ()
325 > param_analyzer_max_recursion_depth)
326 {
327 if (logger)
328 logger->log ("rejecting call edge: recursion limit exceeded");
329 // TODO: issue a sorry for this?
330 return false;
331 }
332 }
333 break;
334
335 case SUPEREDGE_RETURN:
336 {
337 /* Require that we return to the call site in the call string. */
338 if (m_call_string.empty_p ())
339 {
340 if (logger)
341 logger->log ("rejecting return edge: empty call string");
342 return false;
343 }
344 const return_superedge *top_of_stack = m_call_string.pop ();
345 if (top_of_stack != succ)
346 {
347 if (logger)
348 logger->log ("rejecting return edge: return to wrong callsite");
349 return false;
350 }
351 }
352 break;
353
354 case SUPEREDGE_INTRAPROCEDURAL_CALL:
355 {
356 const callgraph_superedge *cg_sedge
357 = as_a <const callgraph_superedge *> (succ);
358 /* Consider turning this edge into a use of an
359 interprocedural summary. */
360 if (eg.get_analysis_plan ().use_summary_p (cg_sedge->m_cedge))
361 {
362 if (logger)
363 logger->log ("using function summary for %qE in %qE",
364 cg_sedge->get_callee_decl (),
365 cg_sedge->get_caller_decl ());
366 return true;
367 }
368 else
369 {
370 /* Otherwise, we ignore these edges */
371 if (logger)
372 logger->log ("rejecting interprocedural edge");
373 return false;
374 }
375 }
376 }
377
378 return true;
379 }
380
381 /* Comparator for program points within the same supernode,
382 for implementing worklist::key_t comparison operators.
383 Return negative if POINT_A is before POINT_B
384 Return positive if POINT_A is after POINT_B
385 Return 0 if they are equal. */
386
387 int
cmp_within_supernode_1(const function_point & point_a,const function_point & point_b)388 function_point::cmp_within_supernode_1 (const function_point &point_a,
389 const function_point &point_b)
390 {
391 gcc_assert (point_a.get_supernode () == point_b.get_supernode ());
392
393 switch (point_a.m_kind)
394 {
395 default:
396 gcc_unreachable ();
397 case PK_BEFORE_SUPERNODE:
398 switch (point_b.m_kind)
399 {
400 default:
401 gcc_unreachable ();
402 case PK_BEFORE_SUPERNODE:
403 {
404 int a_src_idx = -1;
405 int b_src_idx = -1;
406 if (point_a.m_from_edge)
407 a_src_idx = point_a.m_from_edge->m_src->m_index;
408 if (point_b.m_from_edge)
409 b_src_idx = point_b.m_from_edge->m_src->m_index;
410 return a_src_idx - b_src_idx;
411 }
412 break;
413
414 case PK_BEFORE_STMT:
415 case PK_AFTER_SUPERNODE:
416 return -1;
417 }
418 break;
419 case PK_BEFORE_STMT:
420 switch (point_b.m_kind)
421 {
422 default:
423 gcc_unreachable ();
424 case PK_BEFORE_SUPERNODE:
425 return 1;
426
427 case PK_BEFORE_STMT:
428 return point_a.m_stmt_idx - point_b.m_stmt_idx;
429
430 case PK_AFTER_SUPERNODE:
431 return -1;
432 }
433 break;
434 case PK_AFTER_SUPERNODE:
435 switch (point_b.m_kind)
436 {
437 default:
438 gcc_unreachable ();
439 case PK_BEFORE_SUPERNODE:
440 case PK_BEFORE_STMT:
441 return 1;
442
443 case PK_AFTER_SUPERNODE:
444 return 0;
445 }
446 break;
447 }
448 }
449
450 /* Comparator for program points within the same supernode,
451 for implementing worklist::key_t comparison operators.
452 Return negative if POINT_A is before POINT_B
453 Return positive if POINT_A is after POINT_B
454 Return 0 if they are equal. */
455
456 int
cmp_within_supernode(const function_point & point_a,const function_point & point_b)457 function_point::cmp_within_supernode (const function_point &point_a,
458 const function_point &point_b)
459 {
460 int result = cmp_within_supernode_1 (point_a, point_b);
461
462 /* Check that the ordering is symmetric */
463 #if CHECKING_P
464 int reversed = cmp_within_supernode_1 (point_b, point_a);
465 gcc_assert (reversed == -result);
466 #endif
467
468 return result;
469 }
470
471 #if CHECKING_P
472
473 namespace selftest {
474
475 /* Verify that function_point::operator== works as expected. */
476
477 static void
test_function_point_equality()478 test_function_point_equality ()
479 {
480 const supernode *snode = NULL;
481
482 function_point a = function_point (snode, NULL, 0,
483 PK_BEFORE_SUPERNODE);
484 function_point b = function_point::before_supernode (snode, NULL);
485 ASSERT_EQ (a, b);
486 }
487
488 /* Verify that function_point::cmp_within_supernode works as expected. */
489
490 static void
test_function_point_ordering()491 test_function_point_ordering ()
492 {
493 const supernode *snode = NULL;
494 const call_string call_string;
495
496 /* Populate an array with various points within the same
497 snode, in order. */
498 auto_vec<function_point> points;
499 points.safe_push (function_point::before_supernode (snode, NULL));
500 points.safe_push (function_point::before_stmt (snode, 0));
501 points.safe_push (function_point::before_stmt (snode, 1));
502 points.safe_push (function_point::after_supernode (snode));
503
504 /* Check all pairs. */
505 unsigned i;
506 function_point *point_a;
507 FOR_EACH_VEC_ELT (points, i, point_a)
508 {
509 unsigned j;
510 function_point *point_b;
511 FOR_EACH_VEC_ELT (points, j, point_b)
512 {
513 int cmp = function_point::cmp_within_supernode (*point_a, *point_b);
514 if (i == j)
515 ASSERT_EQ (cmp, 0);
516 if (i < j)
517 ASSERT_TRUE (cmp < 0);
518 if (i > j)
519 ASSERT_TRUE (cmp > 0);
520 }
521 }
522 }
523
524 /* Verify that program_point::operator== works as expected. */
525
526 static void
test_program_point_equality()527 test_program_point_equality ()
528 {
529 const supernode *snode = NULL;
530
531 const call_string cs;
532
533 program_point a = program_point::before_supernode (snode, NULL,
534 cs);
535
536 program_point b = program_point::before_supernode (snode, NULL,
537 cs);
538
539 ASSERT_EQ (a, b);
540 // TODO: verify with non-empty callstrings, with different edges
541 }
542
543 /* Run all of the selftests within this file. */
544
545 void
analyzer_program_point_cc_tests()546 analyzer_program_point_cc_tests ()
547 {
548 test_function_point_equality ();
549 test_function_point_ordering ();
550 test_program_point_equality ();
551 }
552
553 } // namespace selftest
554
555 #endif /* CHECKING_P */
556
557 } // namespace ana
558
559 #endif /* #if ENABLE_ANALYZER */
560