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