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