ec5811558b1206d84ec12703f39995c7a2e0dba5
[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 <stdio.h>
19 #include <stdlib.h>
20
21 #ifdef FIRM_STATISTICS
22
23 #include "firmstat.h"
24 # include "irop_t.h"
25 # include "irnode_t.h"
26 # include "irgraph_t.h"
27 # include "pset.h"
28 # include "irprog.h"
29 # include "irgwalk.h"
30 # include "pattern.h"
31 # include "counter.h"
32
33 /*
34  * just be make some things clear :-), the
35  * poor man "generics"
36  */
37 #define HASH_MAP(type) hmap_##type
38
39 typedef pset hmap_node_entry_t;
40 typedef pset hmap_graph_entry_t;
41 typedef pset hmap_opt_entry_t;
42 typedef pset hmap_block_entry_t;
43 typedef pset hmap_ir_op;
44
45 /*
46  * An entry for ir_nodes, used in ir_graph statistics.
47  */
48 typedef struct _node_entry_t {
49   counter_t   cnt_alive;                /**< amount of nodes in this entry */
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   HASH_MAP(node_entry_t)  *opcode_hash;                 /**< hash map containing the opcode counter */
60   HASH_MAP(block_entry_t) *block_hash;                  /**< hash map countaining the block counter */
61   counter_t               cnt_walked;                   /**< walker walked over the graph */
62   counter_t               cnt_walked_blocks;            /**< walker walked over the graph blocks */
63   counter_t               cnt_was_inlined;              /**< number of times other graph were inlined */
64   counter_t               cnt_got_inlined;              /**< number of times this graph was inlined */
65   counter_t               cnt_edges;                    /**< number of DF edges in this graph */
66   HASH_MAP(opt_entry_t)   *opt_hash[STAT_OPT_MAX];      /**< hash maps containing opcode counter for optimizations */
67   ir_graph                *irg;                         /**< the graph of this object */
68   entity                  *ent;                         /**< the entity of this graph if one exists */
69   int                     deleted;                      /**< set if this irg was deleted */
70 } graph_entry_t;
71
72 /**
73  * An entry for optimized ir_nodes
74  */
75 typedef struct _opt_entry_t {
76   counter_t   count;                    /**< optimization counter */
77   const ir_op *op;                      /**< the op for this entry */
78 } opt_entry_t;
79
80 /**
81  * An entry for a block in a ir-graph
82  */
83 typedef struct _block_entry_t {
84   counter_t  cnt_nodes;                 /**< the counter of nodes in this block */
85   counter_t  cnt_edges;                 /**< the counter of edges in this block */
86   counter_t  cnt_in_edges;              /**< the counter of edges incoming from other blocks to this block */
87   counter_t  cnt_out_edges;             /**< the counter of edges outgoing from this block to other blocks */
88   long       block_nr;                  /**< block nr */
89 } block_entry_t;
90
91 /** forward */
92 typedef struct _dumper_t dumper_t;
93
94 /**
95  * handler for dumping an IRG
96  *
97  * @param dmp   the dumper
98  * @param entry the IR-graph hash map entry
99  */
100 typedef void (*dump_graph_FUNC)(dumper_t *dmp, graph_entry_t *entry);
101
102 /**
103  * handler for dumper init
104  *
105  * @param dmp   the dumper
106  * @param name  name of the file to dump to
107  */
108 typedef void (*dump_init_FUNC)(dumper_t *dmp, const char *name);
109
110 /**
111  * handler for dumper finish
112  *
113  * @param dmp   the dumper
114  */
115 typedef void (*dump_finish_FUNC)(dumper_t *dmp);
116
117
118 /**
119  * a dumper description
120  */
121 struct _dumper_t {
122   dump_graph_FUNC         dump_graph;           /**< handler for dumping an irg */
123   dump_init_FUNC          init;                 /**< handler for init */
124   dump_finish_FUNC        finish;               /**< handler for finish */
125   FILE                    *f;                   /**< the file to dump to */
126   dumper_t                *next;                /**< link to the next dumper */
127 };
128
129 /**
130  * statistics info
131  */
132 typedef struct _statistic_info_t {
133   struct obstack          cnts;                 /**< obstack containing the counters */
134   HASH_MAP(graph_entry_t) *irg_hash;            /**< hash map containing the counter for irgs */
135   HASH_MAP(ir_op)         *ir_op_hash;          /**< hash map containing all ir_ops (accessible by op_codes) */
136   int                     recursive;            /**< flag for detecting recursive hook calls */
137   int                     in_dead_node_elim;    /**< set, if dead node elimination runs */
138   ir_op                   *op_Phi0;             /**< needed pseudo op */
139   ir_op                   *op_PhiM;             /**< needed pseudo op */
140   dumper_t                *dumper;              /**< list of dumper */
141   int                     enable;               /**< if set, statistic is enabled */
142 } stat_info_t;
143
144 /**
145  * names of the optimizations
146  */
147 static const char *opt_names[] = {
148   "straightening optimization",
149   "if simplification",
150   "algebraic simplification",
151   "Phi optmization",
152   "Write-After-Write optimization",
153   "Write-After-Read optimization",
154   "Read-After-Write optimization",
155   "Tuple optimization",
156   "ID optimization",
157   "Constant evaluation",
158   "Lowered",
159 };
160
161 /**
162  * need this to be static
163  */
164 static ir_op _op_Phi0, _op_PhiM;
165
166 /* ---------------------------------------------------------------------------------- */
167
168 #define STAT_ENTER              ++status->recursive
169 #define STAT_LEAVE              --status->recursive
170 #define STAT_ENTER_SINGLE       do { if (status->recursive > 0) return; ++status->recursive; } while (0)
171
172 /**
173  * global status
174  */
175 static stat_info_t _status, *status = &_status;
176
177 /**
178  * compare two elements of the opcode hash
179  */
180 static int opcode_cmp(const void *elt, const void *key)
181 {
182   const node_entry_t *e1 = elt;
183   const node_entry_t *e2 = key;
184
185   return e1->op->code - e2->op->code;
186 }
187
188 /**
189  * compare two elements of the graph hash
190  */
191 static int graph_cmp(const void *elt, const void *key)
192 {
193   const graph_entry_t *e1 = elt;
194   const graph_entry_t *e2 = key;
195
196   return e1->irg != e2->irg;
197 }
198
199 /**
200  * compare two elements of the optimization hash
201  */
202 static int opt_cmp(const void *elt, const void *key)
203 {
204   const opt_entry_t *e1 = elt;
205   const opt_entry_t *e2 = key;
206
207   return e1->op->code != e2->op->code;
208 }
209
210 /**
211  * compare two elements of the block hash
212  */
213 static int block_cmp(const void *elt, const void *key)
214 {
215   const block_entry_t *e1 = elt;
216   const block_entry_t *e2 = key;
217
218   return e1->block_nr != e2->block_nr;
219 }
220
221 /**
222  * compare two elements of the ir_op hash
223  */
224 static int opcode_cmp_2(const void *elt, const void *key)
225 {
226   const ir_op *e1 = elt;
227   const ir_op *e2 = key;
228
229   return e1->code != e2->code;
230 }
231
232 /**
233  * Returns the associates node_entry_t for an ir_op
234  */
235 static node_entry_t *opcode_get_entry(const ir_op *op, pset *set)
236 {
237   node_entry_t key;
238   node_entry_t *elem;
239
240   key.op = op;
241
242   elem = pset_find(set, &key, op->code);
243   if (elem)
244     return elem;
245
246   elem = obstack_alloc(&status->cnts, sizeof(*elem));
247
248   /* clear counter */
249   cnt_clr(&elem->cnt_alive);
250   cnt_clr(&elem->new_node);
251   cnt_clr(&elem->into_Id);
252
253   elem->op = op;
254
255   return pset_insert(set, elem, op->code);
256 }
257
258 /**
259  * Returns the associates ir_op for an opcode
260  */
261 static ir_op *opcode_find_entry(opcode code, pset *set)
262 {
263   ir_op key;
264
265   key.code = code;
266   return pset_find(set, &key, code);
267 }
268
269 /**
270  * calculates a hash value for an irg
271  * Addresses are typically aligned at 32bit, so we ignore the lowest bits
272  */
273 static INLINE unsigned irg_hash(const ir_graph *irg)
274 {
275   return (unsigned)irg >> 3;
276 }
277
278 /**
279  * Returns the acssociates graph_entry_t for an irg
280  */
281 static graph_entry_t *graph_get_entry(ir_graph *irg, pset *set)
282 {
283   graph_entry_t key;
284   graph_entry_t *elem;
285   int i;
286
287   key.irg = irg;
288
289   elem = pset_find(set, &key, irg_hash(irg));
290   if (elem)
291     return elem;
292
293   elem = obstack_alloc(&status->cnts, sizeof(*elem));
294
295   cnt_clr(&elem->cnt_walked);
296   cnt_clr(&elem->cnt_walked_blocks);
297   cnt_clr(&elem->cnt_got_inlined);
298   cnt_clr(&elem->cnt_was_inlined);
299   cnt_clr(&elem->cnt_edges);
300
301   /* new hash table for opcodes here  */
302   elem->opcode_hash  = new_pset(opcode_cmp, 5);
303   elem->block_hash   = new_pset(block_cmp, 5);
304   elem->irg          = irg;
305
306   for (i = 0; i < sizeof(elem->opt_hash)/sizeof(elem->opt_hash[0]); ++i)
307     elem->opt_hash[i] = new_pset(opt_cmp, 4);
308
309   return pset_insert(set, elem, irg_hash(irg));
310 }
311
312 /**
313  * Returns the associates opt_entry_t for an ir_op
314  */
315 static opt_entry_t *opt_get_entry(const ir_op *op, pset *set)
316 {
317   opt_entry_t key;
318   opt_entry_t *elem;
319
320   key.op = op;
321
322   elem = pset_find(set, &key, op->code);
323   if (elem)
324     return elem;
325
326   elem = obstack_alloc(&status->cnts, sizeof(*elem));
327
328   /* clear new counter */
329   cnt_clr(&elem->count);
330
331   elem->op = op;
332
333   return pset_insert(set, elem, op->code);
334 }
335
336 /**
337  * Returns the associates block_entry_t for an block
338  */
339 static block_entry_t *block_get_entry(long block_nr, pset *set)
340 {
341   block_entry_t key;
342   block_entry_t *elem;
343
344   key.block_nr = block_nr;
345
346   elem = pset_find(set, &key, block_nr);
347   if (elem)
348     return elem;
349
350   elem = obstack_alloc(&status->cnts, sizeof(*elem));
351
352   /* clear new counter */
353   cnt_clr(&elem->cnt_nodes);
354   cnt_clr(&elem->cnt_edges);
355   cnt_clr(&elem->cnt_in_edges);
356   cnt_clr(&elem->cnt_out_edges);
357
358   elem->block_nr = block_nr;
359
360   return pset_insert(set, elem, block_nr);
361 }
362
363
364 /**
365  * Returns the ir_op for an IR-node,
366  * handles special cases and return pseudo op codes
367  */
368 static ir_op *stat_get_irn_op(const ir_node *node)
369 {
370   ir_op *op = get_irn_op(node);
371
372   if (op->code == iro_Phi && get_irn_arity(node) == 0) {
373     /* special case, a Phi0 node, count on extra counter */
374     op = status->op_Phi0;
375   }
376   else if (op->code == iro_Phi && get_irn_mode(node) == mode_M) {
377     /* special case, a Memory Phi node, count on extra counter */
378     op = status->op_PhiM;
379   }
380   return op;
381 }
382
383 /**
384  * update the block counter
385  */
386 static void count_block_info(ir_node *node, graph_entry_t *graph)
387 {
388   ir_op *op = get_irn_op(node);
389   ir_node *block;
390   block_entry_t *b_entry;
391   int i, arity;
392
393   /* check for block */
394   if (op == op_Block) {
395     arity = get_irn_arity(node);
396     b_entry = block_get_entry(get_irn_node_nr(node), graph->block_hash);
397
398     /* count all incoming edges */
399     for (i = 0; i < arity; ++i) {
400       ir_node *pred = get_irn_n(node, i);
401       ir_node *other_block = get_nodes_block(pred);
402       block_entry_t *b_entry_other = block_get_entry(get_irn_node_nr(other_block), graph->block_hash);
403
404       cnt_inc(&b_entry->cnt_in_edges);  /* an edge coming from another block */
405       cnt_inc(&b_entry_other->cnt_out_edges);
406     }
407     return;
408   }
409   else if (op == op_Call) {
410     // return;
411   }
412
413   block   = get_nodes_block(node);
414   b_entry = block_get_entry(get_irn_node_nr(block), graph->block_hash);
415
416   /* we have a new nodes */
417   cnt_inc(&b_entry->cnt_nodes);
418
419   arity = get_irn_arity(node);
420
421   for (i = 0; i < arity; ++i) {
422     ir_node *pred = get_irn_n(node, i);
423     ir_node *other_block;
424
425     if (get_irn_op(pred) == op_Block)
426       continue;
427
428     other_block = get_nodes_block(pred);
429
430     if (other_block == block)
431       cnt_inc(&b_entry->cnt_edges);     /* a in block edge */
432     else {
433       block_entry_t *b_entry_other = block_get_entry(get_irn_node_nr(other_block), graph->block_hash);
434
435       cnt_inc(&b_entry->cnt_in_edges);  /* an edge coming from another block */
436       cnt_inc(&b_entry_other->cnt_out_edges);
437     }
438   }
439 }
440
441 /**
442  * walker for reachable nodes count
443  */
444 static void count_nodes(ir_node *node, void *env)
445 {
446   graph_entry_t *graph = env;
447   node_entry_t *entry;
448
449   ir_op *op = stat_get_irn_op(node);
450   int arity = get_irn_arity(node);
451
452   entry = opcode_get_entry(op, graph->opcode_hash);
453
454   cnt_inc(&entry->cnt_alive);
455   cnt_add_i(&graph->cnt_edges, arity);
456
457   /* count block edges */
458   count_block_info(node, graph);
459 }
460
461 /**
462  * count all alive nodes and edges in a graph
463  */
464 static void count_nodes_in_graph(graph_entry_t *global, graph_entry_t *graph)
465 {
466   node_entry_t *entry;
467
468   irg_walk_graph(graph->irg, count_nodes, NULL, graph);
469
470   /* assume we walk every graph only ONCE, we could sum here the global count */
471   for (entry = pset_first(graph->opcode_hash); entry; entry = pset_next(graph->opcode_hash)) {
472     node_entry_t *g_entry = opcode_get_entry(entry->op, global->opcode_hash);
473
474     /* update the node counter */
475     cnt_add(&g_entry->cnt_alive, &entry->cnt_alive);
476   }
477
478   /* update the edge counter */
479   cnt_add(&global->cnt_edges, &graph->cnt_edges);
480 }
481
482 /**
483  * register a dumper
484  */
485 static void stat_register_dumper(dumper_t *dumper, const char *name)
486 {
487   dumper->next   = status->dumper;
488   status->dumper = dumper;
489
490   if (dumper->init)
491     dumper->init(dumper, name);
492 }
493
494 /**
495  * dumps an irg
496  */
497 static void dump_graph(graph_entry_t *entry)
498 {
499   dumper_t *dumper;
500
501   for (dumper = status->dumper; dumper; dumper = dumper->next) {
502     if (dumper->dump_graph)
503       dumper->dump_graph(dumper, entry);
504   }
505 }
506
507 /**
508  * finish the dumper
509  */
510 static void dump_finish(void)
511 {
512   dumper_t *dumper;
513
514   for (dumper = status->dumper; dumper; dumper = dumper->next) {
515     if (dumper->finish)
516       dumper->finish(dumper);
517   }
518 }
519
520 /* ---------------------------------------------------------------------- */
521
522 /**
523  * dumps a opcode hash into human readable form
524  */
525 static void simple_dump_opcode_hash(dumper_t *dmp, pset *set)
526 {
527   node_entry_t *entry;
528   counter_t f_alive;
529   counter_t f_new_node;
530   counter_t f_Id;
531
532   cnt_clr(&f_alive);
533   cnt_clr(&f_new_node);
534   cnt_clr(&f_Id);
535
536   fprintf(dmp->f, "%-16s %-8s %-8s %-8s\n", "Opcode", "alive", "created", "->Id");
537   for (entry = pset_first(set); entry; entry = pset_next(set)) {
538     fprintf(dmp->f, "%-16s %8d %8d %8d\n",
539       get_id_str(entry->op->name), entry->cnt_alive.cnt[0], entry->new_node.cnt[0], entry->into_Id.cnt[0]);
540
541     cnt_add(&f_alive,    &entry->cnt_alive);
542     cnt_add(&f_new_node, &entry->new_node);
543     cnt_add(&f_Id,       &entry->into_Id);
544   }
545   fprintf(dmp->f, "-------------------------------------------\n");
546   fprintf(dmp->f, "%-16s %8d %8d %8d\n", "Sum",
547      f_alive.cnt[0],
548      f_new_node.cnt[0],
549      f_Id.cnt[0]);
550 }
551
552 /**
553  * dumps a optimization hash into human readable form
554  */
555 static void simple_dump_opt_hash(dumper_t *dmp, pset *set, int index)
556 {
557   opt_entry_t *entry = pset_first(set);
558
559   if (entry) {
560     fprintf(dmp->f, "\n%s:\n", opt_names[index]);
561     fprintf(dmp->f, "%-16s %-8s\n", "Opcode", "deref");
562
563     for (; entry; entry = pset_next(set)) {
564       fprintf(dmp->f, "%-16s %8d\n",
565         get_id_str(entry->op->name), entry->count.cnt[0]);
566     }
567   }
568 }
569
570 /**
571  * dumps the endges count
572  */
573 static void simple_dump_edges(dumper_t *dmp, counter_t *cnt)
574 {
575   fprintf(dmp->f, "%-16s %8d\n", "Edges", cnt->cnt[0]);
576 }
577
578 /**
579  * dumps the IRG
580  */
581 static void simple_dump_graph(dumper_t *dmp, graph_entry_t *entry)
582 {
583   int dump_opts = 1;
584   block_entry_t *b_entry;
585
586   if (entry->irg) {
587     ir_graph *const_irg = get_const_code_irg();
588
589     if (entry->irg == const_irg) {
590       fprintf(dmp->f, "\nConst code Irg %p", (void *)entry->irg);
591     }
592     else {
593       if (entry->ent)
594         fprintf(dmp->f, "\nEntity %s, Irg %p", get_entity_name(entry->ent), (void *)entry->irg);
595       else
596         fprintf(dmp->f, "\nIrg %p", (void *)entry->irg);
597     }
598
599     fprintf(dmp->f, " %swalked %d over blocks %d was inlined %d got inlined %d:\n",
600         entry->deleted ? "DELETED " : "",
601         entry->cnt_walked.cnt[0], entry->cnt_walked_blocks.cnt[0],
602         entry->cnt_was_inlined.cnt[0],
603         entry->cnt_got_inlined.cnt[0]
604     );
605   }
606   else {
607     fprintf(dmp->f, "\nGlobals counts:\n");
608     dump_opts = 0;
609   }
610
611   simple_dump_opcode_hash(dmp, entry->opcode_hash);
612   simple_dump_edges(dmp, &entry->cnt_edges);
613
614   /* effects of optimizations */
615   if (dump_opts) {
616     int i;
617
618     for (i = 0; i < sizeof(entry->opt_hash)/sizeof(entry->opt_hash[0]); ++i) {
619       simple_dump_opt_hash(dmp, entry->opt_hash[i], i);
620     }
621   }
622
623   /* dump block info */
624   fprintf(dmp->f, "\n%12s %12s %12s %12s %12s %12s\n", "Block Nr", "Nodes", "intern", "incoming", "outgoing", "quot");
625   for (b_entry = pset_first(entry->block_hash);
626        b_entry;
627        b_entry = pset_next(entry->block_hash)) {
628     fprintf(dmp->f, "%12ld %12u %12u %12u %12u %4.8f\n",
629         b_entry->block_nr,
630         b_entry->cnt_nodes.cnt[0],
631         b_entry->cnt_edges.cnt[0],
632         b_entry->cnt_in_edges.cnt[0],
633         b_entry->cnt_out_edges.cnt[0],
634         (double)b_entry->cnt_edges.cnt[0] / (double)b_entry->cnt_nodes.cnt[0]
635     );
636   }
637 }
638
639 /**
640  * initialise the simple dumper
641  */
642 static void simple_init(dumper_t *dmp, const char *name)
643 {
644   dmp->f = fopen(name, "w");
645 }
646
647 /**
648  * finishes the simple dumper
649  */
650 static void simple_finish(dumper_t *dmp)
651 {
652   fclose(dmp->f);
653   dmp->f = NULL;
654 }
655
656 /**
657  * the simple human readable dumper
658  */
659 static dumper_t simple_dumper = {
660   simple_dump_graph,
661   simple_init,
662   simple_finish,
663   NULL,
664   NULL,
665 };
666
667 /* ---------------------------------------------------------------------- */
668
669 /**
670  * count the nodes as needed:
671  *
672  * 1 normal (data) Phi's
673  * 2 memory Phi's
674  * 3 Proj
675  * 0 all other nodes
676  */
677 static void csv_count_nodes(graph_entry_t *graph, counter_t cnt[])
678 {
679   node_entry_t *entry;
680   int i;
681
682   for (i = 0; i < 4; ++i)
683     cnt_clr(&cnt[i]);
684
685   for (entry = pset_first(graph->opcode_hash); entry; entry = pset_next(graph->opcode_hash)) {
686     if (entry->op == op_Phi) {
687       /* normal Phi */
688       cnt_add(&cnt[1], &entry->cnt_alive);
689     }
690     else if (entry->op == status->op_PhiM) {
691       /* memory Phi */
692       cnt_add(&cnt[2], &entry->cnt_alive);
693     }
694     else if (entry->op == op_Proj) {
695       /* Proj */
696       cnt_add(&cnt[3], &entry->cnt_alive);
697     }
698     else {
699       /* all other nodes */
700       cnt_add(&cnt[0], &entry->cnt_alive);
701     }
702   }
703 }
704
705 /**
706  * dumps the IRG
707  */
708 static void csv_dump_graph(dumper_t *dmp, graph_entry_t *entry)
709 {
710   const char *name;
711
712   counter_t cnt[4];
713
714   if (entry->irg) {
715     ir_graph *const_irg = get_const_code_irg();
716
717     if (entry->irg == const_irg) {
718       name = "<Const code Irg>";
719       return;
720     }
721     else {
722       if (entry->ent)
723         name = get_entity_name(entry->ent);
724       else
725         name = "<UNKNOWN IRG>";
726     }
727
728     csv_count_nodes(entry, cnt);
729
730     fprintf(dmp->f, "%-40s, %p, %d, %d, %d, %d\n",
731         name,
732         (void *)entry->irg,
733         cnt[0].cnt[0],
734         cnt[1].cnt[0],
735         cnt[2].cnt[0],
736         cnt[3].cnt[0]
737     );
738   }
739 }
740
741 /**
742  * initialise the simple dumper
743  */
744 static void csv_init(dumper_t *dmp, const char *name)
745 {
746   dmp->f = fopen(name, "a");
747 }
748
749 /**
750  * finishes the simple dumper
751  */
752 static void csv_finish(dumper_t *dmp)
753 {
754   fclose(dmp->f);
755   dmp->f = NULL;
756 }
757
758 /**
759  * the simple human readable dumper
760  */
761 static dumper_t csv_dumper = {
762   csv_dump_graph,
763   csv_init,
764   csv_finish,
765   NULL,
766   NULL,
767 };
768
769
770 /* ---------------------------------------------------------------------- */
771
772 /*
773  * helper: get an ir_op from an opcode
774  */
775 ir_op *stat_get_op_from_opcode(opcode code)
776 {
777   return opcode_find_entry(code, status->ir_op_hash);
778 }
779
780 /* initialize the statistics module. */
781 void init_stat(void)
782 {
783 #define X(a)  a, sizeof(a)-1
784
785   int pseudo_id = 0;
786
787   /* enable statistics */
788   status->enable = enable_options & FIRMSTAT_ENABLED;
789
790   if (! status->enable)
791    return;
792
793   obstack_init(&status->cnts);
794
795   /* build the pseudo-ops */
796   _op_Phi0.code = --pseudo_id;
797   _op_Phi0.name = new_id_from_chars(X("Phi0"));
798
799   _op_PhiM.code = --pseudo_id;
800   _op_PhiM.name = new_id_from_chars(X("PhiM"));
801
802   /* create the hash-tables */
803   status->irg_hash   = new_pset(graph_cmp, 8);
804   status->ir_op_hash = new_pset(opcode_cmp_2, 1);
805
806   status->op_Phi0    = &_op_Phi0;
807   status->op_PhiM    = &_op_PhiM;
808
809   stat_register_dumper(&simple_dumper, "firmstat.txt");
810   stat_register_dumper(&csv_dumper, "firmstat.csv");
811
812   /* initialize the pattern hash */
813   stat_init_pattern_history(enable_options & FIRMSTAT_PATTERN_ENABLED);
814 #undef X
815 }
816
817 /* A new IR op is registered. */
818 void stat_new_ir_op(const ir_op *op)
819 {
820   if (! status->enable)
821     return;
822
823   STAT_ENTER;
824   {
825     graph_entry_t *graph = graph_get_entry(NULL, status->irg_hash);
826
827     /* execute for side effect :-) */
828     opcode_get_entry(op, graph->opcode_hash);
829
830     pset_insert(status->ir_op_hash, op, op->code);
831   }
832   STAT_LEAVE;
833 }
834
835 /* An IR op is freed. */
836 void stat_free_ir_op(const ir_op *op)
837 {
838   if (! status->enable)
839     return;
840
841   STAT_ENTER;
842   {
843   }
844   STAT_LEAVE;
845 }
846
847 /* A new node is created. */
848 void stat_new_node(const ir_node *node)
849 {
850   if (! status->enable)
851     return;
852
853   /* do NOT count during dead node elimination */
854   if (status->in_dead_node_elim > 0)
855     return;
856
857   STAT_ENTER;
858   {
859     node_entry_t *entry;
860     graph_entry_t *graph;
861     ir_op *op = stat_get_irn_op(node);
862
863     /* increase global value */
864     graph = graph_get_entry(NULL, status->irg_hash);
865     entry = opcode_get_entry(op, graph->opcode_hash);
866     cnt_inc(&entry->new_node);
867
868     /* increase local value */
869     graph = graph_get_entry(current_ir_graph, status->irg_hash);
870     entry = opcode_get_entry(op, graph->opcode_hash);
871     cnt_inc(&entry->new_node);
872   }
873   STAT_LEAVE;
874 }
875
876 /* A node is changed into a Id node */
877 void stat_turn_into_id(const ir_node *node)
878 {
879   if (! status->enable)
880     return;
881
882   STAT_ENTER;
883   {
884     node_entry_t *entry;
885     graph_entry_t *graph;
886     ir_op *op = stat_get_irn_op(node);
887
888     /* increase global value */
889     graph = graph_get_entry(NULL, status->irg_hash);
890     entry = opcode_get_entry(op, graph->opcode_hash);
891     cnt_inc(&entry->into_Id);
892
893     /* increase local value */
894     graph = graph_get_entry(current_ir_graph, status->irg_hash);
895     entry = opcode_get_entry(op, graph->opcode_hash);
896     cnt_inc(&entry->into_Id);
897   }
898   STAT_LEAVE;
899 }
900
901 /* A new graph was created */
902 void stat_new_graph(ir_graph *irg, entity *ent)
903 {
904   if (! status->enable)
905     return;
906
907   STAT_ENTER;
908   {
909     /* execute for side effect :-) */
910     graph_entry_t * graph = graph_get_entry(irg, status->irg_hash);
911
912     graph->ent     = ent;
913     graph->deleted = 0;
914   }
915   STAT_LEAVE;
916 }
917
918 /*
919  * A graph was deleted
920  */
921 void stat_free_graph(ir_graph *irg)
922 {
923   if (! status->enable)
924     return;
925
926   STAT_ENTER;
927   {
928     graph_entry_t *graph  = graph_get_entry(irg, status->irg_hash);
929     graph_entry_t *global = graph_get_entry(NULL, status->irg_hash);
930
931     graph->deleted = 1;
932
933     /* count the nodes of the graph yet, it will be destroyed later */
934     count_nodes_in_graph(global, graph);
935
936     /* calculate the pattern */
937     stat_calc_pattern_history(irg);
938   }
939   STAT_LEAVE;
940 }
941
942 /*
943  * A walk over a graph is initiated. Do not count walks from statistic code.
944  */
945 void stat_irg_walk(ir_graph *irg, void *pre, void *post)
946 {
947   if (! status->enable)
948     return;
949
950   STAT_ENTER_SINGLE;
951   {
952     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
953
954     cnt_inc(&graph->cnt_walked);
955   }
956   STAT_LEAVE;
957 }
958
959 /*
960  * A walk over a graph in block-wise order is initiated. Do not count walks from statistic code.
961  */
962 void stat_irg_walk_blkwise(ir_graph *irg, void *pre, void *post)
963 {
964   /* for now, do NOT differentiate between blockwise and normal */
965   stat_irg_walk(irg, pre, post);
966 }
967
968 /*
969  * A walk over the graph's blocks is initiated. Do not count walks from statistic code.
970  */
971 void stat_irg_block_walk(ir_graph *irg, const ir_node *node, void *pre, void *post)
972 {
973   if (! status->enable)
974     return;
975
976   STAT_ENTER_SINGLE;
977   {
978     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
979
980     cnt_inc(&graph->cnt_walked_blocks);
981   }
982   STAT_LEAVE;
983 }
984
985 /**
986  * called for every node that is removed due to an optimization
987  */
988 static void removed_due_opt(ir_node *n, pset *set)
989 {
990   ir_op *op          = get_irn_op(n);
991   opt_entry_t *entry = opt_get_entry(op, set);
992
993   /* increase global value */
994   cnt_inc(&entry->count);
995 }
996
997 /*
998  * Some nodes were optimized into some others due to an optimization
999  */
1000 void stat_merge_nodes(
1001     ir_node **new_node_array, int new_num_entries,
1002     ir_node **old_node_array, int old_num_entries,
1003     stat_opt_kind opt)
1004 {
1005   if (! status->enable)
1006     return;
1007
1008   STAT_ENTER;
1009   {
1010     int i, j;
1011     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1012
1013     for (i = 0; i < old_num_entries; ++i) {
1014       for (j = 0; j < new_num_entries; ++j)
1015         if (old_node_array[i] == new_node_array[j])
1016           break;
1017
1018       /* nodes might be in new and old, these are NOT removed */
1019       if (j >= new_num_entries) {
1020         removed_due_opt(old_node_array[i], graph->opt_hash[opt]);
1021       }
1022     }
1023   }
1024   STAT_LEAVE;
1025 }
1026
1027 /*
1028  * A node was lowered into other nodes
1029  */
1030 void stat_lower(ir_node *node)
1031 {
1032   if (! status->enable)
1033     return;
1034
1035   STAT_ENTER;
1036   {
1037     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1038
1039     removed_due_opt(node, graph->opt_hash[STAT_LOWERED]);
1040   }
1041   STAT_LEAVE;
1042 }
1043
1044 /*
1045  * A graph was inlined
1046  */
1047 void stat_inline(ir_node *call, ir_graph *called_irg)
1048 {
1049   if (! status->enable)
1050     return;
1051
1052   STAT_ENTER;
1053   {
1054     ir_graph *irg = get_irn_irg(call);
1055     graph_entry_t *i_graph = graph_get_entry(called_irg, status->irg_hash);
1056     graph_entry_t *graph   = graph_get_entry(irg, status->irg_hash);
1057
1058     cnt_inc(&graph->cnt_got_inlined);
1059     cnt_inc(&i_graph->cnt_was_inlined);
1060   }
1061   STAT_LEAVE;
1062 }
1063
1064 /*
1065  * Start the dead node elimination.
1066  */
1067 void stat_dead_node_elim_start(ir_graph *irg)
1068 {
1069   if (! status->enable)
1070     return;
1071
1072   ++status->in_dead_node_elim;
1073 }
1074
1075 /*
1076  * Stops the dead node elimination.
1077  */
1078 void stat_dead_node_elim_stop(ir_graph *irg)
1079 {
1080   if (! status->enable)
1081     return;
1082
1083   --status->in_dead_node_elim;
1084 }
1085
1086 /* Finish the statistics */
1087 void stat_finish(void)
1088 {
1089   if (! status->enable)
1090     return;
1091
1092   STAT_ENTER;
1093   {
1094     graph_entry_t *entry;
1095     graph_entry_t *global = graph_get_entry(NULL, status->irg_hash);
1096
1097     /* dump per graph */
1098     for (entry = pset_first(status->irg_hash); entry; entry = pset_next(status->irg_hash)) {
1099
1100       if (entry->irg == NULL) {
1101         /* special entry for the global count */
1102         continue;
1103       }
1104
1105       if (! entry->deleted) {
1106         /* the graph is still alive, count the nodes on it */
1107         count_nodes_in_graph(global, entry);
1108
1109         /* calculate the pattern */
1110         stat_calc_pattern_history(entry->irg);
1111       }
1112
1113       dump_graph(entry);
1114     }
1115
1116     /* dump global */
1117     dump_graph(global);
1118     dump_finish();
1119
1120     stat_finish_pattern_history();
1121
1122     /* finished */
1123     status->enable = 0;
1124   }
1125   STAT_LEAVE;
1126 }
1127
1128 #else
1129
1130 /* need this for prototypes */
1131 #define FIRM_STATISTICS
1132 #include "firmstat.h"
1133
1134 void init_stat(void) {}
1135
1136 void stat_finish(void) {}
1137
1138 void stat_new_ir_op(const ir_op *op) {}
1139
1140 void stat_free_ir_op(const ir_op *op) {}
1141
1142 void stat_new_node(const ir_node *node) {}
1143
1144 void stat_turn_into_id(const ir_node *node) {}
1145
1146 void stat_new_graph(ir_graph *irg, entity *ent) {}
1147
1148 void stat_free_graph(ir_graph *irg) {}
1149
1150 void stat_irg_walk(ir_graph *irg, void *pre, void *post) {}
1151
1152 void stat_irg_block_walk(ir_graph *irg, const ir_node *node, void *pre, void *post) {}
1153
1154 void stat_merge_nodes(
1155     ir_node **new_node_array, int new_num_entries,
1156     ir_node **old_node_array, int old_num_entries,
1157     stat_opt_kind opt) {}
1158
1159 void stat_lower(ir_node *node) {}
1160
1161 void stat_inline(ir_node *call, ir_graph *irg) {}
1162
1163 void stat_dead_node_elim_start(ir_graph *irg) {}
1164
1165 void stat_dead_node_elim_stop(ir_graph *irg) {}
1166
1167 #endif