aboutsummaryrefslogtreecommitdiff
path: root/gcc/c-call-graph.c
blob: 26a7141a0132a6ca0e0ea2c4bd832df2bdd77480 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
/* Construction of the function call graph.
   Copyright (C) 2002, 2003 Free Software Foundation, Inc.
   Contributed by Sebastian Pop <s.pop@laposte.net>

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2, or (at your option) any later
version.

GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING.  If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.  */

#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
#include "c-tree.h"
#include "c-common.h"
#include "hard-reg-set.h"
#include "basic-block.h"
#include "tree-flow.h"
#include "diagnostic.h"


/* Static declarations.  */
static void construct_call_graph (pretty_printer *, tree, HOST_WIDE_INT);
static void print_callee (pretty_printer *, tree, int);

#define INDENT(SPACE) do { \
  int i; for (i = 0; i<SPACE; i++) pp_space (buffer); } while (0)
#define NIY do { \
  pp_flush (buffer); debug_tree (node); abort (); } while (0)


/* Print the call graph associated to the tree T, in the file FILE.  */

void
print_call_graph (FILE *file, tree t)
{
  pretty_printer buffer_rec;
  pretty_printer *buffer = &buffer_rec;

  pp_construct (buffer, /* prefix */NULL, /* line-width */0);
  pp_clear_output_area (buffer);
  pp_printf (buffer, "<file>\n");
  construct_call_graph (buffer, t, 0);
  pp_printf (buffer, "</file>\n");
  fprintf (file, "%s", pp_base_formatted_text (buffer));
}

/* Print the call graph on stderr.  */

void
debug_call_graph (tree t)
{
  print_call_graph (stderr, t);
}


/* Scan the tree T searching for callee/caller functions, then output found
   function calls/callers in the pretty_printer BUFFER under the DTD format
   described above.
   Not Yet Implemented : the dump of global variables and their use.  */

static void
construct_call_graph (pretty_printer *buffer, tree t, HOST_WIDE_INT spc)
{
  static unsigned int decision_points;
  static unsigned int nb_statements;
  static unsigned int nb_calls;
  tree node = t;

  while (node && node != error_mark_node)
    {
      enum tree_code code = TREE_CODE (node);
#if 0
      if (is_ctrl_stmt (node)) decision_points++;
      if (is_exec_stmt (node)) nb_statements++;
#endif

      switch (code)
	{
	case TYPE_DECL:
	case FIELD_DECL:
	case VAR_DECL:
	case PARM_DECL:
	  /* Some nodes on which we need to stop the recursion.  */
	  return;

	case FUNCTION_DECL:
	  if (BUILT_IN_FRONTEND)

	  INDENT (spc);
	  pp_printf (buffer, "<caller id = \"");
	  pp_printf (buffer, get_name (node), 0);
	  pp_printf (buffer, "\">\n");

	  /* What about definition of nested functions?  That will reset the
	     current value of decision_points and nb_statements...  */
	  decision_points = 0;
	  nb_statements = 0;
	  nb_calls = 0;
	  construct_call_graph (buffer, DECL_SAVED_TREE (node), spc+1);

	  /* Statements based statistics.  */
	  INDENT (spc+1);
	  pp_printf (buffer, "<stats calls=\"%d\" decisions=\"%d\" stmts=\"%d\" Gilb=\"%f\"",
			 nb_calls, decision_points, nb_statements,
			 ((nb_statements == 0) ? 0.0 :
			  ((float)decision_points / (float)nb_statements)));

	  /* Control flow statistics.  */
	  init_flow ();
	  build_tree_cfg (&DECL_SAVED_TREE (node));
	  pp_printf (buffer, " CFG-edges=\"%d\" CFG-BB=\"%d\" McCabe=\"%d\">\n",
			 n_edges, n_basic_blocks, n_edges - n_basic_blocks + 2);
	  delete_tree_cfg ();

	  /* End of the node.  */
	  INDENT (spc);
	  pp_printf (buffer, "</caller>\n");
	  return;

	case CALL_EXPR:
	  {
	    tree op0;
	    op0 = TREE_OPERAND (node, 0);

	    nb_calls++;
	    if (TREE_CODE (op0) == NON_LVALUE_EXPR)
	      op0 = TREE_OPERAND (op0, 0);

	    switch (TREE_CODE (op0))
	      {
	      case VAR_DECL:
	      case PARM_DECL:
		/* if (TREE_CODE (op0) == PARM_DECL)
		   This function pointer was received in parameter.
		   I think that this should not enter in the call graph.
		   Or otherwise it enters but with a special mark
		   saying that the name of this function is a parameter,
		   and thus for this name we don't expect a declaration.
		   Example:
		   /gcc/libiberty/splay_tree.c:splay_tree_foreach_helper ().  */
		print_callee (buffer, op0, spc);
		break;

	      case ADDR_EXPR:
	      case INDIRECT_REF:
	      case NOP_EXPR:
		print_callee (buffer, TREE_OPERAND (op0, 0), spc);
		break;

	      case COND_EXPR:
#if 0
		/* XXX: Why is this disabled?  */
		print_callee (buffer, TREE_OPERAND (TREE_OPERAND (op0, 0), 1), spc);
		print_callee (buffer, TREE_OPERAND (TREE_OPERAND (op0, 0), 2), spc);
#endif
		break;

	      case COMPONENT_REF:
		/* The function is a pointer contained in a structure.  */
		if (TREE_CODE (TREE_OPERAND (op0, 0)) == INDIRECT_REF ||
		    TREE_CODE (TREE_OPERAND (op0, 0)) == VAR_DECL)
		  print_callee (buffer, TREE_OPERAND (op0, 1), spc);
#if 0
		else
		/* We can have several levels of structures and a function
		   pointer inside.  This is not implemented yet...  */
		  NIY;
#endif
		break;

	      case ARRAY_REF:
		if (TREE_CODE (TREE_OPERAND (op0, 0)) == VAR_DECL)
		  print_callee (buffer, TREE_OPERAND (op0, 0), spc);
		else
		  print_callee (buffer, TREE_OPERAND (op0, 1), spc);
		break;

	      default:
		NIY;
	      }
	    /* Walk through function's arguments.  */
	    construct_call_graph (buffer, TREE_OPERAND (node, 1), spc);
	    node = TREE_CHAIN (node);
	    break;
	  }

	case ADDR_EXPR:
	  /* May be a function pointer passed as a parameter to a function.  */
	  if (TREE_CODE (TREE_OPERAND (node, 0)) == FUNCTION_DECL)
	    print_callee (buffer, TREE_OPERAND (node, 0), spc);
	  else
	    construct_call_graph (buffer, TREE_OPERAND (node, 0), spc);
	  node = TREE_CHAIN (node);
	  break;

	case ARRAY_REF:
	  if (TREE_CODE (TREE_OPERAND (node, 0)) == ARRAY_REF)
	    construct_call_graph (buffer, TREE_OPERAND (node, 0), spc);

	  construct_call_graph (buffer, TREE_OPERAND (node, 1), spc);
	  node = TREE_CHAIN (node);
	  break;

	case TREE_LIST:
	  construct_call_graph (buffer, TREE_VALUE (node), spc);
	  node = TREE_CHAIN (node);
	  break;

	case FIX_TRUNC_EXPR:
	case FIX_CEIL_EXPR:
	case FIX_FLOOR_EXPR:
	case FIX_ROUND_EXPR:
	case FLOAT_EXPR:
	case TRUTH_NOT_EXPR:
	case BIT_NOT_EXPR:
	case NE_EXPR:
	case INDIRECT_REF:
	case COMPOUND_STMT:
	case EXPR_STMT:
	case NOP_EXPR:
	case RETURN_STMT:
	  /* Unary nodes.  */
	  construct_call_graph (buffer, TREE_OPERAND (node, 0), spc);
	  node = TREE_CHAIN (node);
	  break;

	case DO_STMT:
	case WHILE_STMT:
	case SWITCH_STMT:
	case LSHIFT_EXPR:
	case RSHIFT_EXPR:
	case LROTATE_EXPR:
	case RROTATE_EXPR:
	case BIT_IOR_EXPR:
	case BIT_XOR_EXPR:
	case BIT_AND_EXPR:
	case TRUTH_ANDIF_EXPR:
	case TRUTH_ORIF_EXPR:
	case TRUTH_AND_EXPR:
	case TRUTH_OR_EXPR:
	case TRUTH_XOR_EXPR:
	case LT_EXPR:
	case LE_EXPR:
	case GT_EXPR:
	case GE_EXPR:
	case EQ_EXPR:
	case PLUS_EXPR:
	case MINUS_EXPR:
	case MULT_EXPR:
	case TRUNC_DIV_EXPR:
	case MODIFY_EXPR:
	  /* Binary nodes.  */
	  construct_call_graph (buffer, TREE_OPERAND (node, 0), spc);
	  construct_call_graph (buffer, TREE_OPERAND (node, 1), spc);
	  node = TREE_CHAIN (node);
	  break;

	case COND_EXPR:
	case IF_STMT:
	  /* Ternary nodes.  */
	  construct_call_graph (buffer, TREE_OPERAND (node, 0), spc);
	  construct_call_graph (buffer, TREE_OPERAND (node, 1), spc);
	  construct_call_graph (buffer, TREE_OPERAND (node, 2), spc);
	  node = TREE_CHAIN (node);
	  break;

	case FOR_STMT:
	  /* Quaternary nodes.  */
	  construct_call_graph (buffer, TREE_OPERAND (node, 0), spc);
	  construct_call_graph (buffer, TREE_OPERAND (node, 1), spc);
	  construct_call_graph (buffer, TREE_OPERAND (node, 2), spc);
	  construct_call_graph (buffer, TREE_OPERAND (node, 3), spc);
	  node = TREE_CHAIN (node);
	  break;

	default:
	  node = TREE_CHAIN (node);
	  break;
	}
    }
}


/* Print the callee function declaration.  */

static void
print_callee (pretty_printer *buffer, tree node, int spc)
{
  int i;

  /* Indent.  */
  for (i = 0; i<spc; i++)
    pp_space (buffer);

  /* Print the node.  */
  pp_printf (buffer, "<callee idref = \"");
  pp_printf (buffer, get_name (node), 0);
  pp_printf (buffer, "\"/>\n");
}