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