Added counters for inlining
[libfirm] / ir / stat / firmstat.c
1 /*
2  * Project:     libFIRM
3  * File name:   ir/ir/firmstat.c
4  * Purpose:     Statistics for Firm.
5  * Author:      Michael Beck
6  * Created:
7  * CVS-ID:      $Id$
8  * Copyright:   (c) 2004 Universität Karlsruhe
9  * Licence:     This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
10  */
11
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15
16 # include <string.h>
17
18 # include "irop_t.h"
19 # include "irnode_t.h"
20 # include "irgraph_t.h"
21 # include "pset.h"
22 # include "irprog.h"
23
24 #include <stdio.h>
25 #include <stdlib.h>
26
27 #undef obstack_chunk_alloc
28 #undef obstack_chunk_free
29 #define obstack_chunk_alloc     malloc
30 #define obstack_chunk_free      free
31 #include <obstack.h>
32
33 #ifdef FIRM_STATISTICS
34
35 #include "firmstat.h"
36
37 /*
38  * 32 bit should be enough for now
39  */
40 #define STAT_CNT_NUM 1
41
42 typedef struct _counter_t {
43   unsigned cnt[STAT_CNT_NUM];
44 } counter_t;
45
46 /*
47  * An entry for ir_nodes
48  */
49 typedef struct _node_entry_t {
50   counter_t   new_node;         /**< amount of new nodes for this entry */
51   counter_t   into_Id;          /**< amount of nodes that turned into Id's for this entry */
52   const ir_op *op;              /**< the op for this entry */
53 } node_entry_t;
54
55 /*
56  * An entry for ir_graphs
57  */
58 typedef struct _graph_entry_t {
59   pset           *opcode_hash;                  /**< hash map containing the opcode counter */
60   counter_t      walked;                        /**< walker walked over the graph */
61   counter_t      walked_blocks;                 /**< walker walked over the graph blocks */
62   counter_t      was_inlined;                   /**< number of times other graph were inlined */
63   counter_t      got_inlined;                   /**< number of times this graph was inlined */
64   pset           *opt_hash[STAT_OPT_MAX];       /**< hash maps containing opcode counter for optimizations */
65   const ir_graph *irg;                          /**< the graph of this object */
66   entity         *ent;                          /**< the entity of this graph if one exists */
67   int            deleted;                       /**< set if this irg was deleted */
68 } graph_entry_t;
69
70 /**
71  * An entry for optimized ir_nodes
72  */
73 typedef struct _opt_entry_t {
74   counter_t   count;            /**< optimization counter */
75   const ir_op *op;              /**< the op for this entry */
76 } opt_entry_t;
77
78 /**
79  * statistics info
80  */
81 typedef struct _statistic_info_t {
82   struct obstack cnts;          /**< obstack containing the counters */
83   pset           *opcode_hash;  /**< hash map containing the opcode counter */
84   pset           *irg_hash;     /**< hash map containing the counter for irgs */
85   FILE           *f;            /**< outputfile */
86   ir_op          *op_Phi0;      /**< needed pseudo op */
87 } stat_info_t;
88
89 /*
90  * global status
91  */
92 static stat_info_t _status, *status = &_status;
93
94 /**
95  * increase a counter
96  */
97 static INLINE void cnt_inc(counter_t *cnt)
98 {
99   int i;
100
101   for (i = 0; i < STAT_CNT_NUM; ++i) {
102     if (++cnt->cnt[i])
103       break;
104   }
105 }
106
107 /**
108  * decreace a counter
109  */
110 static INLINE void cnt_dec(counter_t *cnt)
111 {
112   int i;
113
114   for (i = 0; i < STAT_CNT_NUM; ++i) {
115     if (--cnt->cnt[i] != -1)
116       break;
117   }
118 }
119
120 /**
121  * set a counter to zero
122  */
123 static INLINE void cnt_clr(counter_t *cnt)
124 {
125   memset(cnt->cnt, 0, sizeof(cnt->cnt));
126 }
127
128 /*
129  * compare two elements of the opcode hash
130  */
131 static int opcode_cmp(const void *elt, const void *key)
132 {
133   const node_entry_t *e1 = elt;
134   const node_entry_t *e2 = key;
135
136   return e1->op->code - e2->op->code;
137 }
138
139 /*
140  * compare two elements of the graph hash
141  */
142 static int graph_cmp(const void *elt, const void *key)
143 {
144   const graph_entry_t *e1 = elt;
145   const graph_entry_t *e2 = key;
146
147   return e1->irg != e2->irg;
148 }
149
150 /*
151  * compare two elements of the optimization hash
152  */
153 static int opt_cmp(const void *elt, const void *key)
154 {
155   const opt_entry_t *e1 = elt;
156   const opt_entry_t *e2 = key;
157
158   return e1->op->code != e2->op->code;
159 }
160
161 /**
162  * Returns the associates node_entry_t for an ir_op
163  */
164 static node_entry_t *opcode_get_entry(const ir_op *op, pset *set)
165 {
166   node_entry_t key;
167   node_entry_t *elem;
168
169   key.op = op;
170
171   elem = pset_find(set, &key, op->code);
172   if (elem)
173     return elem;
174
175   elem = obstack_alloc(&status->cnts, sizeof(*elem));
176
177   /* clear new counter */
178   cnt_clr(&elem->new_node);
179   cnt_clr(&elem->into_Id);
180
181   elem->op = op;
182
183   return pset_insert(set, elem, op->code);
184 }
185
186 /**
187  * calculates a hash value for an irg
188  */
189 static INLINE unsigned irg_hash(const ir_graph *irg)
190 {
191   return (unsigned)irg;
192 }
193
194 /**
195  * Returns the acssociates graph_entry_t for an irg
196  */
197 static graph_entry_t *graph_get_entry(const ir_graph *irg, pset *set)
198 {
199   graph_entry_t key;
200   graph_entry_t *elem;
201   int i;
202
203   key.irg = irg;
204
205   elem = pset_find(set, &key, irg_hash(irg));
206   if (elem)
207     return elem;
208
209   elem = obstack_alloc(&status->cnts, sizeof(*elem));
210
211   cnt_clr(&elem->walked);
212   cnt_clr(&elem->walked_blocks);
213   cnt_clr(&elem->got_inlined);
214   cnt_clr(&elem->was_inlined);
215
216   /* new hash table for opcodes here  */
217   elem->opcode_hash  = new_pset(opcode_cmp, 5);
218   elem->irg          = irg;
219
220   for (i = 0; i < sizeof(elem->opt_hash)/sizeof(elem->opt_hash[0]); ++i)
221     elem->opt_hash[i] = new_pset(opt_cmp, 4);
222
223   return pset_insert(set, elem, irg_hash(irg));
224 }
225
226 /**
227  * Returns the associates opt_entry_t for an ir_op
228  */
229 static opt_entry_t *opt_get_entry(const ir_op *op, pset *set)
230 {
231   opt_entry_t key;
232   opt_entry_t *elem;
233
234   key.op = op;
235
236   elem = pset_find(set, &key, op->code);
237   if (elem)
238     return elem;
239
240   elem = obstack_alloc(&status->cnts, sizeof(*elem));
241
242   /* clear new counter */
243   cnt_clr(&elem->count);
244
245   elem->op = op;
246
247   return pset_insert(set, elem, op->code);
248 }
249
250 /* ---------------------------------------------------------------------- */
251
252 /* initialize the statistics module. */
253 void stat_init(void)
254 {
255   obstack_init(&status->cnts);
256
257   /* create the hash-tables */
258   status->opcode_hash = new_pset(opcode_cmp, 8);
259   status->irg_hash    = new_pset(graph_cmp, 8);
260
261   status->f           = fopen("firmstat.txt", "w");
262   status->op_Phi0     = NULL;
263 }
264
265 /* A new IR op is registered. */
266 void stat_new_ir_op(const ir_op *op)
267 {
268   /* execute for side effect :-) */
269   opcode_get_entry(op, status->opcode_hash);
270 }
271
272 /* An IR op is freed. */
273 void stat_free_ir_op(const ir_op *op)
274 {
275 }
276
277 /* A new node is created. */
278 void stat_new_node(const ir_node *node)
279 {
280   node_entry_t *entry;
281   graph_entry_t *graph;
282   ir_op *op = get_irn_op(node);
283
284   if (op->code == iro_Phi && get_irn_arity(node) == 0) {
285     /* special case, a Phi0 node, count on extra counter */
286     if (! status->op_Phi0) {
287       ir_op *op_Phi = get_op_Phi();
288
289       status->op_Phi0 = new_ir_op(0xFFFF, "Phi0", op_Phi->pinned, op_Phi->flags, op_Phi->opar, op_Phi->op_index, op_Phi->attr_size);
290     }
291     op = status->op_Phi0;
292   }
293
294   entry = opcode_get_entry(op, status->opcode_hash);
295   graph = graph_get_entry(current_ir_graph, status->irg_hash);
296
297   /* increase global value */
298   cnt_inc(&entry->new_node);
299
300   /* increase local value */
301   entry = opcode_get_entry(op, graph->opcode_hash);
302   cnt_inc(&entry->new_node);
303 }
304
305 /* A node is changed into a Id node */
306 void stat_turn_into_id(const ir_node *node)
307 {
308   node_entry_t *entry;
309   graph_entry_t *graph;
310   ir_op *op = get_irn_op(node);
311
312   if (op->code == iro_Phi && get_irn_arity(node) == 0) {
313     /* special case, a Phi0 node, count on extra counter */
314     if (! status->op_Phi0) {
315       ir_op *op_Phi = get_op_Phi();
316
317       status->op_Phi0 = new_ir_op(0xFFFF, "Phi0", op_Phi->pinned, op_Phi->flags, op_Phi->opar, op_Phi->op_index, op_Phi->attr_size);
318     }
319     op = status->op_Phi0;
320   }
321
322   entry = opcode_get_entry(op, status->opcode_hash);
323   graph = graph_get_entry(current_ir_graph, status->irg_hash);
324
325   /* increase global value */
326   cnt_inc(&entry->into_Id);
327
328   /* increase local value */
329   entry = opcode_get_entry(op, graph->opcode_hash);
330   cnt_inc(&entry->into_Id);
331 }
332
333 /* A new graph was created */
334 void stat_new_graph(const ir_graph *irg, entity *ent)
335 {
336   /* execute for side effect :-) */
337   graph_entry_t * graph = graph_get_entry(irg, status->irg_hash);
338
339   graph->ent     = ent;
340   graph->deleted = 0;
341 }
342
343 /*
344  * A graph was deleted
345  */
346 void stat_free_graph(const ir_graph *irg)
347 {
348   graph_entry_t * graph = graph_get_entry(irg, status->irg_hash);
349
350   graph->deleted = 1;
351 }
352
353 /*
354  * A walk over a graph is initiated
355  */
356 void stat_irg_walk(const ir_graph *irg, void *pre, void *post)
357 {
358   graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
359
360   cnt_inc(&graph->walked);
361 }
362
363 /*
364  * A walk over the graph's blocks is initiated
365  */
366 void stat_irg_block_walk(const ir_graph *irg, const ir_node *node, void *pre, void *post)
367 {
368   graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
369
370   cnt_inc(&graph->walked_blocks);
371 }
372
373 /**
374  * called for every node that is removed due to an optimization
375  */
376 static void removed_due_opt(ir_node *n, pset *set)
377 {
378   ir_op *op          = get_irn_op(n);
379   opt_entry_t *entry = opt_get_entry(op, set);
380
381   /* increase global value */
382   cnt_inc(&entry->count);
383 }
384
385 /*
386  * Some nodes were optimized into some others due to an optimization
387  */
388 void stat_merge_nodes(
389     ir_node **new_node_array, int new_num_entries,
390     ir_node **old_node_array, int old_num_entries,
391     stat_opt_kind opt)
392 {
393   int i, j;
394   graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
395
396   for (i = 0; i < old_num_entries; ++i) {
397     for (j = 0; j < new_num_entries; ++j)
398       if (old_node_array[i] == new_node_array[j])
399         break;
400
401     /* nodes might be in new and old, these are NOT removed */
402     if (j >= new_num_entries) {
403       removed_due_opt(old_node_array[i], graph->opt_hash[opt]);
404     }
405   }
406 }
407
408 /*
409  * A node was lowered into other nodes
410  */
411 void stat_lower(ir_node *node)
412 {
413   graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
414
415   removed_due_opt(node, graph->opt_hash[STAT_LOWERED]);
416 }
417
418 /*
419  * A graph was inlined
420  */
421 void stat_inline(ir_node *call, const ir_graph *called_irg)
422 {
423   ir_graph *irg = get_irn_irg(call);
424   graph_entry_t *i_graph = graph_get_entry(called_irg, status->irg_hash);
425   graph_entry_t *graph   = graph_get_entry(irg, status->irg_hash);
426
427   cnt_inc(&graph->got_inlined);
428   cnt_inc(&i_graph->was_inlined);
429 }
430
431 /**
432  * dumps a opcode hash
433  */
434 static void dump_opcode_hash(pset *set)
435 {
436   node_entry_t *entry;
437
438   fprintf(status->f, "%-16s %-8s %-8s\n", "Opcode", "created", "->Id");
439   for (entry = pset_first(set); entry; entry = pset_next(set)) {
440     fprintf(status->f, "%-16s %8d %8d\n",
441         get_id_str(entry->op->name), entry->new_node.cnt[0], entry->into_Id.cnt[0]);
442   }
443 }
444
445 static const char *opt_names[] = {
446   "straightening optimization",
447   "if simplification",
448   "algebraic simplification",
449   "Phi optmization",
450   "Write-After-Write optimization",
451   "Write-After-Read optimization",
452   "Tuple optimization",
453   "ID optimization",
454   "Constant evaluation",
455   "Lowered",
456 };
457
458 /**
459  * dumps a optimization hash
460  */
461 static void dump_opt_hash(pset *set, int index)
462 {
463   opt_entry_t *entry = pset_first(set);
464
465   if (entry) {
466     fprintf(status->f, "\n%s:\n", opt_names[index]);
467     fprintf(status->f, "%-16s %-8s\n", "Opcode", "deref");
468
469     for (; entry; entry = pset_next(set)) {
470       fprintf(status->f, "%-16s %8d\n",
471           get_id_str(entry->op->name), entry->count.cnt[0]);
472     }
473   }
474 }
475
476 /* Finish the statistics */
477 void stat_finish(void)
478 {
479   int i;
480   graph_entry_t *entry;
481   ir_graph *const_irg = get_const_code_irg();
482
483   /* dump global */
484   fprintf(status->f, "\nGlobal counts:\n");
485   dump_opcode_hash(status->opcode_hash);
486
487   for (entry = pset_first(status->irg_hash); entry; entry = pset_next(status->irg_hash)) {
488     entity *ent = entry->ent;
489
490     if (entry->irg == const_irg) {
491       fprintf(status->f, "\nConst code Irg %p", entry->irg);
492     }
493     else {
494       if (ent)
495         fprintf(status->f, "\nEntity %s, Irg %p", get_entity_name(ent), entry->irg);
496       else
497         fprintf(status->f, "\nIrg %p", entry->irg);
498     }
499
500     fprintf(status->f, " %swalked %d over blocks %d was inlined %d got inlined %d:\n",
501         entry->deleted ? "DELETED " : "",
502         entry->walked.cnt[0], entry->walked_blocks.cnt[0],
503         entry->was_inlined.cnt[0],
504         entry->got_inlined.cnt[0]
505         );
506
507     dump_opcode_hash(entry->opcode_hash);
508
509     for (i = 0; i < sizeof(entry->opt_hash)/sizeof(entry->opt_hash[0]); ++i) {
510       dump_opt_hash(entry->opt_hash[i], i);
511     }
512   }
513
514   fclose(status->f);
515 }
516
517 #else
518
519 /* need this for prototypes */
520 #define FIRM_STATISTICS
521 #include "firmstat.h"
522
523 void stat_init(void) {}
524
525 void stat_finish(void) {}
526
527 void stat_new_ir_op(const ir_op *op) {}
528
529 void stat_free_ir_op(const ir_op *op) {}
530
531 void stat_new_node(const ir_node *node) {}
532
533 void stat_turn_into_id(const ir_node *node) {}
534
535 void stat_new_graph(const ir_graph *irg, entity *ent) {}
536
537 void stat_free_graph(const ir_graph *irg) {}
538
539 void stat_irg_walk(const ir_graph *irg, void *pre, void *post) {}
540
541 void stat_irg_block_walk(const ir_graph *irg, const ir_node *node, void *pre, void *post) {}
542
543 void stat_merge_nodes(
544     ir_node **new_node_array, int new_num_entries,
545     ir_node **old_node_array, int old_num_entries,
546     stat_opt_kind opt) {}
547
548 void stat_lower(ir_node *node) {}
549
550 void stat_inline(const ir_node *call, const ir_graph *irg) {}
551
552 #endif