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