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