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