xref: /netbsd-src/external/gpl3/gcc/dist/gcc/analyzer/program-point.cc (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /* Classes for representing locations within the program.
2    Copyright (C) 2019-2022 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 "json.h"
28 #include "analyzer/call-string.h"
29 #include "ordered-hash-map.h"
30 #include "options.h"
31 #include "cgraph.h"
32 #include "function.h"
33 #include "cfg.h"
34 #include "basic-block.h"
35 #include "gimple.h"
36 #include "gimple-iterator.h"
37 #include "digraph.h"
38 #include "analyzer/analyzer.h"
39 #include "analyzer/analyzer-logging.h"
40 #include "analyzer/supergraph.h"
41 #include "analyzer/program-point.h"
42 #include "sbitmap.h"
43 #include "bitmap.h"
44 #include "tristate.h"
45 #include "selftest.h"
46 #include "analyzer/store.h"
47 #include "analyzer/region-model.h"
48 #include "analyzer/sm.h"
49 #include "analyzer/program-state.h"
50 #include "alloc-pool.h"
51 #include "fibonacci_heap.h"
52 #include "diagnostic-event-id.h"
53 #include "analyzer/pending-diagnostic.h"
54 #include "analyzer/diagnostic-manager.h"
55 #include "shortest-paths.h"
56 #include "analyzer/exploded-graph.h"
57 #include "analyzer/analysis-plan.h"
58 
59 #if ENABLE_ANALYZER
60 
61 namespace ana {
62 
63 /* Get a string for PK.  */
64 
65 const char *
point_kind_to_string(enum point_kind pk)66 point_kind_to_string (enum point_kind pk)
67 {
68   switch (pk)
69     {
70     default:
71       gcc_unreachable ();
72     case PK_ORIGIN:
73       return "PK_ORIGIN";
74     case PK_BEFORE_SUPERNODE:
75       return "PK_BEFORE_SUPERNODE";
76     case PK_BEFORE_STMT:
77       return "PK_BEFORE_STMT";
78     case PK_AFTER_SUPERNODE:
79       return "PK_AFTER_SUPERNODE";
80     case PK_EMPTY:
81       return "PK_EMPTY";
82     case PK_DELETED:
83       return "PK_DELETED";
84     }
85 }
86 
87 /* class function_point.  */
88 
function_point(const supernode * supernode,const superedge * from_edge,unsigned stmt_idx,enum point_kind kind)89 function_point::function_point (const supernode *supernode,
90 				const superedge *from_edge,
91 				unsigned stmt_idx,
92 				enum point_kind kind)
93 : m_supernode (supernode), m_from_edge (from_edge),
94   m_stmt_idx (stmt_idx), m_kind (kind)
95 {
96   if (from_edge)
97     {
98       gcc_checking_assert (m_kind == PK_BEFORE_SUPERNODE);
99       gcc_checking_assert (from_edge->get_kind () == SUPEREDGE_CFG_EDGE);
100     }
101   if (stmt_idx)
102     gcc_checking_assert (m_kind == PK_BEFORE_STMT);
103 }
104 
105 /* Print this function_point to PP.  */
106 
107 void
print(pretty_printer * pp,const format & f) const108 function_point::print (pretty_printer *pp, const format &f) const
109 {
110   switch (get_kind ())
111     {
112     default:
113       gcc_unreachable ();
114 
115     case PK_ORIGIN:
116       pp_printf (pp, "origin");
117       if (f.m_newlines)
118 	pp_newline (pp);
119       break;
120 
121     case PK_BEFORE_SUPERNODE:
122       {
123 	if (m_from_edge)
124 	  {
125 	    if (basic_block bb = m_from_edge->m_src->m_bb)
126 	      pp_printf (pp, "before SN: %i (from SN: %i (bb: %i))",
127 			 m_supernode->m_index, m_from_edge->m_src->m_index,
128 			 bb->index);
129 	    else
130 	      pp_printf (pp, "before SN: %i (from SN: %i)",
131 			 m_supernode->m_index, m_from_edge->m_src->m_index);
132 	  }
133 	else
134 	  pp_printf (pp, "before SN: %i (NULL from-edge)",
135 		     m_supernode->m_index);
136 	f.spacer (pp);
137 	for (gphi_iterator gpi
138 	       = const_cast<supernode *>(get_supernode ())->start_phis ();
139 	     !gsi_end_p (gpi); gsi_next (&gpi))
140 	  {
141 	    const gphi *phi = gpi.phi ();
142 	    pp_gimple_stmt_1 (pp, phi, 0, (dump_flags_t)0);
143 	  }
144       }
145       break;
146 
147     case PK_BEFORE_STMT:
148       pp_printf (pp, "before (SN: %i stmt: %i): ", m_supernode->m_index,
149 		 m_stmt_idx);
150       f.spacer (pp);
151       pp_gimple_stmt_1 (pp, get_stmt (), 0, (dump_flags_t)0);
152       if (f.m_newlines)
153 	{
154 	  pp_newline (pp);
155 	  print_source_line (pp);
156 	}
157       break;
158 
159     case PK_AFTER_SUPERNODE:
160       pp_printf (pp, "after SN: %i", m_supernode->m_index);
161       if (f.m_newlines)
162 	pp_newline (pp);
163       break;
164     }
165 }
166 
167 /* Generate a hash value for this function_point.  */
168 
169 hashval_t
hash() const170 function_point::hash () const
171 {
172   inchash::hash hstate;
173   if (m_supernode)
174     hstate.add_int (m_supernode->m_index);
175   hstate.add_ptr (m_from_edge);
176   hstate.add_int (m_stmt_idx);
177   hstate.add_int (m_kind);
178   return hstate.end ();
179 }
180 
181 /* Get the function at this point, if any.  */
182 
183 function *
get_function() const184 function_point::get_function () const
185 {
186   if (m_supernode)
187     return m_supernode->m_fun;
188   else
189     return NULL;
190 }
191 
192 /* Get the gimple stmt for this function_point, if any.  */
193 
194 const gimple *
get_stmt() const195 function_point::get_stmt () const
196 {
197   if (m_kind == PK_BEFORE_STMT)
198     return m_supernode->m_stmts[m_stmt_idx];
199   else if (m_kind == PK_AFTER_SUPERNODE)
200     return m_supernode->get_last_stmt ();
201   else
202     return NULL;
203 }
204 
205 /* Get a location for this function_point, if any.  */
206 
207 location_t
get_location() const208 function_point::get_location () const
209 {
210   const gimple *stmt = get_stmt ();
211   if (stmt)
212     return stmt->location;
213   if (m_kind == PK_BEFORE_SUPERNODE)
214     return m_supernode->get_start_location ();
215   else if (m_kind == PK_AFTER_SUPERNODE)
216     return m_supernode->get_end_location ();
217   else
218     return UNKNOWN_LOCATION;
219 }
220 
221 /* Return true if this function_point is a before_stmt for
222    the final stmt in its supernode.  */
223 
224 bool
final_stmt_p() const225 function_point::final_stmt_p () const
226 {
227   if (m_kind != PK_BEFORE_STMT)
228     return false;
229   return m_stmt_idx == get_supernode ()->m_stmts.length () - 1;
230 }
231 
232 /* Create a function_point representing the entrypoint of function FUN.  */
233 
234 function_point
from_function_entry(const supergraph & sg,function * fun)235 function_point::from_function_entry (const supergraph &sg, function *fun)
236 {
237   return before_supernode (sg.get_node_for_function_entry (fun), NULL);
238 }
239 
240 /* Create a function_point representing entering supernode SUPERNODE,
241    having reached it via FROM_EDGE (which could be NULL).  */
242 
243 function_point
before_supernode(const supernode * supernode,const superedge * from_edge)244 function_point::before_supernode (const supernode *supernode,
245 				  const superedge *from_edge)
246 {
247   if (from_edge && from_edge->get_kind () != SUPEREDGE_CFG_EDGE)
248     from_edge = NULL;
249   return function_point (supernode, from_edge, 0, PK_BEFORE_SUPERNODE);
250 }
251 
252 /* A subclass of diagnostic_context for use by
253    program_point::print_source_line.  */
254 
255 class debug_diagnostic_context : public diagnostic_context
256 {
257 public:
debug_diagnostic_context()258   debug_diagnostic_context ()
259   {
260     diagnostic_initialize (this, 0);
261     show_line_numbers_p = true;
262     show_caret = true;
263   }
~debug_diagnostic_context()264   ~debug_diagnostic_context ()
265   {
266     diagnostic_finish (this);
267   }
268 };
269 
270 /* Print the source line (if any) for this function_point to PP.  */
271 
272 void
print_source_line(pretty_printer * pp) const273 function_point::print_source_line (pretty_printer *pp) const
274 {
275   const gimple *stmt = get_stmt ();
276   if (!stmt)
277     return;
278   // TODO: monospace font
279   debug_diagnostic_context tmp_dc;
280   gcc_rich_location richloc (stmt->location);
281   diagnostic_show_locus (&tmp_dc, &richloc, DK_ERROR);
282   pp_string (pp, pp_formatted_text (tmp_dc.printer));
283 }
284 
285 /* class program_point.  */
286 
287 /* Print this program_point to PP.  */
288 
289 void
print(pretty_printer * pp,const format & f) const290 program_point::print (pretty_printer *pp, const format &f) const
291 {
292   pp_string (pp, "callstring: ");
293   m_call_string.print (pp);
294   f.spacer (pp);
295 
296   m_function_point.print (pp, f);
297 }
298 
299 /* Dump this point to stderr.  */
300 
301 DEBUG_FUNCTION void
dump() const302 program_point::dump () const
303 {
304   pretty_printer pp;
305   pp_show_color (&pp) = pp_show_color (global_dc->printer);
306   pp.buffer->stream = stderr;
307   print (&pp, format (true));
308   pp_flush (&pp);
309 }
310 
311 /* Return a new json::object of the form
312    {"kind"  : str,
313     "snode_idx" : int (optional), the index of the supernode,
314     "from_edge_snode_idx" : int (only for kind=='PK_BEFORE_SUPERNODE'),
315     "stmt_idx": int (only for kind=='PK_BEFORE_STMT',
316     "call_string": object for the call_string}.  */
317 
318 json::object *
to_json() const319 program_point::to_json () const
320 {
321   json::object *point_obj = new json::object ();
322 
323   point_obj->set ("kind",
324 		  new json::string (point_kind_to_string (get_kind ())));
325 
326   if (get_supernode ())
327     point_obj->set ("snode_idx",
328 		    new json::integer_number (get_supernode ()->m_index));
329 
330   switch (get_kind ())
331     {
332     default: break;
333     case PK_BEFORE_SUPERNODE:
334       if (const superedge *sedge = get_from_edge ())
335 	point_obj->set ("from_edge_snode_idx",
336 			new json::integer_number (sedge->m_src->m_index));
337       break;
338     case PK_BEFORE_STMT:
339       point_obj->set ("stmt_idx", new json::integer_number (get_stmt_idx ()));
340       break;
341     }
342 
343   point_obj->set ("call_string", m_call_string.to_json ());
344 
345   return point_obj;
346 }
347 
348 /* Update the callstack to represent a call from caller to callee.
349 
350    Generally used to push a custom call to a perticular program point
351    where we don't have a superedge representing the call.  */
352 void
push_to_call_stack(const supernode * caller,const supernode * callee)353 program_point::push_to_call_stack (const supernode *caller,
354 				   const supernode *callee)
355 {
356   m_call_string.push_call (callee, caller);
357 }
358 
359 /* Pop the topmost call from the current callstack.  */
360 void
pop_from_call_stack()361 program_point::pop_from_call_stack ()
362 {
363   m_call_string.pop ();
364 }
365 
366 /* Generate a hash value for this program_point.  */
367 
368 hashval_t
hash() const369 program_point::hash () const
370 {
371   inchash::hash hstate;
372   hstate.merge_hash (m_function_point.hash ());
373   hstate.merge_hash (m_call_string.hash ());
374   return hstate.end ();
375 }
376 
377 /* Get the function * at DEPTH within the call stack.  */
378 
379 function *
get_function_at_depth(unsigned depth) const380 program_point::get_function_at_depth (unsigned depth) const
381 {
382   gcc_assert (depth <= m_call_string.length ());
383   if (depth == m_call_string.length ())
384     return m_function_point.get_function ();
385   else
386     return m_call_string[depth].get_caller_function ();
387 }
388 
389 /* Assert that this object is sane.  */
390 
391 void
validate() const392 program_point::validate () const
393 {
394   /* Skip this in a release build.  */
395 #if !CHECKING_P
396   return;
397 #endif
398 
399   m_call_string.validate ();
400   /* The "callee" of the final entry in the callstring should be the
401      function of the m_function_point.  */
402   if (m_call_string.length () > 0)
403     gcc_assert (m_call_string[m_call_string.length () - 1].get_callee_function ()
404 		== get_function ());
405 }
406 
407 /* Check to see if SUCC is a valid edge to take (ensuring that we have
408    interprocedurally valid paths in the exploded graph, and enforcing
409    recursion limits).
410 
411    Update the call string if SUCC is a call or a return.
412 
413    Return true if SUCC can be taken, or false otherwise.
414 
415    This is the "point" half of exploded_node::on_edge.  */
416 
417 bool
on_edge(exploded_graph & eg,const superedge * succ)418 program_point::on_edge (exploded_graph &eg,
419 			const superedge *succ)
420 {
421   logger * const logger = eg.get_logger ();
422   LOG_FUNC (logger);
423   switch (succ->m_kind)
424     {
425     case SUPEREDGE_CFG_EDGE:
426       {
427 	const cfg_superedge *cfg_sedge = as_a <const cfg_superedge *> (succ);
428 
429 	/* Reject abnormal edges; we special-case setjmp/longjmp.  */
430 	if (cfg_sedge->get_flags () & EDGE_ABNORMAL)
431 	  return false;
432       }
433       break;
434 
435     case SUPEREDGE_CALL:
436       {
437 	const call_superedge *call_sedge = as_a <const call_superedge *> (succ);
438 
439 	if (eg.get_analysis_plan ().use_summary_p (call_sedge->m_cedge))
440 	  {
441 	    if (logger)
442 	      logger->log ("rejecting call edge: using summary instead");
443 	    return false;
444 	  }
445 
446 	/* Add the callsite to the call string.  */
447 	m_call_string.push_call (eg.get_supergraph (), call_sedge);
448 
449 	/* Impose a maximum recursion depth and don't analyze paths
450 	   that exceed it further.
451 	   This is something of a blunt workaround, but it only
452 	   applies to recursion (and mutual recursion), not to
453 	   general call stacks.  */
454 	if (m_call_string.calc_recursion_depth ()
455 	    > param_analyzer_max_recursion_depth)
456 	  {
457 	    if (logger)
458 	      logger->log ("rejecting call edge: recursion limit exceeded");
459 	    // TODO: issue a sorry for this?
460 	    return false;
461 	  }
462       }
463       break;
464 
465     case SUPEREDGE_RETURN:
466       {
467 	/* Require that we return to the call site in the call string.  */
468 	if (m_call_string.empty_p ())
469 	  {
470 	    if (logger)
471 	      logger->log ("rejecting return edge: empty call string");
472 	    return false;
473 	  }
474 	const call_string::element_t top_of_stack = m_call_string.pop ();
475 	call_string::element_t current_call_string_element (succ->m_dest,
476 							    succ->m_src);
477 	if (top_of_stack != current_call_string_element)
478 	  {
479 	    if (logger)
480 	      logger->log ("rejecting return edge: return to wrong callsite");
481 	    return false;
482 	  }
483       }
484       break;
485 
486     case SUPEREDGE_INTRAPROCEDURAL_CALL:
487       {
488 	const callgraph_superedge *cg_sedge
489 	  = as_a <const callgraph_superedge *> (succ);
490 	/* Consider turning this edge into a use of an
491 	   interprocedural summary.  */
492 	if (eg.get_analysis_plan ().use_summary_p (cg_sedge->m_cedge))
493 	  {
494 	    if (logger)
495 	      logger->log ("using function summary for %qE in %qE",
496 			   cg_sedge->get_callee_decl (),
497 			   cg_sedge->get_caller_decl ());
498 	    return true;
499 	  }
500 	else
501 	  {
502 	    /* Otherwise, we ignore these edges  */
503 	    if (logger)
504 	      logger->log ("rejecting interprocedural edge");
505 	    return false;
506 	  }
507       }
508     }
509 
510   return true;
511 }
512 
513 /* Comparator for program points within the same supernode,
514    for implementing worklist::key_t comparison operators.
515    Return negative if POINT_A is before POINT_B
516    Return positive if POINT_A is after POINT_B
517    Return 0 if they are equal.  */
518 
519 int
cmp_within_supernode_1(const function_point & point_a,const function_point & point_b)520 function_point::cmp_within_supernode_1 (const function_point &point_a,
521 					const function_point &point_b)
522 {
523   gcc_assert (point_a.get_supernode () == point_b.get_supernode ());
524 
525   switch (point_a.m_kind)
526     {
527     default:
528       gcc_unreachable ();
529     case PK_BEFORE_SUPERNODE:
530       switch (point_b.m_kind)
531 	{
532 	default:
533 	  gcc_unreachable ();
534 	case PK_BEFORE_SUPERNODE:
535 	  {
536 	    int a_src_idx = -1;
537 	    int b_src_idx = -1;
538 	    if (point_a.m_from_edge)
539 	      a_src_idx = point_a.m_from_edge->m_src->m_index;
540 	    if (point_b.m_from_edge)
541 	      b_src_idx = point_b.m_from_edge->m_src->m_index;
542 	    return a_src_idx - b_src_idx;
543 	  }
544 	  break;
545 
546 	case PK_BEFORE_STMT:
547 	case PK_AFTER_SUPERNODE:
548 	  return -1;
549 	}
550       break;
551     case PK_BEFORE_STMT:
552       switch (point_b.m_kind)
553 	{
554 	default:
555 	  gcc_unreachable ();
556 	case PK_BEFORE_SUPERNODE:
557 	  return 1;
558 
559 	case PK_BEFORE_STMT:
560 	  return point_a.m_stmt_idx - point_b.m_stmt_idx;
561 
562 	case PK_AFTER_SUPERNODE:
563 	  return -1;
564 	}
565       break;
566     case PK_AFTER_SUPERNODE:
567       switch (point_b.m_kind)
568 	{
569 	default:
570 	  gcc_unreachable ();
571 	case PK_BEFORE_SUPERNODE:
572 	case PK_BEFORE_STMT:
573 	  return 1;
574 
575 	case PK_AFTER_SUPERNODE:
576 	  return 0;
577 	}
578       break;
579     }
580 }
581 
582 /* Comparator for program points within the same supernode,
583    for implementing worklist::key_t comparison operators.
584    Return negative if POINT_A is before POINT_B
585    Return positive if POINT_A is after POINT_B
586    Return 0 if they are equal.  */
587 
588 int
cmp_within_supernode(const function_point & point_a,const function_point & point_b)589 function_point::cmp_within_supernode (const function_point &point_a,
590 				      const function_point &point_b)
591 {
592   int result = cmp_within_supernode_1 (point_a, point_b);
593 
594   /* Check that the ordering is symmetric  */
595 #if CHECKING_P
596   int reversed = cmp_within_supernode_1 (point_b, point_a);
597   gcc_assert (reversed == -result);
598 #endif
599 
600   return result;
601 }
602 
603 /* Comparator for imposing an order on function_points.  */
604 
605 int
cmp(const function_point & point_a,const function_point & point_b)606 function_point::cmp (const function_point &point_a,
607 		     const function_point &point_b)
608 {
609   int idx_a = point_a.m_supernode ? point_a.m_supernode->m_index : -1;
610   int idx_b = point_b.m_supernode ? point_b.m_supernode->m_index : -1;
611   if (int cmp_idx = idx_a - idx_b)
612     return cmp_idx;
613   gcc_assert (point_a.m_supernode == point_b.m_supernode);
614   if (point_a.m_supernode)
615     return cmp_within_supernode (point_a, point_b);
616   else
617     return 0;
618 }
619 
620 /* Comparator for use by vec<function_point>::qsort.  */
621 
622 int
cmp_ptr(const void * p1,const void * p2)623 function_point::cmp_ptr (const void *p1, const void *p2)
624 {
625   const function_point *fp1 = (const function_point *)p1;
626   const function_point *fp2 = (const function_point *)p2;
627   return cmp (*fp1, *fp2);
628 }
629 
630 /* For PK_BEFORE_STMT, go to next stmt (or to PK_AFTER_SUPERNODE).  */
631 
632 void
next_stmt()633 function_point::next_stmt ()
634 {
635   gcc_assert (m_kind == PK_BEFORE_STMT);
636   if (++m_stmt_idx == m_supernode->m_stmts.length ())
637     {
638       m_kind = PK_AFTER_SUPERNODE;
639       m_stmt_idx = 0;
640     }
641 }
642 
643 /* For those function points for which there is a uniquely-defined
644    successor, return it.  */
645 
646 function_point
get_next() const647 function_point::get_next () const
648 {
649   switch (get_kind ())
650     {
651     default:
652       gcc_unreachable ();
653     case PK_ORIGIN:
654     case PK_AFTER_SUPERNODE:
655       gcc_unreachable (); /* Not uniquely defined.  */
656     case PK_BEFORE_SUPERNODE:
657       if (get_supernode ()->m_stmts.length () > 0)
658 	return before_stmt (get_supernode (), 0);
659       else
660 	return after_supernode (get_supernode ());
661     case PK_BEFORE_STMT:
662       {
663 	unsigned next_idx = get_stmt_idx () + 1;
664 	if (next_idx < get_supernode ()->m_stmts.length ())
665 	  return before_stmt (get_supernode (), next_idx);
666 	else
667 	  return after_supernode (get_supernode ());
668       }
669     }
670 }
671 
672 /* For those program points for which there is a uniquely-defined
673    successor, return it.  */
674 
675 program_point
get_next() const676 program_point::get_next () const
677 {
678   switch (m_function_point.get_kind ())
679     {
680     default:
681       gcc_unreachable ();
682     case PK_ORIGIN:
683     case PK_AFTER_SUPERNODE:
684       gcc_unreachable (); /* Not uniquely defined.  */
685     case PK_BEFORE_SUPERNODE:
686       if (get_supernode ()->m_stmts.length () > 0)
687 	return before_stmt (get_supernode (), 0, get_call_string ());
688       else
689 	return after_supernode (get_supernode (), get_call_string ());
690     case PK_BEFORE_STMT:
691       {
692 	unsigned next_idx = get_stmt_idx () + 1;
693 	if (next_idx < get_supernode ()->m_stmts.length ())
694 	  return before_stmt (get_supernode (), next_idx, get_call_string ());
695 	else
696 	  return after_supernode (get_supernode (), get_call_string ());
697       }
698     }
699 }
700 
701 #if CHECKING_P
702 
703 namespace selftest {
704 
705 /* Verify that function_point::operator== works as expected.  */
706 
707 static void
test_function_point_equality()708 test_function_point_equality ()
709 {
710   const supernode *snode = NULL;
711 
712   function_point a = function_point (snode, NULL, 0,
713 				     PK_BEFORE_SUPERNODE);
714   function_point b = function_point::before_supernode (snode, NULL);
715   ASSERT_EQ (a, b);
716 }
717 
718 /* Verify that function_point::cmp_within_supernode works as expected.  */
719 
720 static void
test_function_point_ordering()721 test_function_point_ordering ()
722 {
723   const supernode *snode = NULL;
724   const call_string call_string;
725 
726   /* Populate an array with various points within the same
727      snode, in order.  */
728   auto_vec<function_point> points;
729   points.safe_push (function_point::before_supernode (snode, NULL));
730   points.safe_push (function_point::before_stmt (snode, 0));
731   points.safe_push (function_point::before_stmt (snode, 1));
732   points.safe_push (function_point::after_supernode (snode));
733 
734   /* Check all pairs.  */
735   unsigned i;
736   function_point *point_a;
737   FOR_EACH_VEC_ELT (points, i, point_a)
738     {
739       unsigned j;
740       function_point *point_b;
741       FOR_EACH_VEC_ELT (points, j, point_b)
742 	{
743 	  int cmp = function_point::cmp_within_supernode (*point_a, *point_b);
744 	  if (i == j)
745 	    ASSERT_EQ (cmp, 0);
746 	  if (i < j)
747 	    ASSERT_TRUE (cmp < 0);
748 	  if (i > j)
749 	    ASSERT_TRUE (cmp > 0);
750 	}
751     }
752 }
753 
754 /* Verify that program_point::operator== works as expected.  */
755 
756 static void
test_program_point_equality()757 test_program_point_equality ()
758 {
759   const supernode *snode = NULL;
760 
761   const call_string cs;
762 
763   program_point a = program_point::before_supernode (snode, NULL,
764 						     cs);
765 
766   program_point b = program_point::before_supernode (snode, NULL,
767 						     cs);
768 
769   ASSERT_EQ (a, b);
770   // TODO: verify with non-empty callstrings, with different edges
771 }
772 
773 /* Run all of the selftests within this file.  */
774 
775 void
analyzer_program_point_cc_tests()776 analyzer_program_point_cc_tests ()
777 {
778   test_function_point_equality ();
779   test_function_point_ordering ();
780   test_program_point_equality ();
781 }
782 
783 } // namespace selftest
784 
785 #endif /* CHECKING_P */
786 
787 } // namespace ana
788 
789 #endif /* #if ENABLE_ANALYZER */
790